问题
I have a Telerik MVC grid, in an MVC 3 application with Razor, which is being Ajax-bound. I am now trying to add a drop list column to it, so that users can use it in edit mode, but can't figure out how. The grid displays a list of Products, and I want the drop list to contain a collection of ProductCategories, to which the Product can be associated. I've been at this for hours now and I'm out of ideas. I really hope someone here can help :)
I have been referencing a Telerik demo, which is located here.
I think the part that is hanging me up is in a help view that the demo uses. In the demo, this is called "ClientEmployee(Editor)." In my case, I've placed the helper in a file called "ProductCategoryDropList.cshtml". In this helper, I'm having a tough time getting the DropDownList to bind correctly. I think this might be because I'm not setting up the BindTo() method with the right data somehow. I've marked this point of confusion in the sample DropDownList Helper code, below, with "SomeCollectionReference," as the first parameter in a "new SelectList()" constructor call. When I try to put "Model" in that spot, I get a NullReferecne exception. When I try to access ViewBag data containing the list, I get a message similar to "the SelectList doesn't have a ProductCategoryID column," or something like that. So, I'm not sure what else to try.
I'm not sure how clear this description of my problem is, but in an effort to be complete, I've included the code that I think is relevant below.
Controller:
public ActionResult Index()
{
ViewBag.ProductCategories = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductcategoryName");
var products = _productService.GetProducts().ToList();
var presentationModel = _mapper.MapAsList(products);
return View(presentationModel);
}
//
// GET: /Product/
[GridAction]
public ViewResult _Index()
{
ViewBag.ProductCategories = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductcategoryName");
return View(new GridModel<ProductPresentationModel>
{
Data = _mapper.MapAsList(_productService.GetProducts().ToList())
});
}
View:
This is a bit long, but I've tried to simplify it by placing "// <--- DropList Here" next to the column that I am trying to work with.
@model IEnumerable<Models.PresentationModels.ProductPresentationModel>
@(Html.Telerik().Grid(Model).HtmlAttributes(new { style = "width: 100%;" })
// Give the Grid an HTML id attribute value
.Name("ProductGrid")
// Establish the promiry key, to be used for Insert, Update, and Delete commands
.DataKeys(dataKeys => dataKeys.Add(p => p.ProductID))
// Add an Insert command to the Grid Toolbar
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.ImageAndText))
// Using Ajax Data Binding to bind data to the grid
.DataBinding(dataBinding => dataBinding
// Ajax Binding
.Ajax()
.Select("_Index", "Product")
// Home.Insert inserts a new data record
.Insert("Create", "Product")
// Home.Update updates an existing data record
.Update("Edit", "Product")
// Home.Delete deletes an existing data record
.Delete("Delete", "Product")
)
.Columns(columns =>
{
columns.Bound(p => p.ProductName).Width(120);
columns.Bound(p => p.ProductDescription).Width(150);
columns.Bound(p => p.PricePerMonth).Width(120);
columns.Bound(p => p.ProductImagePath).Width(150)
columns.Bound(p => p.ProductActive).Width(120)
.ClientTemplate("<input type='checkbox' disabled='disabled' name='Active' <#= ProductActive ? checked='checked' : '' #> />");
columns.Bound(p => p.ProductCategoryName); // <--- DropList Here
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Image);
commands.Delete().ButtonType(GridButtonType.Image);
});
})
.Editable(editing => editing.Mode(GridEditMode.PopUp))
.ClientEvents(events => events.OnEdit("onEdit"))
.Pageable()
.Scrollable()
.Sortable()
.Filterable()
)
@section HeadContent {
<script type="text/javascript">
function onEdit(e) {
$(e.form).find('#ProductCategoryName').data('tDropDownList').select(function (dataItem) {
return dataItem.Text == e.dataItem['ProductCategoryName'];
});
}
</script>
}
Model:
[DisplayName(@"Category Name")]
[UIHint("ProductCategoryDropList"), Required]
[StringLength(255, ErrorMessage = @"Product Category Name cannot be more than 255 characters in length")]
public string ProductCategoryName
{
get
{
string name = string.Empty;
if (_model.ProductCategory != null)
{
name = _model.ProductCategory.ProductCategoryName;
}
return name;
}
set
{
if (_model.ProductCategory != null)
{
_model.ProductCategory.ProductCategoryName = value;
}
}
}
DropList Helper:
@model Models.PresentationModels.ProductPresentationModel
@(Html.Telerik().DropDownList()
.Name("ProductCategoryName")
.BindTo(new SelectList(<SomeCollectionReference>, "ProductCategoryID", "ProductCategoryName"))
)
ProductMapper:
public List<ProductPresentationModel> MapAsList(List<Product> products)
{
//var categoryList = new SelectList(_productCategoryService.GetProductCategories().ToList(), "ProductCategoryID", "ProductCategoryName");
var presentationModels = products
.Select(x => new ProductPresentationModel()
{
ProductID = x.ProductID,
ProductCategoryID = ((x.ProductCategory != null) ? x.ProductCategory.ProductCategoryID : 0),
ProductCategoryName = ((x.ProductCategory != null) ? x.ProductCategory.ProductCategoryName : String.Empty),
ProductName = x.ProductName,
ProductDescription = x.ProductDescription,
PricePerMonth = x.PricePerMonth,
ProductImagePath = x.ProductImagePath,
ProductActive = x.ProductActive,
ProductCategories = new SelectList(_productCategoryService.GetProductCategories().ToList(), "ProductCategoryID", "ProductCategoryName")//categoryList
}).ToList();
return presentationModels;
}
回答1:
I managed to work this out, somewhat, but I still have a question. here's what I changed to get it working:
Created a ViewData object in the controller, like this ...
public ActionResult Index()
{
// ViewData object here ...
ViewData["ProductCategories"] = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductCategoryName");
var products = _productService.GetProducts().ToList();
var presentationModel = _mapper.MapAsList(products);
return View(presentationModel);
}
//
// GET: /Product/
[GridAction]
public ViewResult _Index()
{
// ViewData object here ...
ViewData["ProductCategories"] = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductCategoryName");
return View(new GridModel<ProductPresentationModel>
{
Data = _mapper.MapAsList(_productService.GetProducts().ToList())
});
}
Then, I used the ViewData object in the DropDownListHelper, like this ...
@using System.Collections
@model Models.PresentationModels.ProductPresentationModel
@(Html.Telerik().DropDownList()
.Name("ProductCategoryName")
.BindTo(new SelectList((IEnumerable)ViewData["ProductCategories"], "Value", "Text"))
);
My question now is ... do I have to use the ViewData object? I'd love to be able to use a property off of my model. But, for some reason my Model is always NULL inside the Helper file. And, if I try to place the DropDownList code inside the Grid creation code, the DropDownList doesn't work at all.
So, do I have any other options?
回答2:
Currently I'm facing same problem as you write and it is really true what Telerik guys wrote you. Partial view is pre-rendered on server (content included). This may be sufficient solution, if the list of allowed values is static, but...
...imagine that you want to have different list of allowed values for each grid row. In that case this concept is not feasible...
Since there is just one combo (per column) within grid, only one solution I found is to handle onEdit grid event where you can databind combo box to allowed values using AJAX. In grid onEdit handler you can access all data fields of proper row, so you can use them for binding purposes.
Regards, Ondrej.
回答3:
I asked the good support folks at Telerik about this. Here is the answer they gave me:
The Model is null because the DropDownList partial view is rendered for ajax editing. In that case the grid prerenders all partial view editor templates so it can use them on the client-side. In that case Model will be null. If you used server binding and editing the Model would be set to the right value.
At this point, I'm going to accept this post as the answer to my question. It's unfortunate that I have to accept my own answer in this case, but ... well, I didn't get any others to choose from :)
来源:https://stackoverflow.com/questions/7087546/telerik-mvc-grid-how-to-use-dropdownlist-in-a-column