Reputation: 1
I populate Dropdownlist from database and I want to show certain elements in red by setting their "class" attribute. So, I tried lots of ways and later I have created a custom HTML Helper for Dropdownlist. But it does not make any sense and although it seems to be added class attribute, this paramater cannot pass to Razor View from Controller. Could you help pls?
MyHelper.cs:
public static MvcHtmlString Custom_DropdownList(this HtmlHelper helper, string name, IEnumerable<SelectListItem> list, object htmlAttributes)
{
TagBuilder dropdown = new TagBuilder("select");
dropdown.Attributes.Add("name", name);
dropdown.Attributes.Add("id", name);
StringBuilder options = new StringBuilder();
foreach (var item in list)
{
options = options.Append("<option value='" + item.Value + "'>" + item.Text + "</option>");
}
dropdown.InnerHtml = options.ToString();
dropdown.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(dropdown.ToString(TagRenderMode.Normal));
}
Controller:
private void PopulateMeetingsDropDownList(object selectedMeetings = null)
{
var meetingsQuery = repository.Meetings
.Join(repository.Cities, m => m.MeetingCityId, c => c.CityID,
(m, c) => new
{
CityID = c.CityID,
CityName = c.CityName,
MeetingDate = m.MeetingStartDate
}
)
.OrderBy(x => x.CityID)
.AsEnumerable()
.Select(
i => new
{
CityID = i.CityID,
Name = string.Format(
"{0} ({1:dd MMMM yyyy})",
i.CityName, i.MeetingDate),
Expired = i.MeetingDate < DateTime.UtcNow
}
).ToList();
var selectItems = new List<SelectListItem>(meetingsQuery.Count);
foreach (var record in meetingsQuery)
{
var item = new SelectListItem
{
Text = record.Name,
Value = record.Name
};
if (record.Expired)
{
item.Attributes.Add("class", "disabled"); //!!! Problem on this line
}
selectItems.Add(item);
}
ViewData["MeetingId"] = new SelectList(meetingsQuery, "CityID", "Name", selectedMeetings);
}
But after applying this method I got an error "'System.Web.Mvc.SelectListItem' does not contain a definition for 'Attributes' and no extension method 'Attributes' accepting a first argument of type...". So, I think I need to use another property or helper to assign class properties to custom racords (there is no problem filtering records on the "if (record.Expired)" line).
View:
@Html.Custom_DropdownList("MeetingId", ViewData["MeetingId"] as SelectList, new { id = "meetingId"})
Could you clarify me how to provide this? Thanks in advance.
Here is the modificated code for having ability for both Class and Disabled attributes:
Updated Code (for MyDropdownListFor):
Custom HTML Helper Class:
public static class MyHelpers
{
public class MySelectItem : SelectListItem
{
public string Class { get; set; }
public string Disabled { get; set; }
}
public static MvcHtmlString MyDropdownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<MySelectItem> list, string optionLabel, object htmlAttributes)
{
return MyDropdownList(htmlHelper, ExpressionHelper.GetExpressionText(expression), list, optionLabel, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString MyDropdownList(this HtmlHelper htmlHelper, string name, IEnumerable<MySelectItem> list, string optionLabel, IDictionary<string, object> htmlAttributes)
{
TagBuilder dropdown = new TagBuilder("select");
dropdown.Attributes.Add("name", name);
dropdown.Attributes.Add("id", name);
StringBuilder options = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
options = options.Append("<option value='" + String.Empty + "'>" + optionLabel + "</option>");
foreach (var item in list)
{
if(item.Disabled == "disabled")
options = options.Append("<option value='" + item.Value + "' class='" + item.Class + "' disabled='" + item.Disabled + "'>" + item.Text + "</option>");
else
options = options.Append("<option value='" + item.Value + "' class='" + item.Class + "'>" + item.Text + "</option>");
}
dropdown.InnerHtml = options.ToString();
dropdown.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(dropdown.ToString(TagRenderMode.Normal));
}
}
Controller:
private void PopulateMeetingsDropDownList(object selectedMeetings = null)
{
var meetingsQuery = repository.Meetings
.Join(repository.Cities, m => m.MeetingCityId, c => c.CityID,
(m, c) => new
{
CityID = c.CityID,
CityName = c.CityName,
MeetingDate = m.MeetingStartDate
}
)
.OrderBy(x => x.CityID)
.AsEnumerable()
.Select(
i => new
{
Value = i.CityID.ToString(),
DisplayValue = string.Format(
"{0} ({1:dd MMMM yyyy})",
i.CityName, i.MeetingDate),
Expired = i.MeetingDate < DateTime.UtcNow
}
).ToList();
var selectItems = new List<MyHelpers.MySelectItem>(meetingsQuery.Count);
foreach (var record in meetingsQuery)
{
var item = new MyHelpers.MySelectItem
{
Text = record.DisplayValue,
Value = record.Value
};
if (record.Expired)
{
item.Class = "disabled";
item.Disabled = "disabled";
}
selectItems.Add(item);
}
ViewBag.MeetingData = selectItems;
}
View:
<label>Meeting</label>
@Html.MyDropdownListFor(m => m.MeetingId, ViewBag.MeetingData as List<MyHelpers.MySelectItem>, "---- Select ----",
new { name = "meetingId", id = "meetingId"})
Upvotes: 2
Views: 3925
Reputation: 988
You can inherit the SelectList and add your own properties to the class, like this:
public class MySelectListItem : SelectListItem
{
public bool Highlighted { get; set; }
}
Once you have the custom class, you write your helper based on it. You can apply different css classes or change the style attribute.
public static class MyHtmlHelpers
{
public static MvcHtmlString DropDownListHighlighted(this HtmlHelper helper, string name, IEnumerable<MySelectListItem> itens)
{
StringBuilder dropDown = new St();
dropDown.AppendFormat("<select id='{0}' name='{0}'>", name);
foreach (var item in itens)
{
dropDown.AppendFormat("<option selected='{2}' value='{1}' class='{3}'>{0}</option>",
item.Text,
item.Value,
item.Selected ? "selected" : "",
item.Highlighted ? "highlighted" : "normal");
}
dropDown.Append("</select>");
return MvcHtmlString.Create(dropDown.ToString());
}
}
In your view (don't forget to add the namespace of your HTML helper class at the web.config or at the view):
@Html.DropDownListHighlighted("DropDown", ViewData["DropDownOptions"] as List<MySelectListItem>)
DropDownListHighlightedFor
You could do something like this to implementation a simple DropDownListHighlightedFor:
public static MvcHtmlString DropDownListHighlightedFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<MySelectListItem> itens)
{
return DropDownListHighlighted(htmlHelper, ExpressionHelper.GetExpressionText(expression), itens);
}
In the view:
@Html.DropDownListHighlightedFor(x => x.Option, ViewData["DropDownOptions"] as List<MySelectListItem>)
Upvotes: 2
Reputation: 15463
TL;DR; You are using SelectList
, but you don't need to.
Create a new view model class, which you will pass to the view. It will contain all the properties you need, and may look like this:
Custom class to hold info about items:
public class CustomSelectItem
{
public string Text {get;set;}
public string Value {get;set;}
public string Class {get;set;}
public bool Selected {get;set;}
}
Since you are passing this data using ViewData, you don't have a limitation and you can put anything there. I advise that you use ViewBag instead of ViewData.
In your controller you can create a new List<CustomSelectItem>
and pass it. When building the collection, if the item is expired just set the Class property to "disabled" or whatever you are using. Here is the code (I skipped the part where you get the meetings):
Controller:
var selectItems = new List<CustomSelectItem>(meetingsQuery.Count);
foreach (var record in meetingsQuery)
{
var item = new CustomSelectItem
{
Text = record.Name,
Value = record.Name
};
if (record.Expired)
{
item.Class = "disabled";
}
selectItems.Add(item);
}
ViewBag.MeetingData = selectItems;
Then modify the custom helper method you have created to accept a collection of CustomSelectItem
instead of SelectListItem
. You can write directly the HTML, because you have access to the Class property.
Custom helper method:
public static MvcHtmlString Custom_DropdownList(this HtmlHelper helper, string name, IList<CustomSelectItem> list, object htmlAttributes)
{
TagBuilder dropdown = new TagBuilder("select");
dropdown.Attributes.Add("name", name);
dropdown.Attributes.Add("id", name);
StringBuilder options = new StringBuilder();
foreach (var item in list)
{
options = options.Append("<option value='" + item.Value + "' class='" + item.Class + "'>" + item.Text + "</option>");
}
dropdown.InnerHtml = options.ToString();
dropdown.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(dropdown.ToString(TagRenderMode.Normal));
}
Once this is done, you can invoke the helper from the view as follows:
View:
@Html.Custom_DropdownList("MeetingId", ViewBag.MeetingData as List<CustomListItem>, new { id = "meetingId"})
Upvotes: 2