Am trying to Bind Dropdown in MVC using Enum and it works great !! Here is sample code
//Enum Set up
public enum Name
{
Name1,
Name2,
Name3
}
public enum Number
{
11,
12,
13
}
public Name nameDropDown { get; set; }
public Number numberDropDown { get; set; }
//Like this i have have 10 Enums
//View
#Html.EnumDropDownListFor(m => m.numberDropDown) or #Html.EnumDropDownListFor(m => m.nameDropDown)
Here is where i got struck !!I am going to load only one dropdown in my view based on the condition. I can have if else if and load the drop down based on the condition. But as i have large list it wont be that good.
Is there a alternative way i can decide which enum to bind in my view ? Like dynamically locating based on the condition. Please suggest
There is no easy way to achieve this. One way would be to create an extension method, something like:
public enum Name { Name1, Name2, Name3 }
public enum Number { Number1, Number2, Number3 }
public class ViewModel
{
public Name nameDropDown { get; set; }
public Number numberDropDown { get; set; }
// x number of other enum properties
}
public static class MyHtmlHelpers
{
public static MvcHtmlString MyEnumDropDownListFor(this HtmlHelper html, string propertyName)
{
var propertyInfo = typeof(ViewModel).GetProperty(propertyName);
if (propertyInfo == null) return null;
var expParam = Expression.Parameter(typeof(ViewModel));
var expProp = Expression.Property(expParam, propertyName);
var expression = Expression.Lambda(expProp, expParam);
var htmlStr = (MvcHtmlString) typeof(SelectExtensions)
.GetMethods()
.First(m => m.Name == "EnumDropDownListFor" && m.IsGenericMethod)
.MakeGenericMethod(typeof(ViewModel), propertyInfo.PropertyType)
.Invoke(null, new object[] { html, expression });
return htmlStr;
}
}
And your view could fetch the property name from the query string and based on that show the dropdown list:
#model ViewModel
#{
var propertyName = Request["property"] ?? "numberDropDown";
}
#Html.MyEnumDropDownListFor(propertyName)
Related
I have a table where I store all the different code/value keywords that I need in my app:
public class Keyword
{
public int id { get; set;}
public string name { get; set; }
public string valuecode { get; set; }
public string valuename { get; set; }
}
Then I use Keyword to store records like these
name valuecode valuename
.DealState 1 Draft
.DealState 2 Final
.DealState 3 Cancelled
.DealType NEW New Business
.DealType RNW Renewal
.DealType WFA Waiting for approval
Then in other models I have fields that are filled using these keywords. For example,
public class Deal
{
....
public string state { get; set; }
public string type { get; set; }
....
}
I have managed to have the fields filled with "valuecode" while displaying "valuename" in Create and Edit views (I use DropDownList with a SelectList built in the controller), but I cannot find a way to display valuename instead of valuecode in Index and Details views.
I'm trying to pass the same SelectList in the ViewBag for Index, but then I do not know which syntax to use in order to replace the "state" code with the state "description" for each record returned.
Any hint?
PS: I'm quite new to .net and mvc, usually work with RoR and ActiveRecord...
EDIT
In my KeywordController I have a method
public SelectList selectKeywordValues(string kwname, object selectedKeyword = null)
{
var keywordsQuery = from d in db.Keywords
where d.name == kwname
orderby d.valuename
select d;
SelectList kwlist = new SelectList(keywordsQuery, "valuecode", "valuename", selectedKeyword);
return kwlist;
}
Then in my DealController i have the index method
public ActionResult Index()
{
var kw = new KeywordController();
ViewBag.state = kw.selectKeywordValues(".DealState");
return View(db.Deals.ToList());
}
SOLVED
In DealController the index method is the following
public ActionResult Index()
{
var kw = new KeywordController();
SelectList states = kw.selectKeywordValues(".DealState");
SelectList types = kw.selectKeywordValues(".DealType");
foreach (var item in db.Deals.ToList())
{
SelectListItem mystate = states.Where(row => row.Value == item.state).ElementAt(0);
SelectListItem mytype = types.Where(row => row.Value == item.type).ElementAt(0);
item.state = mystate.Text;
item.type = mytype.Text;
}
return View(db.Deals.ToList());
}
Now the db.Deals.ToList() is filled with descriptions and not with codes.
You can define a view model called DealViewModel that contains DealState and DealType properties. Then populate the DealViewModel with joins in LINQ before passing to the views that reference the view model.
Another approach is to use enums in EF5.
I'm trying to write a helper for my ASP.NET MVC3 website which will be able to return a new SelectList containing all the Description attribute tag of an Enum
For example, with the following enum :
public enum Test
{
[Display(Name = "Membre 1")]
Member1,
[Display(Name = "Membre 2")]
Member2
}
I would like to be able to fill a DropDownListFor with something like :
#Html.DropDownListFor(m => m.MyTest, MyHelper(Test))
(with MyTest is a Test variable).
and I expect my DropDownList contains :
Membre 1
Membre 2
I used to use this working helper :
public static string GetEnumDescription(this Enum value)
{
Type enumType = value.GetType();
var enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
var attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
var outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
... but I can't get it work in a SelectList
How can I modify this to directly "incorporate" it directly in my #Html.DropDownListFor helper ?
I have seen some helper over the Internet, especially here or here, but no one works for me. Does anyone is able to share a short and elegant helper which returns all the Display attributes of the members of an Enum in order to put them in a DropDownListFor ?
The following is what I use. It's a slightly modified version of something I found online at one point. I'd give credit where credit is due, but I don't remember where I found it originally at this point:
public static SelectList ToSelectList(this Enum enumeration)
{
var list = (from Enum d in Enum.GetValues(enumeration.GetType())
select new { Value = Enum.GetName(enumeration.GetType(), d), Text = d.GetDescription() }).ToList();
var selectedValue = (int)Enum.Parse(enumeration.GetType(), Enum.GetName(enumeration.GetType(), enumeration));
return new SelectList(list, "Value", "Text");
}
public static string GetDescription(this Enum en)
{
Type type = en.GetType();
System.Reflection.MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((System.ComponentModel.DataAnnotations.DisplayAttribute)attrs[0]).GetName();
}
return en.ToString();
}
In your view, you'd use it:
#Html.DropDownListFor(m => m.MyEnumProperty, Model.MyEnumProperty.ToSelectList())
For implementing Enum type data, I think the easiest way is to use custom Enum helper and Templates. Below is how I implement them in my project.
1) Create Enum Helper
public static class EnumHelper
{
public static IEnumerable<SelectListItem> GetItems(this Type enumType, int? selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
string[] names = Enum.GetNames(enumType);
IEnumerable<int> values = Enum.GetValues(enumType).Cast<int>();
IEnumerable<SelectListItem> items = names.Zip(values, (name, value) =>
new SelectListItem
{
Text = GetName(enumType, name),
Value = value.ToString(),
Selected = value == selectedValue
}
);
return items;
}
// Get Display Name
private static string GetName(Type enumType, string name)
{
string result = name;
DisplayAttribute attribute = enumType.GetField(name)
.GetCustomAttributes(false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (attribute != null)
{
result = attribute.GetName();
}
return result;
}
public static string GetItemName(this Type enumType, int selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
var itemName = GetName(enumType, Enum.GetNames(enumType)[selectedValue]);
return itemName;
}
}
2) Create folder call "DisplayTemplates" in Shared folder.
3) Create View inside "DisplayTemmplates". The view will look like below:
#using Demo.Web.Helper
#{
var itemName = typeof(Test).GetItemName((int)Model);
}
4) Create floder call "EditorTemplates" in Shared folder.
5) Create View inside "EditorTemplates". The view will look like below:
#using Demo.Web.Helper
#{
var items = typeof (Test).GetItems((int?)Model);
}
#Html.DropDownList("",items)
Here you have finished all of helper and templates, ready for use. When you want to implement Enum Type data, just use it like below:
Model
public class MyModel
{
public int Id { get; set; }
//
public Test Test { get; set; }
}
View
#Html.DisplayFor(m => m.Test)
or
#Html.EditorFor(m => m.Test)
Hope it helps.
For web application in development(ASP.Net MVC), I'm using the telerik grid. The grid is bound to an IQueryable of my list, because it's a big table, and I want that telerik apply it's filter on the list, and then executes this result, not dowloading 10'000 rows(with the joined tables), and then with the filter, use only rows.
I'm using(and I really need it for this page, it's one of the key feature) the filter/order of the grid.
One of the main column(determining the kind of the data) is an enum.
The problem is that I get a "Specified type member is not supported in linq to entities" as soon as I'm trying to filter/sort it.
I've to bind it on the enum(and not the mapped int) because if I use the id, filters/order by will be on an int, and I can't expect that the user knows the id of the foreign table.
I just cannot implement myself again all grids parameter(located in url)(I assume, it's either I do everything, or nothing) and filter it correctly, order it correctly).
Do you have an idea of workaround?
I don't know how your Entity Model looks like but I'll suppose that you've something like this Model:
public partial class Project
{
public int Id { get; set; }
public string Name { get; set; }
public int Status { get; set; }
}
and the Status property represents your enum value then you've this enum:
public enum ProjectStatuses
{
Current = 1,
Started = 2,
Stopped = 3,
Finished = 4,
}
Then just create new ViewModel like this :
public class ProjectDetails
{
public int Id { get; set; }
public string Name { get; set; }
public int Status { get; set; }
public ProjectStatuses StatusValue { get { return (ProjectStatuses) Status; } }
// This property to display in telerik ClientTemplate
public string StatusName { get { return Enum.GetName(typeof (ProjectStatuses), Status ); } }
}
And because I love Extension Methods I'll add this one :
public static class ModelListExtensions
{
public static IQueryable<ProjectDetails> ToViewModelDetails(this IQueryable<Project> modelList)
{
return modelList.Select(m => new ProjectDetails
{
Id = m.Id,
Name = m.Name,
Status = m.Status,
};
}
}
Update :
Here is the Controller
public ActionResult Index()
{
int total;
var viewModel = getGridList(out total);
ViewBag.Total = total;
return View(viewModel);
}
//this Action to get ajax pages
[GridAction(EnableCustomBinding = true)]
public ActionResult ReGetIndex(GridCommand command, int roleId)
{
int total;
var list = getGridList(out total, roleId, command);
return View(new GridModel {Data = list, Total = total});
}
private IEnumerable<ProjectDetails> getGridList(out int total, GridCommand command = null)
{
command = command ?? new GridCommand {Page = 1};
foreach (var descriptor in command.SortDescriptors)
{
if (descriptor.Member == "StatusValue")
descriptor.Member = "Status";
}
foreach (FilterDescriptor descriptor in command.FilterDescriptors)
{
if (descriptor.Member == "StatusValue")
descriptor.Member = "Status";
}
var list = modelService.AllAsQuery()
.ToViewModelDetails() // To convert it to our ViewModel if we have one
.Where(command.FilterDescriptors);
total = list.Count();
return (IEnumerable<ProjectDetails>) list.Sort(command.SortDescriptors)
.Page(command.Page - 1, command.PageSize)
.GroupBy(command.GroupDescriptors).ToIList();
}
And this is the View
#model IEnumerable<ProjectDetails>
#{
Html.Telerik()
.Grid(Model)
.Name("ProjectsGrid")
.Sortable()
.Filterable()
.EnableCustomBinding(true)
.DataBinding(dataBinding => dataBinding
.Ajax()
.Select("ReGetIndex", "Projects"))
.Pageable(page => page.Style(GridPagerStyles.PageSizeDropDown | GridPagerStyles.NextPreviousAndNumeric).Total(ViewBag.Total))
.Columns(column =>
{
column.Bound(m => m.Id).Hidden(true);
column.Bound(m => m.Name);
column.Bound(m => m.StatusValue).ClientTemplate("<#= StatusName #>");
})
.Render();
}
Update :
If you want to enforce at least one sort order you could use something like this:
if (!command.SortDescriptors.Any())
{
command.SortDescriptors.Add(new SortDescriptor {Member = "YourDefaultProperty"});
}
You don't really have choice (or few annoying choices)
Wether you use a class instead of enum (but if you used an enum, that's because it was better).
Or you "pseudo-sort" your enum, and use the mapped int.
public enum TT
{
Brown = 0,
Green = 1
}
Of course, you'll have to check the actual datas (mapped int) in your DB and update them to conform to the new order (can't change enum order without impact). And you'll have to do that everytime you want to insert a value between existing enum values.
Or you wait for next EF / linq / c# version, which should have enum support in linq2entities
I'm using jqGrid to display some data on a page. Within the controller action, we're using an anonymous object to represent the data that the jqGrid needs. My question is, is there a way we can create a strongly typed object to represent the jqGrid data that we are sending with Json()?
Main reason for this is so that we can do unit testing with the objects that are being sent to it.
Thanks!
EDIT:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GridData(FormCollection form, string alias, string location, string state)
{
int pageSize = Convert.ToInt32(form["rows"]);
int pageIndex = Convert.ToInt32(form["page"]) - 1;
var deviceList = this._device.GetList(CreateFilter(location,alias,state),this._securityCache.GetSecurityContext(),pageSize,pageIndex);
int totalResults = deviceList.TotalRecords;
int totalPages = (int)Math.Ceiling((float)totalResults / (float)pageSize);
var jsonData = new {
total = totalPages,
page = pageIndex + 1,
records = totalResults,
rows = (from device in deviceList.Data
select new {i = device.Alias,cell = new string[]{device.Alias,device.Location,device.RatePlan,device.State,device.DateCreated.ToString()}}).ToArray()
};
return Json(jsonData);
This above here works, but we can't unit test the data that is being passed into the Json() method.
var newJsonData = new JsonJQGridReturnData();
newJsonData.total = totalPages;
newJsonData.page = pageIndex + 1;
newJsonData.records = totalResults;
List<JsonJQGridRow> list = new List<JsonJQGridRow>();
foreach (var device in deviceList.Data)
{
list.Add(new JsonJQGridRow(device.Alias, new string[] { device.Alias, device.Location, device.RatePlan, device.State, device.DateCreated.ToString() }));
}
newJsonData.rows = list.ToArray();
_cookieHelper.SaveCookie("DeviceListIndex", this._securityCache.GetSecurityContext().UserID.ToString(), COOKIE_PAGE_SIZE_KEY, pageSize.ToString());
return Json(newJsonData);
}
Here is my poor attempt at trying to wrap these into strongly typed objects. Unfortunately, running this gives me a "u is undefined" in the jqGrid file. I suspect that this is because the json being passed in is not correctly formatted. Here are the classes....
[DataContract]
public class JsonJQGridReturnData
{
[DataMember]
public int total { get; set; }
[DataMember]
public int page { get; set; }
[DataMember]
public int records { get; set; }
[DataMember]
public JsonJQGridRow[] rows { get; set; }
}
[DataContract]
public class JsonJQGridRow
{
public JsonJQGridRow(string i, string[] columns)
{
this.i = i;
this.cells = columns;
}
[DataMember]
public string i { get; set; }
[DataMember]
public string[] cells { get; set; }
}
If I understand your question you can use Generics to do this:
Model:
// represents one row in the JQGrid
class Customer
{
public string firstname { get; set; }
public string lastname { get; set; }
}
JQGrid class:
class JQGridData<TModel>
{
// add in whatever other properties you want for JQGrid
public int responseTime {get; set; };
public List<TModel> rows = new List<TModel>();
}
Controller Action :
public JsonResult GridData(int page)
{
var gridData = new JQGridData<Customer>();
// Populate your data here, this is just an example:
gridData.rows.Add(new Customer()
{
firstname = "fred", lastname = "pharkas"
});
// return the result
return Json(gridData, JsonRequestBehavior.AllowGet);
}
Result:
{
responseTime: 0
rows: [
{
firstname: "fred"
lastname: "pharkas"
}
]
}
Is that what you were asking?
David,
Here's the kinda thing i use in an app i'm working on at the moment for this type of thing. I know it doesn't provide a strongly typed object as such, but the 'list' could be a part of the model that is then sent ToArray() at the end of the piece.
public JsonResult GridData(int id)
{
// get our messages based on id
var bookingmessagesList = _repository.Find(x => x.ID == id);
var list = new ArrayList();
foreach (var bookingmessage in bookingmessagesList) //populate data containers with read data
{
list.Add(new
{
bookingmessage.ClassRowVersionDate,
bookingmessage.ID,
bookingmessage.BookingID,
bookingmessage.AssignedFrom,
bookingmessage.AssignedTo,
bookingmessage.AssignedDate,
bookingmessage.CompletedDate,
bookingmessage.MessageType,
bookingmessage.Notes
});
}
int totalOjectCount = list.Count;
return Json(new { dataitems = list.ToArray(), totalItems = totalOjectCount });
}
hope it gives you some ideas.. Will be interested to see the suggestions made.
Here's a quick take on a strongly-typed JQGridResult.
public class JQGridResult<T> : JsonResult where T : class
{
public T Model
{
get { return (T)this.Data; }
set { this.Data = value; }
}
}
Used as...
return new JQGridResult<JsonModel> {
Model = new GridModel { ... initialize model here ... }
});
where GridModel is basically a container class holding the strongly typed properties for the grid.
I feel really silly. I had a misspelling in the GridRow that was causing jqGrid to blow up. After I fixed that, I was able to get the jqGrid to work with my strongly typed object...
Now in my unit tests, I can just do...
var result = controllerToTest.GridData(form, null, null, null) as JsonResult;
var data = result.Data as JsonJQGridReturnData;
and now I can access the fields :D
I would like to use Linq and strongly typed views in the right way. at the moment I do the following:
Make a Model to verify agianst:
public class Menu
{
public int Id { get; private set; }
public string Text { get; private set; }
public string Action { get; private set; }
public string Controller { get; private set; }
public string Parameter { get; private set; }
public string Langue { get; private set; }
public Menu(int id, string controller, string action, string parameter, string text)
{
Id = id;
Controller = controller;
Action = action;
Text = text;
Parameter = parameter;
}
Use Linq to get the data from the database into the model:
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
var tabList = (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new { m.Id, p.Controller, p.Action, p.Parameter, ml.Text}).ToList();
List<Menu> menu = new List<Menu>();
foreach (var item in tabList)
{
menu.Add(new Menu(item.Id, item.Controller, item.Action, item.Parameter, item.Text));
}
return menu;
}
I am pretty convinced that this is not the optimal way to do this and have 2 questions:
When I get the data from the database I first use a var and then have to move it to the object with a foreach. this seems like a waste of both my time and less effeicent then getting it with sql.
I have been told that I can just verify up agianst the entitymodel. Even if i use multiple entities in a view. is this true? (the one telling me this wes not able to get it to work and I have not been able to find anything about it online).
I will try to look back on this post in the next couple of hours, but might have to wait 24 hours.
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
return (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new Menu(m.Id, p.Controller, p.Action, p.Parameter, ml.Text)
).ToList();
}
As for the validation is concerned you shouldn't use multiple entities in the view. You should use a single entity which is called ViewModel. This ViewModel is a class that represents the data on the view. If you are using DataAnnotations for validation you could decorate this view model properties with attributes that indicate how to be validated.