使用 EditorTemplate+MVC 在 Kendo GridView 中使用 Kendo Dropdown





0/5 (0投票)
在 ASP.NET-MVC 中,使用 Editor Template 和 Client Template 实现 Kendo 子网格中的 Kendo 下拉框
引言
本文将指导您使用 Editor Templates 和 ASP.NET MVC 在 Kendo 子网格中渲染 Kendo 下拉框。
背景
Kendo 子网格最初使用 HTML 下拉框代替 Kendo 下拉框。它还包含一段冗长的 JavaScript 代码。我浏览了很多文章来尝试实现它,但每个人都缺少一些内容或不清楚,因此我一路学习!!
此实现最好的部分在于,首次渲染时,如果下拉框尚未保存任何值,则它会将其渲染为空白,但单击网格单元格后,它会变成一个用于选择的下拉框。
此外,只要您从网格单元格导航出去,编辑模式就会结束,并返回到数据库中保存的最后一个值。
使用代码
在以下代码中,OrderGrid(网格)是包含另一个 Kendo 网格(grid2_#=OpportunityId)的主 Kendo 网格。
在 OrderGrid.cshtml 中
@(Html.Kendo().Grid<Models.OrderGridViewModel>()
.Name("grid")
.HtmlAttributes(new { style = "line-height:10px;" })
.Columns(columns =>
{
columns.Bound(m => m.VersionName).Title("Ver");
columns.Bound(m => m.OrderStatusText).Title("Status");
columns.Bound(m => m.StartDateTime).Title("Start Date");
columns.Bound(m => m.EndDateTime).Title("End Date");
})
.Selectable(s => s.Mode(GridSelectionMode.Single).Type(GridSelectionType.Cell))
.Events(events => events.DataBound("grid_dataBound"))
.Pageable()
.Sortable() // Enable sorting
.ClientDetailTemplateId("OrderDetailsAll")
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(5)
.Read(read => read.Action("Get", "Order"))
)
)
<script id="OrderDetailsAll" type="text/kendo-tmpl">
@(Html.Kendo().Grid<Models.OrderDetailAllViewModel>()
.Name("grid2_#=OpportunityId#")
.Events(e =>
{
e.DataBound("grid2_onDataBound");
e.Edit("onEdit");
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Columns(columns =>
{
columns.Bound(m => m.IncludeInForecast).HtmlAttributes(new { Align = "center" }).Title("Include In Forecast?").ClientTemplate("\\# if (IncludeInForecast == true ) { \\#" + "Yes" +
"\\# }else { \\#" + "No" +
"\\# } \\#"); //this is a checkbox within Kendo Sub-grid
);
columns.Bound(m => m.SalesStrategyId).EditorTemplateName("SalesStrategies").Title("Sales Strategy").ClientTemplate("\\#=SalesStrategyName\\#"); //this is a dropdown within Kendo Sub-grid
})
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("GetDetailsAll", "Order", new { opportunityId = "#=OpportunityId#" }))
.Model(model =>
{
model.Field(x => x.IncludeInForecast).Editable(true);
model.Field(x => x.EditIncludeInForecast).Editable(false);
model.Field(x => x.SalesStrategyList).DefaultValue(ViewData["ssDefault"] as OMSWeb.Models.OrderDetailAllViewModel);
})
)
.ToClientTemplate())
</script>
请注意,对于 ClientTemplate,我们有("\\#=SalesStrategyName\\#")- 这对于指示属性位于下一级别的子网格中是必要的。
请注意 EditorTemplateName(SalesStrategies)- 现在添加一个新的文件夹 - EditorTemplates,位于与您的页面所在的 Views 文件夹的同一级别,并在其中添加一个新的 cshtml 文件,名为“SalesStrategies.cshtml”(例如,我的文件夹层次结构为 - \~OMSWeb\Views\Order\EditorTemplates\SalesStrategies.cshtml,OMSWeb 是 Web 项目)
@using Kendo.Mvc.UI
@using Utility;
@using System.Collections;
@model OMSWeb.Models.OrderDetailAllViewModel
@(Html.Kendo().DropDownListFor(m => m.SalesStrategyList)
.OptionLabel("-- Select Sales Strategy --")
.DataValueField("Value")
.DataTextField("Text")
.BindTo(ViewData["ssList"] as IEnumerable<List<SelectListItem>>)
.AutoBind(true)
.Name("SalesStrategyddList")
.Events(e =>
{
e.DataBound("onDataBound"); //Logic/code for this event is written in OrderGrid.cshtml page below
})
)
ViewModel (OrderDetailAllViewModel.cs)
(请注意,模型包含其他相关属性,但为了保持简洁和清晰,已省略了它们)
public class OrderDetailAllViewModel : BaseModel
{
public OrderDetailAllViewModel()
{
this.SalesStrategyList = new List<SelectListItem>();
}
public string SalesStrategyName { get; set; }
public int? SalesStrategyId { get; set; }
[UIHint("SalesStrategies")] //This has to match the EditorTemplateName given above for Bound column - SalesStrategy in the sub-grid
public List<SelectListItem> SalesStrategyList { get; set; }
}
Controller 代码(在 ~OMSWeb\Controllers\OrderController.cs 中),OrderBusinessService.cs
//to get OrderDetails for sub-grid (in OrderController.cs file)
public ActionResult GetDetailsAll([DataSourceRequest] DataSourceRequest request,Guid opportunityId)
{
_iOrderBusinessService = new OrderBusinessService();
List<OrderDetailAllViewModel> mList = new List<OrderDetailAllViewModel>();
if(opportunityId != null)
{
mList = _iOrderBusinessService.GetOrderDetails(opportunityId).OrderBy(d => d.SubSystemId).ThenBy(d => d.StartDate).ToList(); //function call as described immediately below
}
DataSourceResult result = mList.ToDataSourceResult(request);
if (mList!= null && mList[0].SalesStrategyList.Any())
{
ViewData["ssList"] = mList[0].SalesStrategyList;
}
return Json(result);
}
//Gets Sales Strategy from Database, handles null/empty values (in OrderBusinessService.cs file)
public List<OrderDetailAllViewModel> GetOrderDetails(Guid opportunityId)
{
OrderDetailAllViewModel m;
List<OrderDetailAllViewModel> mList = new List<OrderDetailAllViewModel>();
ISalesStrategy ssMgr = new SalesStrategyManager();
List<OrderDetail> dList = _iOrderManager.GetOrderDetails(opportunityId, false);//Gets OrderDetails from OrderManager which in turn calls the Database
foreach (OrderDetail d in dList)
{
m.SalesStrategyId = d.SalesStrategyId.HasValue ? d.SalesStrategyId.Value : 0; //Have value a Zero if null
var ssItem = ssMgr.GetSalesStrategyById(m.SalesStrategyId.Value);
if(ssItem != null)
{
m.SalesStrategyName = string.IsNullOrEmpty(ssItem.SalesStrategyName) ? string.Empty : ssItem.SalesStrategyName;
}
else
{
m.SalesStrategyName = string.Empty; //Name as empty string
}
mList.Add(m);
}
return mList;
}
// To get SalesStrategies from Database (in OrderController.cs file)
public JsonResult GetSalesStrategies([DataSourceRequest] DataSourceRequest request)
{
_iOrderBusinessService = new OrderBusinessService();
OrderDetailAllViewModel vm = new OrderDetailAllViewModel();
var salesStrategyList = _iOrderBusinessService.GetSalesStrategiesList();
ViewData["ssList"] = vm.SalesStrategyList;
return Json(salesStrategyList, JsonRequestBehavior.AllowGet);
}
这里的逻辑是,在 SalesStrategy 下拉框的 Databound 事件上,我正在进行 ajax 调用以从数据库获取值。为了绑定回保存的值,使用 existingValue
(以下代码在 OrderGrid.schtml 中编写)
<script type="text/javascript">
var dataItem,selectedOrderDetailId,existingValue,newSelectedValue,existingForecast,selectedProposalId,newIIFValue;
function onDataBound(e)
{
$.ajax({
url: "/Order/GetSalesStrategies",
type: "GET",
success: function (result)
{
if(result != null && result.length > 0)
{
$("#SalesStrategyddList").kendoDropDownList({
dataTextField: "Text",
dataValueField: "Value",
selectedIndex: <code>existingValue</code>, //this is assigned in the onEdit event that is fired before onDataBound
dataSource: result,
optionLabel: "-- Select Sales Strategy --"
});
$("#SalesStrategyddList").val(existingValue);
}
},
});
}
function onEdit(e)
{
selectedOrderDetailId = e.model.OrderDetailId;
<code>existingValue</code> = e.model.SalesStrategyId;
existingForecast = e.model.ForecastYesNo;
selectedGuid = e.model.OpportunityId;
selectedProposalId = e.model.ProposalId;
var isEditIIF = e.model.EditIncludeInForecast;
var orderDetailGrid = $('#grid2_' + selectedGuid).data("kendoGrid");
if (e.container[0].innerHTML.indexOf('Sales Strategy') >= 0) //check if SalesStrategyddList edited
{
orderDetailGrid.table.on("change", "#SalesStrategyddList", onChange); //trigger onChange event for SalesStrategyddList only
}
else if (e.container[0].innerHTML.indexOf('Forecast') >= 0) //check if IncludeInForecast edited
{
// custom logic to handle IncludeInForecast checkbox
if (isEditIIF)
{
$(this)[0].element[0].disabled = false;
orderDetailGrid.table.on("change", "#IncludeInForecast", onChange); //trigger onChange event for IncludeInForecast only when EditIncludeInForecast true
}
else
{
$(this)[0].element[0].disabled = true;
}
}
}
function onChange(e)
{
var orderDetailGrid = $('#grid2_' + selectedGuid).data("kendoGrid");
if (e.target.name == "IncludeInForecast")
{
//code for custom logic to handle change for IncludeInForecast
}
else if (e.target.name == "SalesStrategyddList")
{
newSelectedValue = e.target.value;
//call for SalesStrategy Update
var dataToSend =
{
orderDetailId: selectedOrderDetailId,
selectedSalesStrategy: newSelectedValue
};
if (selectedOrderDetailId != 0 && newSelectedValue != 0)
{
$.ajax({
url: "/Order/UpdateSalesStrategy",
type: "POST",
data: dataToSend,
success: function (result)
{
if (result != null && result == "True")
{
orderDetailGrid.dataSource.page(1); //refresh datagrid if update was successfull to reflect new changes
}
},
});
}
}
}
</script>
Ajax 调用 Controller 代码以更新数据库中的 SalesStrategy(在 OrderController.cs 文件中)
[HttpPost]
public bool UpdateSalesStrategy(int orderDetailId, int selectedSalesStrategy)
{
_iOrderBusinessService = new OrderBusinessService(); //this is turn calls OrderManager to update in Database
return _iOrderBusinessService.UpdateSalesStrategy(orderDetailId, selectedSalesStrategy);
}
关注点
在 Kendo 网格中存在 Kendo 下拉框的情况下,onEdit 事件会捕获 SalesStrategy 的保存值,而 Kendo 下拉框(用于 SalesStrategy)的 onDatabound 事件会进行 ajax 调用以从数据库获取值。我浏览了很多帖子,但没有找到更好的方法来完成此操作。
请注意 - 在其中一张图片(SalesStrategy_OnCellClick.jpg)中,显示了工具提示(在 SalesStrategy 网格单元格上鼠标悬停时)- 请注意我没有在此帖子中包含该代码,如果有人要求/需要它,我会这样做。