Custom Ajax Binding does not work properly - asp.net-mvc

I have following code for Custom Ajax Binding. This has following problems even though it is displaying data for the first page.
• The request.Sorts is coming as NULL in to the Orders_Read method
• The request.PageSize is coming as 0 to the Orders_Read method
• The request.Page is coming as 1 to the Orders_Read method (even if I click on the page 2)
What changes need to be done here to get proper sort and pagesize values?
Note: I am using MVC Wrapper for Kendo Grid.
VIEW
#Scripts.Render("~/bundles/jquery")
<script type ="text/javascript">
$(document).ready(function (){
$('#Submit1').click(function () {
alert('1');
$('#grid12').data('kendoGrid').dataSource.read();
});
});
</script>
#model KendoPratapSampleMVCApp.Models.Sample
#{
ViewBag.Title = "CustomAjaxbind";
}
<h2>CustomAjaxbind</h2>
#using (Html.BeginForm("PostValues", "CustomAjaxBinding", FormMethod.Post))
{
<input id="Submit1" type="button" value="SubmitValue" />
#(Html.Kendo().Grid<KendoPratapSampleMVCApp.Models.Sample>()
.Name("grid12")
.EnableCustomBinding(true)
.Columns(columns => {
columns.Bound(p => p.SampleDescription).Filterable(false).Width(100);
columns.Bound(p => p.SampleCode).Filterable(false).Width(100);
columns.Bound(p => p.SampleItems).Filterable(false).Width(100);
})
.Pageable()
.Sortable()
.Scrollable()
.AutoBind(false)
.Filterable()
.HtmlAttributes(new { style = "height:430px;" })
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(2)
.Read(read => read.Action("Orders_Read", "CustomAjaxBinding"))
)
)
}
Controller
public class CustomAjaxBindingController : Controller
{
//
// GET: /CustomAjaxBinding/
public ActionResult Index()
{
return View("CustomAjaxbind");
}
public ActionResult Orders_Read([DataSourceRequest(Prefix = "grid12")]DataSourceRequest request)
{
string sortField = "SampleDescription";
string sortDirection = string.Empty;
if (request.Sorts != null)
{
foreach (SortDescriptor sortDescriptor in request.Sorts)
{
sortField = sortDescriptor.Member;
if (sortDescriptor.SortDirection == ListSortDirection.Ascending)
{
sortDirection = "Ascending";
}
else
{
sortDirection = "Descending";
}
}
}
int total = 1;
int myPageSize = 2;
if(request.PageSize !=0)
{
myPageSize = request.PageSize;
}
IEnumerable<Sample> currentSamples = GetSubsetEmployees(request.Page - 1, myPageSize, out total, sortField, sortDirection);
var result = new DataSourceResult()
{
Data = currentSamples,
Total = total // Total number of records
};
return Json(result);
}
public IEnumerable<Sample> GetSubsetEmployees(int pageIndex, int pageSize, out int itemCount, string sortField, string sortDirection)
{
IEnumerable<Sample> samples = GetSamples();
itemCount = samples.ToList().Count;
var selector = new Func<Sample, object>(e => e.GetType().GetProperty(sortField).GetValue(e, null));
var query = sortDirection.Equals("descending", StringComparison.OrdinalIgnoreCase)
? samples.OrderByDescending(selector)
: samples.OrderBy(selector);
List<Sample> currentPageEmployees = query
.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToList();
return currentPageEmployees;
}
public static IEnumerable<Sample> GetSamples()
{
List<Sample> sampleAdd = new List<Sample>();
Sample s12 = new Sample();
s12.SampleCode = "1";
s12.SampleDescription = "A";
s12.SampleItems = "newone";
Sample s2 = new Sample();
s2.SampleCode = "2";
s2.SampleDescription = "B";
s2.SampleItems = "oldone";
Sample s3 = new Sample();
s3.SampleCode = "3";
s3.SampleDescription = "C";
s3.SampleItems = "latestone";
Sample s4 = new Sample();
s4.SampleCode = "4";
s4.SampleDescription = "D";
s4.SampleItems = "latestoneitem";
sampleAdd.Add(s12);
sampleAdd.Add(s2);
sampleAdd.Add(s3);
sampleAdd.Add(s4);
return sampleAdd;
}
}
Model
namespace KendoUIMvcSample.Models
{
public class SampleModel
{
public List<Sample> samples;
}
public class Sample
{
public string SampleDescription { get; set; }
public string SampleCode { get; set; }
public string SampleItems { get; set; }
}
}

