I have been trying to add some events to the fullCalendar using a call to a ASHX page using the following code.
Page script:
<script type="text/javascript">
$(document).ready(function() {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today', center: 'title', right: 'month, agendaWeek,agendaDay'
},
events: 'FullCalendarEvents.ashx'
})
});
</script>
c# code:
public class EventsData
{
public int id { get; set; }
public string title { get; set; }
public string start { get; set; }
public string end { get; set; }
public string url { get; set; }
public int accountId { get; set; }
}
public class FullCalendarEvents : IHttpHandler
{
private static List<EventsData> testEventsData = new List<EventsData>
{
new EventsData {accountId = 0, title = "test 1", start = DateTime.Now.ToString("yyyy-MM-dd"), id=0},
new EventsData{ accountId = 1, title="test 2", start = DateTime.Now.AddHours(2).ToString("yyyy-MM-dd"), id=2}
};
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json.";
context.Response.Write(GetEventData());
}
private string GetEventData()
{
List<EventsData> ed = testEventsData;
StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (var data in ed)
{
sb.Append("{");
sb.Append(string.Format("id: {0},", data.id));
sb.Append(string.Format("title:'{0}',", data.title));
sb.Append(string.Format("start: '{0}',", data.start));
sb.Append("allDay: false");
sb.Append("},");
}
sb.Remove(sb.Length - 1, 1);
sb.Append("]");
return sb.ToString();
}
}
The ASHX page gets called and returnd the following data:
[{id: 0,title:'test 1',start: '2010-06-07',allDay: false},{id: 2,title:'test 2',start: '2010-06-07',allDay: false}]
The call to the ASHX page does not display any results, but if I paste the values returned directly into the events it displays correctly. I am I have been trying to get this code to work for a day now and I can't see why the events are not getting set.
Any help or advise on how I can get this to work would be appreciated.
Steve
In case anyone stumbles across this problem. I tried all of the above solutions, but none of them worked.
For me, the problem was solved by using an older version of jquery. I switched from version 1.5.2 which was included in the fullcalendar package to version 1.3.2
Steve,
I ran into something similar -- it would render the events if the JSON was directly in the fullCalendar call, but it would not render the identicla JSON coming from an outside URL. I finally got it to work by modifying the JSON so that "id", "title", "start", "end", and "allDay" had the quotes around them.
So instead of this (to use your sample JSON):
[{id: 0,title:'test 1',start: '2010-06-07',allDay: false},{id: 2,title:'test 2',start: '2010-06-07',allDay: false}]
...I had this:
[{"id": 0,"title":"test 1","start": "2010-06-07","allDay": false},{"id": 2,"title":"test 2","start": "2010-06-07","allDay": false}]
Now, why it worked locally but not remotely, I can't say.
Your JSON data lost the end item:
{id: 0,title:'test 1',start: '2010-06-07',end: '2010-06-07',allDay: false}
Let's look at what we know and eliminate possibilities:
The ASHX page gets called and returnd the ... data:
So the server-side portion is working just fine, and the code to call out to the server side is working.
I paste the values returned directly into the events it displays correctly.
So the code that handles the response works correctly.
Logically we see here that code that connects your server response to your calendar's input is not working. Unfortunately, I'm not up on the jQuery fullCalendar method, but perhaps you're missing a callback declaration?
I think it might have something to do with your date values.
FullCalendar events from asp.net ASHX page not displaying is a correct solution to the issue.
And I used long format for dates.
And #Steve instead of StringAppending we can use :-
System.Web.Script.Serialization.JavaScriptSerializer oSerializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
String sJSON = oSerializer.Serialize(evList);
evList being your list containing all events which has the essential properties like id,start,end,description,allDay etc..
I know this thread is an old thread,but this will be helpful to other users.
Just collating all the answers.
I struggled with this issue and resolved it using an .ashx handler as follows
My return class looks like…
public class Event
{
public Guid id { get; set; }
public string title { get; set; }
public string description { get; set; }
public long start { get; set; }
public long end { get; set; }
public bool allDay { get; set; }
}
Where DateTime values are converted to long values using…
private long ConvertToTimestamp(DateTime value)
{
long epoch = (value.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
return epoch;
}
And the ProcessRequest looks like…
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/html";
DateTime start = new DateTime(1970, 1, 1);
DateTime end = new DateTime(1970, 1, 1);
try
{
start = start.AddSeconds(double.Parse(context.Request.QueryString["start"]));
end = end.AddSeconds(double.Parse(context.Request.QueryString["end"]));
}
catch
{
start = DateTime.Today;
end = DateTime.Today.AddDays(1);
}
List<Event> evList = new List<Event>();
using (CondoManagerLib.Linq.CondoDataContext Dc = new CondoManagerLib.Linq.CondoDataContext(AppCode.Common.CGlobals.DsnDB))
{
evList = (from P in Dc.DataDailySchedules
where P.DateBeg>=start && P.DateEnd<=end
select new Event
{ description = P.Description,
id = P.RecordGuid,
title = P.Reason,
start = ConvertToTimestamp(P.DateBeg),
end = ConvertToTimestamp(P.DateEnd),
allDay = IsAllDay(P.DateBeg, P.DateEnd)
}).ToList();
}
System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
String sJSON = oSerializer.Serialize(evList);
context.Response.Write(sJSON);
}
And my Document Ready…
> $(document).ready(function () {
> $('#calendar').fullCalendar({
> header: { left: 'title', center: 'prev,today,next', right: 'month,agendaWeek,agendaDay' },
> editable: false,
> aspectRatio: 2.1,
> events: "CalendarEvents.ashx",
> eventRender: function (event, element) {
> element.qtip({
> content: event.description,
> position: { corner: { tooltip: 'topLeft', target: 'centerLeft'} },
> style: { border: { width: 1, radius: 3, color: '#000'},
> padding: 5,
> textAlign: 'center',
> tip: true,
> name: 'cream'
> }
> });
> }
> })
> });
The qTip pluging can be found at http://craigsworks.com/projects/qtip/
Hope this helps.
Related
I have ASP.NET application with the form handled by AngularJS. There are 2 date-time pickers (fromDate and toDate variables) and check box that indicate if open dates are included (isOpenDatesIncluded).
The code looks as following:
1) In first AngularJS module:
SearchActionsApp.controller("ListController", function($scope, $location,$q, Actions, ActionDates, CounterAgents,Counteragents, AsyncTask) {
...
$scope.actionsQuery = {
searchText: "",
fromDate: new Date(moment().format("MM/DD/YYYY")),
toDate: new Date(moment().add('d', 3).format("MM/DD/YYYY")),
isOpenDatesIncluded: false,
...
ActionDates.query({ actionId: action.Id }, task.success, task.error);
...
}
2) In service.js:
.factory('ActionDates', function ($resource) {
return $resource('/RestApi/api/ActionDates', {});
})
3) On server side:
public IEnumerable<ActionDateItem> QueryActionDates(ActionDateQuery query)
{
var sql = query.BuildSql();
using (var cn = CreateSqlConnection())
{
return cn.Query<ActionDateItem>(sql, new
{
query.ActionId,
query.IsOpenDatesIncluded,
BeginDate = query.FromDate,
EndDate = query.ToDate
});
}
}
public class ActionDateQuery
{
public ActionDateQuery()
{
ActionId = -1;
IsOpenDatesIncluded = false;
FromDate = new DateTime(1980, 1, 1);
ToDate = DateTime.Now.AddYears(1);
}
public long ActionId { get; set; }
public bool IsOpenDatesIncluded { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
}
The issue is the query returns the set of data from: 1980, 1, 1 to DateTime.Now.AddYears(1), but I need to return the data in range: fromDate - toDate, which I get from the client side. I need to pass 3 more parameters except from action.Id that is: fromDate, toDate and isOpenDatesIncluded.
How to do that?
Simply add them to the arguments of the query method as follows:
query({
actionId: action.Id,
fromDate: $scope.actionsQuery.fromDate,
...
}, task.success, task.error)
If they are not pre-bound in the resouce, they will be appended to the request URL as query parameters.
-- Edit --
Also might I suggest using promises rather than callbacks, as it'll help with readability and chaining:
query({
actionId: action.Id,
fromDate: $scope.actionsQuery.fromDate,
...
}).$promise.then(function(response) {
// success
}).catch(function(error) {
// error
});
ActionDates.query({ actionId: action.Id }, task.success, task.error);
Add the missing 3 parameters, like
{actionId: action.Id, fromDate: fromDate, etc}
and try to bind your parameters in the resource
$resource('/RestApi/api/ActionDates', { id: '#id', fromdate:'#fromdate', etc});
I know how to hook up ajax paging to a grid or a webgrid in asp.net mvc. But how can I accomplish ajax paging, using custom paging for large data sets for another format outside of a table grid.
Is that even possible using an mvc helper or mvc.pagedlist?
I used to be a webforms guys and it was so easy to hook up a listview where you could use divs to create whatever layout you want for individual items, you could then hook up a datapage and wrap it all in an update panel.
Basically I want a list of items that I can page through via ajax but with having large data sets I can just pull down all the items and page via jquery, I need to do custom paging on the server side and only return the items for a specific page.
By reusing a partial view and some ajax, this is very easily done in MVC.
Add this model as a property to your page's ViewModel to handle the pagination:
namespace Models.ViewModels
{
[Serializable()]
public class PagingInfoViewModel
{
public int TotalItems { get; set; }
public int ResultsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages {
get { return Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(this.TotalItems) / this.ResultsPerPage)); }
}
public string LinkTextShowMore { get; set; }
public string LinkTextShowingAll { get; set; }
/// <summary>
/// Paging url used by the jQuery Ajax function
/// </summary>
public string UrlGetMore { get; set; }
public PagingInfoViewModel(string linkTextShowMore, string linkTextShowingAll, int resultsPerPage)
{
this.LinkTextShowMore = linkTextShowMore;
this.LinkTextShowingAll = linkTextShowingAll;
this.ResultsPerPage = resultsPerPage;
}
}
}
Add the following code to your partial view to handle the pagination:
//Start Pagination
//determine the value for the X for "Showing X of Y"
{
int currentTotal = 0;
if ((Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage) < Model.PagingInfo.TotalItems) {
//the current max item we are displaying is less than the total number of policies
//display the current max item index\
currentTotal = Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage;
} else {
//the current is greater than the total number of policies
//display the total number of policies
currentTotal = Model.PagingInfo.TotalItems;
}
if (Model.PagingInfo.TotalPages == 0 || Model.PagingInfo.CurrentPage == Model.PagingInfo.TotalPages)
{
#<li>
<h3>#Model.PagingInfo.LinkTextShowingAll</h3>
<p><strong>Showing #currentTotal Of #Model.PagingInfo.TotalItems</strong></p>
</li>
} else {
#<li id="GetMore">
<a href="#" id="lnkGetMore">
<h3>#Model.PagingInfo.LinkTextShowMore</h3>
<p><strong>Showing #(currentTotal) Of #Model.PagingInfo.TotalItems</strong></p>
</a>
</li>
#<script type="text/javascript" lang="javascript">
$('#lnkGetMore').click(function () {
$.ajax({
url: "#Model.PagingInfo.UrlGetMore",
success: function (data) {
$('#ProducerList li:last').remove();
$('#ProducerList').append(data);
$('#ProducerList').listview('refresh');
}
});
return false;
});
</script>
}
}
Now, the javascript at the end is specifically for a UI that uses ul's and li's, but can easily be customized for your needs.
The UrlGetMore property is set on the back end when the model is passed to the view. I am sure there is a more elegant way of doing this. Here is the code I used:
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore == Url.RouteUrl("RouteItemList", new { page = view.PagingInfo.CurrentPage + 1 })
And finally, here is the action that handles both the initial View and the subsequent Partial View (ajax call)
public ActionResult List(UserModel user, ViewModel view, int page = 1)
{
IQueryable<model> models = this.RetrieveModels(user, view);
if ((models != null) && models.Count > 0) {
view.PagingInfo.CurrentPage = page;
view.PagingInfo.ResultsPerPage = user.Preferences.ResultsPerPage;
view.PagingInfo.TotalItems = models.Count;
view.items = models.Skip((page - 1) * user.Preferences.ResultsPerPage).Take(user.Preferences.ResultsPerPage).ToList();
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore = Url.RouteUrl("RouteList", new { page = view.PagingInfo.CurrentPage + 1 });
}
if (page == 1) {
return View(view);
} else {
return PartialView("ListPartial", view);
}
}
HTH.
You could create simple HtmlHelper simillar to this:
public static class HtmlPaginHelper
{
public static MvcHtmlString PagerNoLastPage(this AjaxHelper ajaxHelper,
int page,
int pageSize,
bool isLastPage,
Func<int, string> pageUrl,
Func<int, AjaxOptions> pageAjaxOptions)
{
var result = new StringBuilder();
var firstPageAnchor = new TagBuilder("a");
firstPageAnchor.SetInnerText("<<");
var prevPageAnchor = new TagBuilder("a");
prevPageAnchor.SetInnerText("<");
var nextPageAnchor = new TagBuilder("a");
nextPageAnchor.SetInnerText(">");
var currentPageText = new TagBuilder("span");
currentPageText.SetInnerText(string.Format("Page: {0}", page));
if (page > 1)
{
firstPageAnchor.MergeAttribute("href", pageUrl(1));
firstPageAnchor.MergeAttributes(pageAjaxOptions(1).ToUnobtrusiveHtmlAttributes());
prevPageAnchor.MergeAttribute("href", pageUrl(page - 1));
prevPageAnchor.MergeAttributes(pageAjaxOptions(page - 1).ToUnobtrusiveHtmlAttributes());
}
if (!isLastPage)
{
nextPageAnchor.MergeAttribute("href", pageUrl(page + 1));
nextPageAnchor.MergeAttributes(pageAjaxOptions(page + 1).ToUnobtrusiveHtmlAttributes());
}
result.Append(firstPageAnchor);
result.Append(prevPageAnchor);
result.Append(currentPageText);
result.Append(nextPageAnchor);
return MvcHtmlString.Create(result.ToString());
}
}
... and then use it in your Razor view:
grid results go here...
#Ajax.PagerNoLastPage(Model.Query.Page,
Model.Query.PageSize,
Model.Data.IsLastPage,
i => Url.Action("Index", RouteValues(i)),
i => new AjaxOptions
{
UpdateTargetId = "content",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
Url = Url.Action("Grid", RouteValues(i))
})
where RouteValues(i) is defined for example like this:
#functions {
private object PageRouteValues(int i)
{
return new
{
payId = Model.Query.PayId,
clientCode = Model.Query.ClientCode,
fromDate = Model.Query.FromDate,
tillDate = Model.Query.TillDate,
payNum = Model.Query.PayId,
checkNum = Model.Query.CheckNum,
payType = Model.Query.PayType,
payStatus = Model.Query.PayStatus,
page = i,
pageSize = Model.Query.PageSize
};
}
}
Is that even possible using an mvc helper or mvc.pagedlist?
Yes, but of course you have to coordinate the client-side requests with server-side actions to handle the actual data paging. In that sense, it's not as simple as as WebForms, but it's still possible.
Here's an example of using PagedList to render each returned item in its own table, separated by horizontal rules. You should easily be able to modify the HTML in the example to produce any rendering you want.
"How can i use engine in my console application"
I shouldn't use the ITemplate-interface and Transform-Method.
I am using Tridion 2011
Could anyone please suggest me.
You can't. The Engine class is part of the TOM.NET and that API is explicitly reserved for use in:
Template Building Blocks
Event Handlers
For all other cases (such as console applications) you should use the Core Service.
There are many good questions (and articles on other web sites) already:
https://stackoverflow.com/search?q=%5Btridion%5D+core+service
http://www.google.com/#q=tridion+core+service
If you get stuck along the way, show us the relevant code+configuration you have and what error message your get (or at what step you are stuck) and we'll try to help from there.
From a console application you should use the Core Service. I wrote a small example using the Core Service to search for items in the content manager.
Console.WriteLine("FullTextQuery:");
var fullTextQuery = Console.ReadLine();
if (String.IsNullOrWhiteSpace(fullTextQuery) || fullTextQuery.Equals(":q", StringComparison.OrdinalIgnoreCase))
{
break;
}
Console.WriteLine("SearchIn IdRef:");
var searchInIdRef = Console.ReadLine();
var queryData = new SearchQueryData
{
FullTextQuery = fullTextQuery,
SearchIn = new LinkToIdentifiableObjectData
{
IdRef = searchInIdRef
}
};
var results = coreServiceClient.GetSearchResults(queryData);
results.ToList().ForEach(result => Console.WriteLine("{0} ({1})", result.Title, result.Id));
Add a reference to Tridion.ContentManager.CoreService.Client to your Visual Studio Project.
Code of the Core Service Client Provider:
public interface ICoreServiceProvider
{
CoreServiceClient GetCoreServiceClient();
}
public class CoreServiceDefaultProvider : ICoreServiceProvider
{
private CoreServiceClient _client;
public CoreServiceClient GetCoreServiceClient()
{
return _client ?? (_client = new CoreServiceClient());
}
}
And the client itself:
public class CoreServiceClient : IDisposable
{
public SessionAwareCoreServiceClient ProxyClient;
private const string DefaultEndpointName = "netTcp_2011";
public CoreServiceClient(string endPointName)
{
if(string.IsNullOrWhiteSpace(endPointName))
{
throw new ArgumentNullException("endPointName", "EndPointName is not specified.");
}
ProxyClient = new SessionAwareCoreServiceClient(endPointName);
}
public CoreServiceClient() : this(DefaultEndpointName) { }
public string GetApiVersionNumber()
{
return ProxyClient.GetApiVersion();
}
public IdentifiableObjectData[] GetSearchResults(SearchQueryData filter)
{
return ProxyClient.GetSearchResults(filter);
}
public IdentifiableObjectData Read(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
public ApplicationData ReadApplicationData(string subjectId, string applicationId)
{
return ProxyClient.ReadApplicationData(subjectId, applicationId);
}
public void Dispose()
{
if (ProxyClient.State == CommunicationState.Faulted)
{
ProxyClient.Abort();
}
else
{
ProxyClient.Close();
}
}
}
When you want to perform CRUD actions through the core service you can implement the following methods in the client:
public IdentifiableObjectData CreateItem(IdentifiableObjectData data)
{
data = ProxyClient.Create(data, new ReadOptions());
return data;
}
public IdentifiableObjectData UpdateItem(IdentifiableObjectData data)
{
data = ProxyClient.Update(data, new ReadOptions());
return data;
}
public IdentifiableObjectData ReadItem(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
To construct a data object of e.g. a Component you can implement a Component Builder class that implements a create method that does this for you:
public ComponentData Create(string folderUri, string title, string content)
{
var data = new ComponentData()
{
Id = "tcm:0-0-0",
Title = title,
Content = content,
LocationInfo = new LocationInfo()
};
data.LocationInfo.OrganizationalItem = new LinkToOrganizationalItemData
{
IdRef = folderUri
};
using (CoreServiceClient client = provider.GetCoreServiceClient())
{
data = (ComponentData)client.CreateItem(data);
}
return data;
}
Hope this gets you started.
Is it possible to bind properties on the client and server side in Scriptcontrol, so when I set property in javascript, change will be visible also in code behind and when I set property in code behind, change will be visible in javascript?
I can't get it work like above - it is set initially, when I set property where scriptcontrol is declared, but when I change it later it is still the same as before...
EDIT: I try to do a ProgressBar for long postbacks in our ASP.NET application. I have tried many options but none works for me... I want to set progress value in code behind and has it updated in view during long task postback.
Code for ScriptControl:
C#:
public class ProgressBar : ScriptControl
{
private const string ProgressBarType = "ProgressBarNamespace.ProgressBar";
public int Value { get; set; }
public int Maximum { get; set; }
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
this.Value = 100;
this.Maximum = 90;
var descriptor = new ScriptControlDescriptor(ProgressBarType, this.ClientID);
descriptor.AddProperty("value", this.Value);
descriptor.AddProperty("maximum", this.Maximum);
yield return descriptor;
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
yield return new ScriptReference("ProgressBar.cs.js");
}
}
Javascript:
Type.registerNamespace("ProgressBarNamespace");
ProgressBarNamespace.ProgressBar = function(element) {
ProgressBarNamespace.ProgressBar.initializeBase(this, [element]);
this._value = 0;
this._maximum = 100;
};
ProgressBarNamespace.ProgressBar.prototype = {
initialize: function () {
ProgressBarNamespace.ProgressBar.callBaseMethod(this, "initialize");
this._element.Value = this._value;
this._element.Maximum = this._maximum;
this._element.show = function () {
alert(this.Value);
};
},
dispose: function () {
ProgressBarNamespace.ProgressBar.callBaseMethod(this, "dispose");
},
get_value: function () {
return this._value;
},
set_value: function (value) {
if (this._value !== value) {
this._value = value;
this.raisePropertyChanged("value");
}
},
get_maximum: function () {
return this._maximum;
},
set_maximum: function (value) {
if (this._maximum !== value) {
this._maximum = value;
this.raisePropertyChanged("maximum");
}
}
};
ProgressBarNamespace.ProgressBar.registerClass("ProgressBarNamespace.ProgressBar", Sys.UI.Control);
if (typeof (Sys) !== "undefined") Sys.Application.notifyScriptLoaded();
I'll appreciate any way to implement this progress bar...
Personally, I do this often using hidden fields.
Bear in mind that hidden fields are not secure and may have other downfalls, since they don't actually hide their value, just simply do not display it.
ASPX Markup
<asp:HiddenField ID="hiddenRequest" runat="server" ClientIDMode="Static" />
ASPX.CS Code behind
public string HiddenRequest
{
set
{
hiddenRequest.Value = value;
}
get
{
return hiddenRequest.Value;
}
}
Page JAVASCRIPT (with jQuery)
$('#hiddenRequest').val('MyResult');
This way, I can access the same field using one variable as such, accessed both from client side and server side.
I want to upload a file from the client to the server. Is there a way to upload a file with SignalR or must i need a Controller for this?
SignalR is for real time messaging not uploading files.
While SignalR cannot help with the actual upload, it can be used for updating the client with progress while a file is uploaded.
This file uploading using file input bootstrap plugin (krajee)
You can also upload file without using this plugin.
#section Page{
<script src="~/Scripts/bootstrap-switch.min.js"></script>
<script src="~/Scripts/Uploader/fileinput.js"></script>
<link href="~/Scripts/Uploader/fileinput.css" rel="stylesheet" />
<script>
var itemHub = $.connection.ItemHub;
$(document).ready(function() {
$.connection.hub.start().done(function() {
//do any thing
});
$("#fileinput").fileinput({
allowedFileExtensions: ["jpg", "png", "gif", "jpeg"],
maxImageWidth: 700,
maxImageHeight: 700,
resizePreference: 'height',
maxFileCount: 1,
resizeImage: true
});
$("#fileinput").on('fileloaded', function (event, file, previewId, index, reader) {
var readers = new FileReader();
readers.onloadend = function () {
$(".file-preview-image").attr('src', readers.result);
}
readers.readAsDataURL(file);
});
$('#btnSave').click(function() {
var imagesJson = $('.file-preview-image').map(function () {
var $this = $(this);
return {
image: $this.attr('src'),
filename: $this.attr('data-filename')
};
}).toArray();
itemHub.server.getByteArray(imagesJson);
});
});
</script>
}
Hub class code
[HubName("ItemHub")]
public class ItemHub : Hub
{
public void GetByteArray(IEnumerable<ImageData> images)
{
foreach (var item in images ?? Enumerable.Empty<ImageData>())
{
var tokens = item.Image.Split(',');
if (tokens.Length > 1)
{
byte[] buffer = Convert.FromBase64String(tokens[1]);
}
}
}
}
public class ImageData
{
public string Description { get; set; }
public string Filename { get; set; }
public string Image { get; set; }
}