I had the same problem as yours and finally found the solution recently after consuming days on investigating the problem. I post it here if someone else faced the same issue.
You need to remove Prefix from your parameter:
public ActionResult Orders_Read([DataSourceRequest(Prefix = "grid12")]DataSourceRequest request)
Converted to:
public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
I don't know if this is a bug from Kendo or not! But in this scenario grid can't be found by Prefix defined.
You can find an example here

Related

How to get all action , controller and area names while running asp core 3.1

i have an asp.net core 3.1 application and i want to get all controller , action and area names when my application is running like get action names with reflection in mvc.
Is there any way ?
Try this:
1.Model:
public class ControllerActions
{
public string Controller { get; set; }
public string Action { get; set; }
public string Area { get; set; }
}
2.Display the Controller,Action and Area Name:
[HttpGet]
public List<ControllerActions> Index()
{
Assembly asm = Assembly.GetExecutingAssembly();
var controlleractionlist = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Select(x => new
{
Controller = x.DeclaringType.Name,
Action = x.Name,
Area = x.DeclaringType.CustomAttributes.Where(c => c.AttributeType == typeof(AreaAttribute))
}).ToList();
var list = new List<ControllerActions>();
foreach (var item in controlleractionlist)
{
if (item.Area.Count() != 0)
{
list.Add(new ControllerActions()
{
Controller = item.Controller,
Action = item.Action,
Area = item.Area.Select(v => v.ConstructorArguments[0].Value.ToString()).FirstOrDefault()
});
}
else
{
list.Add(new ControllerActions()
{
Controller = item.Controller,
Action = item.Action,
Area = null,
});
}
}
return list;
}
In my application I didn't require finding areas, so I ended up creating a simplified version based on the accepted answer. If it's useful to anyone else, here it is:
public static class ControllerActionEnumerator
{
public static List<ControllerAndItsActions> GetAllControllersAndTheirActions()
{
Assembly asm = Assembly.GetExecutingAssembly();
IEnumerable<Type> controllers = asm.GetTypes().Where(type => type.Name.EndsWith("Controller"));
var theList = new List<ControllerAndItsActions>();
foreach (Type curController in controllers)
{
List<string> actions = curController.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)
.Where(m => m.CustomAttributes.Any(a => typeof(HttpMethodAttribute).IsAssignableFrom(a.AttributeType)))
.Select(x => x.Name)
.ToList();
theList.Add(new ControllerAndItsActions(curController.Name, actions));
}
return theList;
}
}
public class ControllerAndItsActions
{
public string Controller { get; }
public List<string> Actions { get; }
public ControllerAndItsActions(string controller, List<string> actions) => (Controller, Actions) = (controller, actions);
}
Try this:
ControllerFeature controllerFeature = new ControllerFeature();
this.ApplicationPartManager.PopulateFeature(controllerFeature);
IEnumerable<TypeInfo> typeInfos = controllerFeature.Controllers;
ApplicationPartManager must use DI to your class.
I wrote new code with a Controller and Action DisplayNames.
Assembly assembly = Assembly.GetExecutingAssembly();
var controllersList = assembly.GetTypes().Where(ctype => typeof(Controller).IsAssignableFrom(ctype)).Select(type => new { ty = type, methods = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public).Where(y => Attribute.IsDefined(y, typeof(LogRequestAttribute))) }).Where(type => type.methods.Any(x => Attribute.IsDefined(x, typeof(LogRequestAttribute)))).Select(controller => new
{
AreaName = (controller.ty.GetCustomAttribute(typeof(AreaAttribute)) as AreaAttribute)?.RouteValue,
ControllerTitle = (controller.ty.GetCustomAttribute(typeof(DisplayNameAttribute)) as DisplayNameAttribute)?.DisplayName,
ControllerName = controller.ty.Name,
Actions = controller.methods.Select(action => new
{
ActionTitle = (action.GetCustomAttribute(typeof(DisplayNameAttribute)) as DisplayNameAttribute)?.DisplayName,
ActionName = action.Name,
}).ToList(),
}).ToList();
// group by on area
var areaList = controllersList.GroupBy(group => group.AreaName).Select(ar => new
{
AreaName = ar.Key,
Controllers = ar.Select(co => new
{
ControllerTitle = co.ControllerTitle,
ControllerName = co.ControllerName,
Actions = co.Actions,
}).ToList(),
}).ToList();

how to return back list of data from controller to knockout function to view page

This is my controller where i am returning back list of tags with the post:
public JsonResult GetPosts(int? id)
{
var varid = id;
var ret = (from post in db.Posts.ToList()
orderby post.PostedDate descending
select new
{
CityName = post.City.CityName,
TagName = post.Tags.ToList()
// TagName = post.Tags
}
}
I dont know, here, is this the way to return back all the tags selected.
Posts and Tags table are interconnected by many to many relation with a join table TagPost in database which contains TagId and PostId.
this is the knockout code:
function Post(data) {
var self = this;
data = data || {};
self.CityName = data.CityName || "";
self.TagName = data.TagName || "";
}
function viewModel() {
var self = this;
self.posts = ko.observableArray();
self.newMessage = ko.observable();
self.error = ko.observable();
self.loadPosts = function () {
// to load existing posts
$.ajax({
url: postApiUrl1,
data: { id: $("#Locations").val() },
datatype: "json",
contentType: "application/json",
cache: false,
type: 'Get'
})
.done(function (data) {
var mappedPosts = $.map(data, function (item)
{ return new Post(item); });
self.posts(mappedPosts);
})
.fail(function () {
error('unable to load posts');
});
}
This is the view page where i want to data-bind the cityName along with the tags:
<div>
<img src="~/assests/images/icon.png" alt=""><span><a data-bind="text: CityName"></a></span>
</div>
<div>
<img src="~/assests/images/tag.png" alt=""><span><a data-bind="text: TagName"></a></span>
</div>
Here, i want to return back all the tag name with comma seperated.Please someone suggest me what to do from here.
This is my Post class:
public class Post
{
[Key]
public int PostId { get; set; }
public string Message { get; set; }
public int? cityId { get; set; }
public IList<Tag> Tags { get; set; }
}
and this is my tag class:
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
public IList<Post> Posts { get; set; }
}
There is a many to many relationship between tag and post class so its creating a new join Table TagPost with column(TagId, PostId).
This is how i am inserting data to this table with on model creating:
modelBuilder.Entity<Tag>()
.HasMany(p => p.Posts)
.WithMany(t => t.Tags)
.Map(m =>
{
m.ToTable("TagPost");
m.MapLeftKey("TagId");
m.MapRightKey("PostId");
});
This should bring the data in the format you want:
var data = db.Posts.Include(x => x.Tags)
.Include(x => x.City)
.Where(x => x.PostId == id)
.SingleOrDefault();
var json = new {
PostId = data.PostId,
PostMessage = data.Message,
CityName = data.City.CityName,
Tags = string.Join(",", data.Tags.Select(t => t.TagName))
};
return Json(json, JsonRequestBehavior.AllowGet);
This will return the following Json:
{
"PostId": 1,
"PostMessage": "ABC",
"CityName": "Chicago",
"Tags": "C#,.NET,StackOverflow"
}
Just note that I've included the City using Include in the Post but in the model you posted, there's only the cityId. Perhaps you'll need to change that too.
EDIT
As per request, to return all posts and related tags change the code to this:
var data = db.Posts.Include(x => x.Tags)
.Include(x => x.City)
.ToList();
if (data.Count == 0)
return null; //Just return something if no post is found
var json = data.Select(x => new
{
PostId = x.PostId,
PostMessage = x.Message,
CityName = x.City.CityName,
Tags = string.Join(",", x.Tags.Select(t => t.TagName))
}).ToList();

DevExpress GridView ExportToPdf doesn't export filtered data MVC

I have a webpage with gridview from Devexpress and i've implemented export to pdf. But somehow I don't get the current filter, order or group setting. I'm wondering if there is something wrong with how i set my setting in code or something since I've googled a lot at seems like those options should be handled automatically in ExportToPdf as long as you have rigth setting. My _GripPartial.cshtml:
#Html.DevExpress().GridView(TreeMenuTest.Controllers.LogViewController.GridViewSettings).Bind(TreeMenuTest.Controllers.LogViewController.DataSource).GetHtml()
_LogView.cshtml:
#using (Html.BeginForm("ExportToPDF", "LogView", FormMethod.Post))
{
<div id="buttonExport" class ="gridBtn">
<input type="submit" id ="ExportBtn" value="Export to pdf" />
</div>}
<div id="buttonReset" class ="gridBtn">
<input type="button" id ="ResetBtn" value="Reset Grid" onclick="javascript: ResetGrid()"/>
</div>
#Html.Action("Grid","LogView")
And finally LogViewController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data.SqlClient;
using TreeMenuTest.Models;
using DevExpress.Web.Mvc;
using DevExpress.Web.ASPxGridView;
using System.Globalization;
using System.Web.UI.WebControls;
using DevExpress.Web.ASPxClasses;
using DevExpress.XtraPrinting;
using DevExpress.XtraPrintingLinks;
using System.IO;
using System.Drawing.Printing;
namespace TreeMenuTest.Controllers
{
public class LogViewController : Controller
{
//
// GET: /LogView/
public static List<LoggingEvent> DataSource;
static GridViewSettings exportGridViewSettings;
public static GridViewSettings GridViewSettings
{
get
{
if (exportGridViewSettings == null)
exportGridViewSettings = GetGridViewSettings();
return exportGridViewSettings;
}
}
static GridViewSettings GetGridViewSettings()
{
GridViewSettings settings = new GridViewSettings();
settings.Name = "GridView";
settings.CallbackRouteValues = new { Controller = "LogView", Action = "Grid" };
settings.Width = Unit.Percentage(100);
settings.Theme = "BlackGlass";
settings.KeyFieldName = "Id";
settings.SettingsPager.Visible = true;
settings.Settings.ShowGroupPanel = true;
settings.Settings.ShowFilterRow = true;
settings.SettingsBehavior.AllowSelectByRowClick = true;
settings.SettingsPager.PageSize = 25;
settings.SettingsBehavior.ColumnResizeMode = ColumnResizeMode.Control;
settings.Settings.ShowHeaderFilterButton = true;
settings.SettingsPopup.HeaderFilter.Height = 200;
settings.SettingsExport.Landscape = true;
settings.SettingsExport.TopMargin = 0;
settings.SettingsExport.LeftMargin = 0;
settings.SettingsExport.RightMargin = 0;
settings.SettingsExport.BottomMargin = 0;
settings.SettingsExport.PaperKind = PaperKind.A4;
settings.SettingsExport.RenderBrick = (sender, e) =>
{
if (e.RowType == GridViewRowType.Data && e.VisibleIndex % 2 == 0)
e.BrickStyle.BackColor = System.Drawing.Color.FromArgb(0xEE, 0xEE, 0xEE);
};
settings.Columns.Add("Id");
settings.Columns.Add(column =>
{
column.FieldName = "Ts";
column.Settings.AutoFilterCondition = AutoFilterCondition.Like;
});
settings.Columns.Add("LogLevelText").Caption = "Level";
settings.Columns.Add("LoggerName");
settings.Columns.Add("Message");
settings.Columns.Add("ThreadName").Caption = "Thread";
settings.Columns.Add("UserName");
settings.Columns.Add("Domain");
settings.Columns.Add("ClassName");
settings.Columns.Add("FileName");
settings.Columns.Add("MethodName").Caption = "Method";
settings.Columns.Add("LineNumber").Caption = "Line";
settings.Columns.Add("LocalIP");
settings.Columns.Add("MachineName").Caption = "Machine";
settings.Columns.Add("UnikeName");
settings.Settings.ShowPreview = true;
settings.PreviewFieldName = "ExceptionString";
return settings;
}
public ActionResult Index()
{
return PartialView("_LogView");
}
public ActionResult Grid()
{
if (DataSource == null)
{
DataSource = GetAllEventsLast24h();
}
return PartialView("_GridPartial");
}
public ActionResult EmptyGrid()
{
DataSource = null;
return PartialView("_GridPartial");
}
public List<LoggingEvent> GetAllEventsLast24h() {
return GetEventsWithCommand("where Ts > '" + DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd HH:mm:ss") + "';");
}
public List<LoggingEvent> GetEventsOnDateTime(DateTime fromDate, DateTime toDate)
{
return GetEventsWithCommand("where Ts > '" + fromDate.ToString("yyyy-MM-dd HH:mm:ss") + "' AND Ts < '" + toDate.ToString("yyyy-MM-dd HH:mm:ss") + "';");
}
public List<LoggingEvent> GetEventsWithCommand(string where)
{
List<LoggingEvent> events = new List<LoggingEvent>();
SqlConnection myConnection = new SqlConnection("Data Source=xxx;user id=xxx;password=xxx;connection timeout=30");
myConnection.Open();
SqlDataReader myReader = null;
SqlCommand command = new SqlCommand("SELECT Id,Ts,LogLevel,LoggerName,Message,ThreadName,UserName,ExceptionString,Domain,ClassName,FileName,MethodName,LineNumber,LocalIP,MachinName,UnikeName from dbo.LoggingEvent " + where);
command.Connection = myConnection;
myReader = command.ExecuteReader();
while (myReader.Read())
{
events.Add(LoggingEvent.ReadEntityFromDbReader(myReader));
}
myConnection.Close();
return events;
}
[HttpPost]
public ActionResult ReloadGrid(string fromDate, string toDate)
{
DateTime fromDateTime;
DateTime toDateTime;
if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate)) // todo
{
fromDateTime = ParseStringToDate(fromDate);
toDateTime = ParseStringToDate(toDate);
}
else// No dates specified = get last 24 hours
{
toDateTime = DateTime.Now;
fromDateTime = toDateTime.AddHours(-24);
}
if (fromDateTime.CompareTo(toDateTime) > 0)
{
// from date grater then todate, change places
DateTime temp = toDateTime;
toDateTime = fromDateTime;
fromDateTime = temp;
}
DataSource = GetEventsOnDateTime(fromDateTime, toDateTime);
return PartialView("_LogView");
}
public DateTime ParseStringToDate(string datetxt)// todo this is copy froim treemenuviewcontroller, create utilsclass
{
const string dateformat = "dd.MM.yyyy HH:mm"; // jquery datepicker
const string dateformat2 = "yyyy-MM-dd HH:mm";// chrome TODO different formats with different location setting?
DateTime dateTime;
try //todo error handling!
{
dateTime = DateTime.ParseExact(datetxt, dateformat, CultureInfo.InvariantCulture);
}
catch
{
dateTime = DateTime.ParseExact(datetxt, dateformat2, CultureInfo.InvariantCulture);
}
return dateTime;
}
public ActionResult ExportToPDF()
{
var printable = GridViewExtension.CreatePrintableObject(GridViewSettings, DataSource);
PrintingSystem ps = new PrintingSystem();
PrintableComponentLink link1 = new PrintableComponentLink(ps);
link1.Component = printable;
link1.PrintingSystem.Document.AutoFitToPagesWidth = 1;
link1.Landscape = true;
CompositeLink compositeLink = new CompositeLink(ps);
compositeLink.Links.Add(link1);
compositeLink.CreateDocument();
using (MemoryStream stream = new MemoryStream())
{
compositeLink.PrintingSystem.ExportToPdf(stream);
WriteToResponse("filename", true, "pdf", stream);
}
ps.Dispose();
return Index();
}
void WriteToResponse(string fileName, bool saveAsFile, string fileFormat, MemoryStream stream)
{
string disposition = saveAsFile ? "attachment" : "inline";
Response.Clear();
Response.Buffer = false;
Response.AppendHeader("Content-Type", string.Format("application/{0}", fileFormat));
Response.AppendHeader("Content-Transfer-Encoding", "binary");
Response.AppendHeader("Content-Disposition",
string.Format("{0}; filename={1}.{2}", disposition, fileName, fileFormat));
Response.BinaryWrite(stream.GetBuffer());
Response.End();
}
}
}
Any clues?
Ps. Asking me why I don't write to DevExpress support is not helpful, so please just don't comment at all.
Check the How to export GridView rows and keep end-user modifications (such as sorting, grouping, filtering, selection) KB Article and make sure if all the steps are implemented.
After help for #Mikhail i fix the issue by putting #Html.Action("Grid","LogView") in the Html.BeginForm in _Log.View.cshtml like that:
<div id="buttonReset" class ="gridBtn">
<input type="button" id ="ResetBtn" value="Reset Grid" onclick="javascript: ResetGrid()"/>
</div>
#using (Html.BeginForm("ExportToPDF", "LogView", FormMethod.Post))
{
<div id="buttonExport" class ="gridBtn">
<input type="submit" id ="ExportBtn" value="Export to pdf" />
</div>
#Html.Action("Grid","LogView")}

Custom Paging for ASP.Net MVC Kendo Grid

I have a MVC Kendo Grid as follows. It is working fine with default paging.
Now, I want to do custom paging. In the controller action we need to know the current page index. Also it should set the “total” count for the grid. [The actual data source will have only 2 records at a time even if there are 100 records in the database. So the grid must know the total number of records in database using “total” attribute.]
The query should return only 2 records from the database at a time.
How can we do this custom server paging using the MVC wrapper for Kendo Grid?
#using (Html.BeginForm())
{
#(Html.Kendo().Grid<KendoUIMvcSample.Models.Sample>()
.Name("ssgrid222")
.Columns(columns => {
columns.Bound(p => p.SampleDescription).Filterable(false).Width(100);
columns.Bound(p => p.SampleCode).Filterable(false).Width(100);
columns.Bound(p => p.SampleItems).Filterable(false).Width(100);
})
.AutoBind(false)
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.HtmlAttributes(new { style = "height:430px;" })
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(2)
.Read(read => read.Action("Orders_Read", "Sample")
)
)
)
}
Controller
public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
{
int currentPageNumber = request.Page;
return Json(GetOrders().ToDataSourceResult(request));
}
It is defined in the kendo site
CONTROLLER CODE
//Paging and Sorting
int currentPage = request.Page;
int pageSize = request.PageSize;
string sortDirection = "ASC";
string sortField = "UpdatedDateTime";
if (request.Sorts != null && request.Sorts.Count > 0)
{
sortField = request.Sorts[0].Member;
sortDirection = request.Sorts[0].SortDirection.ToString();
}
//Setting the TOTAL
var result = new DataSourceResult()
{
Data = orders,
Total = total // Total number of records
};
return Json(result);
VIEW
<div class="GridSearch">
#(Html.Kendo().Grid<MVC.Models.TransactionHistoryModel>()
.Name("TransactionHistroyGrid")
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(25)
.ServerOperation(true)
.Read(read => read
.Action("TransactionHistorySearch_Read", "TransactionHistoryResults")
.Data("additionalData")
)
)
.Columns(columns =>
{
columns.Bound(p => p.UserId).Filterable(false).Width(40).Title("Userid");
columns.Bound(p => p.Reviewed).Template(#<text></text>).ClientTemplate("<input id='checkbox' class='chkbx' type='checkbox' />").Filterable(false).Width(30).Title("Reviewed");
columns.Bound(p => p.CreatedOnEnd).Format("{0:MM/dd/yyyy}").Filterable(false).Width(50).Title("Created On");
columns.Bound(p => p.UpdatedOnEnd).Format("{0:MM/dd/yyyy}").Filterable(false).Width(50).Title("Updated On");
columns.Bound(p => p.Comment).Filterable(false).Width(50).Title("Comment");
})
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.HtmlAttributes(new { style = "height:325px;" })
)
</div>
Here is a custom paging solution we have implemented for Kendo ListView. With minor modification it should work for the grid. The solution consists of a custom DataSoure Object and a custom JSonResult class.
The custom data source:
public class MyDataSource
{
public object AggregateResults { get; set; }
public object Data { get; set; }
public object Errors { get; set; }
public int Total { get; set; }
}
The custom ActionResult:
public class JsonNetResult : ActionResult
{
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings();
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
A Typical use in an action method would be:
public ActionResult Orders_Read([DataSourceRequest] Object request)
{
int count = 0;
var ds = (DataSourceRequest)request;
var ListOfItemsToDisplay = GetItemsWithPagingInfo
(
MySearchParameters,
ds.PageSize,
ds.Page,
out count
);
return new JsonNetResult
{
Formatting = Formatting.Indented,
Data = new MyDataSource
{
Data = ListOfItemsToDisplay
Total = count,
AggregateResults = null,
Errors = null
}
};
}

Building a Kendo UI grid dynamically

I am having challenge making my Kendo Grid Title from an array columnName as shown in the code below. columnName is an array that i want to use as my column/field name.
#(Html.Kendo()
.Grid<rtxVending.Web.Models.ProductDetails>()
.Name("ProductDetailGrid").ClientDetailTemplateId("")
.HtmlAttributes(new { #style = "align:center; font-size:9px;" })
.Columns(columns =>
{
var colums = rtxVending.Web.Repositories.SessionRepository.GetSessionObject<IList<rtxVending.Web.Models.
ProductCategoryTags>>(ApplicationConstants.sesProductsHeaderCategoryTags);
if (colums != null && colums.Count > 0)
{
**//columnName is an array that i want to use as my column/field name**
var columnName = colums.Select(a => a.ValueX).ToArray();
foreach (var column in columnName)
{
columns.Bound(column.ToString());
}
}
else
{
columns.Bound(o => o.Amount).Width(100);
columns.Bound(o => o.Value).Width(100);
columns.Bound(o => o.Value1).Width(100);
columns.Bound(o => o.Value2).Width(100);
columns.Bound(o => o.Value3).Width(100);
columns.Bound(o => o.Value4).Width(100);
columns.Bound(o => o.Value5).Width(100);
columns.Bound(o => o.Value6).Width(100);
columns.Bound(o => o.Value7).Width(100);
columns.Bound(o => o.Value8).Width(100);
columns.Bound(o => o.Value9).Width(100);
columns.Bound(o => o.Value10).Width(100);
}
})
.Pageable(pager => pager.Refresh(true))
.Sortable()
.Scrollable()
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("GetProductDetailsGrid", "Products"))
.Events(events => events.Error("error_handler"))
)
)
Thank you Prof. Pickle for your response.
This is what I have done. The method where the grid data is read from is as below.
public ActionResult GetProductDetailsGrid([DataSourceRequest] DataSourceRequest request)
{
try
{
List<ProductDetails> productDetails = null;
//IEnumerable<ProductDetails> productDetails = null;
if (SessionRepository.VerifySessionExists(ApplicationConstants.sesProductDetails))
{
productDetails = SessionRepository.GetSessionObject<List<ProductDetails>>(ApplicationConstants.sesProductDetails).ToList(); //.AsEnumerable();
//Return converted list<T> as DataTable
var dataTable = ToDataTable<ProductDetails>(productDetails);
if (dataTable != null)
{
var dataColumnCollection = dataTable.Columns;
SessionRepository.CacheSession<DataColumnCollection>(ApplicationConstants.sesAcquireStockDataColumnCollection, dataColumnCollection);
}
return Json(dataTable.ToDataSourceResult(request));
}
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
return Json(ModelState.ToDataSourceResult());
}
return Json(ModelState.ToDataSourceResult(request));
}
my model is
public class ProductDetails
{
[Key,ScaffoldColumn(false)]
public long ProductDetailId { get; set; }
public int ProductHeaderId { get; set; }
public double Amount { get; set; }
public string Value { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Value4 { get; set; }
public string Value5 { get; set; }
public string Value6 { get; set; }
public string Value7 { get; set; }
public string Value8 { get; set; }
public string Value9 { get; set; }
public string Value10 { get; set; }
public string Value1DisplayName {get; set;}
public bool Valid { get; set; }
}
Before the view comes up i pass in data to the grid if my session is not null else it passes nothing to the view as shown below:
public ActionResult AcquireStock()
{
//TODO uncomment the Session Below
//SessionRepository.RemoveSession(ApplicationConstants.sesProductDetails);
var productHeader = new ProductHeaders();
if (SessionRepository.VerifySessionExists(ApplicationConstants.sesProductDetails))
{
var productDetails = SessionRepository.GetSessionObject<List<ProductDetails>>(ApplicationConstants.sesProductDetails).ToList(); //.AsEnumerable();
//Return converted list<T> as DataTable
var dataTable = ToDataTable<ProductDetails>(productDetails);
if (dataTable != null)
{
productHeader.ProductDetailsDataTable = dataTable;
//SessionRepository.CacheSession<DataColumnCollection>(ApplicationConstants.sesAcquireStockDataColumnCollection, dataColumnCollection);
}
return View(productHeader);
}
productHeader.ProductDetailsDataTable = null;
return View(productHeader);
}
I created a generic method that takes care of transforming my data to datatable by passing in the columns names and rows respectively without having to display every value for model properties. I want it to passing the field with values that are not "" or null. so that the grid displays it. My generic method is as follows:
//Converts Generic List to DataTable
private DataTable ToDataTable<T>(List<T> data)// T is any generic type
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
int columnCount = 0;
DataTable table = new DataTable();
var colums = rtxVending.Web.Repositories.SessionRepository.GetSessionObject<IList<rtxVending.Web.Models.ProductCategoryTags>>(ApplicationConstants.sesProductsHeaderCategoryTags);
if (colums != null && colums.Count > 0)
{
var columnName = colums.Select(a => a.ValueX).ToArray();
for (int i = 0; i < columnName.Count(); i++)
{
table.Columns.Add(columnName[i]);
}
columnCount = columnName.Count();
}
else
{
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
object[] newValues = new object[columnCount];
int j = 0;
foreach (var p in values)
{
Type argType = p.GetType();
if (argType == typeof(bool) && !((bool)p))
{
newValues[j] = p;
j++;
}
else if(argType == typeof(int) && (int)p != 0)
{
newValues[j] = p;
j++;
}
else if (argType == typeof(double) && (double)p != 0)
{
newValues[j] = p;
j++;
}
else if (argType == typeof(string) && p != null && p != string.Empty)
{
newValues[j] = p;
j++;
}
if (j >= columnCount)
break;
}
//table.Rows.Add(values);
//table.Rows.Add(newValues);
}
return table;
}
My Grid is now this
#(Html.Kendo()
//.Grid<rtxVending.Web.Models.ProductDetails>()
.Grid(Model.ProductDetailsDataTable)
.Name("ProductDetailGrid")//.ClientDetailTemplateId("")
.HtmlAttributes(new { #style = "align:center; font-size:9px;" })
.Columns(columns =>
{
//var columnData = rtxVending.Web.Repositories.SessionRepository.GetSessionObject<System.Data.DataColumnCollection>(ApplicationConstants.sesAcquireStockDataColumnCollection);
//if(columnData != null && columnData.Count > 0)
if (Model.ProductDetailsDataTable != null)
foreach (System.Data.DataColumn column in Model.ProductDetailsDataTable.Columns)
{
columns.Bound(column.ColumnName);
}
})
.Pageable(pager => pager.Refresh(true))
.Sortable()
.Scrollable()
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("GetProductDetailsGrid", "Products"))
.Model(model =>
{
if (Model.ProductDetailsDataTable != null)
foreach (System.Data.DataColumn column in Model.ProductDetailsDataTable.Columns)
{
model.Field(column.ColumnName, column.DataType);
}
})
.Events(events => events.Error("error_handler")))
)
If there any thing i need to explain, i will respond asap. Thank Prof. Pickle!

Resources