I am trying to make my code more readable.
This is an MVC project i am using an hard-coded
ViewBag.Origin = new List<SelectListItem>
{
new SelectListItem { Text = "Born", Value = "Born"},
new SelectListItem { Text = "Donated", Value = "Donated"},
new SelectListItem { Text = "Bought", Value = "Bought"}
};
lot of time in app so i decided to move it into a repository class.
public class Repository
{
public List<SelectListItem> GetOriginList()
{
List<SelectListItem> originItems = new List<SelectListItem>
{
new SelectListItem { Text = "Born", Value = "Born"},
new SelectListItem { Text = "Donated", Value = "Donated"},
new SelectListItem { Text = "Bought", Value = "Bought"}
};
return originItems;
}
Then tried to access it.
public class CowController : Controller
{
Repository repository = new Repository();
ActionResult Create() {
ViewBag.origin = repository.GetOriginList();
return View();
}
}
My View
#Html.DropDownList("Origin", "Select Origin")
But it view me run time error.
An exception of type 'System.InvalidOperationException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: The ViewData item that has the key 'Origin' is of type 'System.Collections.Generic.List`1[[System.Web.WebPages.Html.SelectListItem, System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]' but must be of type 'IEnumerable<SelectListItem>'.
Works fine when hard coded on action only.
Am i forgetting any type conversion??
Worked When I replaced using System.Web.WebPages.Html;
with using System.Web.Mvc.
I do not know the technical difference.. but if you have same problem you can try my solution...
It would be great if someone could comment with an technical difference...
Another takeaway when i included both reference.
Error 1 'SelectListItem' is an ambiguous reference between 'System.Web.WebPages.Html.SelectListItem' and 'System.Web.Mvc.SelectListItem'
Theres an error there, the case from origin. ViewBag uses dynamic types, then this will not throw any error in compilation time, but in runtime if something is wrong.
ViewBag.origin = repository.GetOriginList();
But you calling the DropDownList
#Html.DropDownList("Origin", "Select Origin")
Its case sensitive, you should change
ViewBag.origin to ViewBag.Origin
or call it:
#Html.DropDownList("origin ", "Select Origin")
EDIT:
Change the Repository function too:
public IEnumerable<SelectListItem> GetOriginList()
Related
I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);
The project:
ASP.NET 4.5.2
MVC 5
I seem to be utterly unable to add a first entry to a selectlist in a conditional manner.
Below is my code:
public SelectList getStatus(Guid company) {
var statusValue = db.Company.Where(x => x.CompanyId.Equals(company)).Select(x => new { CompanyStatusId = x.CompanyStatusId }).SingleOrDefault();
var statusList = db.CompanyStatus.Select(x => new { CompanyStatusId = x.CompanyStatusId, CompanyStatusName = x.CompanyStatusName }).ToList();
var status = new SelectList(statusList, "CompanyStatusId", "CompanyStatusName", statusValue);
if(statusValue.CompanyStatusId.Equals(Guid.Empty) || statusValue.CompanyStatusId.Equals(null)) {
var insertItem = new SelectListItem { Value = "", Text = " « ‹ Select a Status › » " };
status.ToList().Add(insertItem);
}
return status;
}
Walkthrough via line number, within the method:
Get CompanyStatusId from the Company table. This is a foreign key to
a lookup table.
Get the lookup table that provides the foreign key.
IF statement: if the CompanyStatusId is null or blank or is an empty
Guid, add an initial "choose status" entry with a blank value.
The reason why I want this is so that any company that already has a status will not get the "choose status" entry, but any company that doesn't does get the entry. However, what I have above DOES NOT WORK. I have confirmed via breakpoints that a null value for the Guid does indeed trigger the If statement (so that works at least) and that the contents of the IF statement do get processed, but the resulting drop-down menu does not have the "choose status" entry.
This is all sitting in a BaseController since it needs to be accessible from a large swath of internal pages. The drop-down list on the front end is populated by a ViewBag from this BaseController.
Help?
status.ToList().Add(insertItem); is first generating a new List<SelectListItem> and adding insertItem to it. It is not altering the original SelectList. To make this work, change the method to
public List<SelectListItem> getStatus(Guid company) // change signature
{
var statusValue = db.Company.Where(x => x.CompanyId.Equals(company)).Select(x => new { CompanyStatusId = x.CompanyStatusId }).SingleOrDefault();
var statusList = db.CompanyStatus.Select(x => new { CompanyStatusId = x.CompanyStatusId, CompanyStatusName = x.CompanyStatusName }).ToList();
var status = new SelectList(statusList, "CompanyStatusId", "CompanyStatusName", statusValue).ToList(); // change
if(statusValue.CompanyStatusId.Equals(Guid.Empty) || statusValue.CompanyStatusId.Equals(null))
{
var insertItem = new SelectListItem { Value = "", Text = " « ‹ Select a Status › » " };
status.Add(insertItem); // change
}
return status;
}
Side note: typically the "Select a Status" option would be the first option, in which case you would need to use status.Insert(0, insertItem);
Note also that there is no point adding the last parameter in the SelectList constructor (i.e. statusValue) if your binding to a model property (its ignored by the DropDownListFor() method.
I have a data source that is handing me an IEnumerable<SelectListItem> The text in each selectListItem is all uppercase. I would like to find the easiest way to change them to proper case without actually changing the datasource.
I think the best answer might be to convert your Enumerable to SelectListItems before passing to your view, and converting the case to TitleCase then. Some faux code for you:
Given this DataSource:
EnumerableItems = new List<string>() { "ITEM ONE", "ITEM TWO" };
I have this on my ViewModel:
public string BoundValue { get; set; }
public IEnumerable<SelectListItem> SelectListItems { get; set; }
I set the SelectListItems like so:
viewModel.SelectListItems = from e in EnumerableItems
select new SelectListItem
{
Selected = e == dto.BoundValue,
Text = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(e.ToLower()),
Value = e
};
In my view something like:
#Html.DropDownList("BoundValue", new SelectList(Model.SelectListItems, "Value", "Text"), "-- select --")
And I get this:
Theoretically I think you could also change the case in the view by calling the ToTileCase where the "Text" argument is, but that would be less clean I think.
EDIT: I amended the code for creating the SelectListItem to have the Value remain uppercase (as e, instead of e.ToTitleCase()) - since I guess it will bind to your original data source ultimately :)
Okay, in an effort to provide a little direction. If you own the class that is each list item you do have an option. Override the ToString() method. Like this maybe:
public override string ToString()
{
// here is a really primitive algorithm
return string.Format("{0}{1}",
this.DisplayProperty.Take(1),
this.DisplayProperty.Substring(1).ToLower());
}
However, if you don't own the class that is each list item you're pretty stuck because you don't want to modify the data source.
Problem
Need to convert int to string using EF4 + SQL CE4. The recommended option of using SqlFunctions.StringConvert(double) still gives me errors.
Option 1 (original). This gives me error:
public static IEnumerable<SelectListItem> xxGetCustomerList()
{
using (DatabaseEntities db = new DatabaseEntities())
{
var list = from l in db.Customers
orderby l.CompanyName
select new SelectListItem { Value = l.CustomerID.ToString(), Text = l.CompanyName };
return list.ToList();
}
}
Option 2 (most suggested). Then as many posts suggests, I used the SqlFunctions.StringConvert() function from the library System.Data.Objects.SqlClient:
public static IEnumerable<SelectListItem> GetCustomerList()
{
using (DatabaseEntities db = new DatabaseEntities())
{
var list = from l in db.Customers
orderby l.CompanyName
select new SelectListItem { Value = SqlFunctions.StringConvert((double)l.CustomerID), Text = l.CompanyName };
return list.ToList();
}
}
Which now shows below error:
The specified method 'System.String StringConvert(System.Nullable`1[System.Double])' on the type 'System.Data.Objects.SqlClient.SqlFunctions' cannot be translated into a LINQ to Entities store expression.
Option 3 (for very specific case). Then anoter post shows a smart solution using Dictionary, which finally works:
public static IEnumerable<SelectListItem> xGetCustomerList()
{
using (DatabaseEntities db = new DatabaseEntities())
{
var customers = db.Customers.ToDictionary(k => k.CustomerID, k => k.CompanyName);
var list = from l in customers
orderby l.Value
select new SelectListItem { Value = l.Key.ToString(), Text = l.Value };
return list.ToList();
}
}
But only work for simple pair values (key, value). Can someone help me with another solution or what I'm doing wrong with option 2?
And I hope Microsoft will soon make EF right before pushing us to move from L2S which is already stable and much more mature. I actually using EF4 just because want to use SQL CE, otherwise I stay with L2S.
EF is database independent at upper layers but the part dealing with conversion of linq query to SQL is always database dependent and SqlFunctions are dependent on SQL Server Provider but you are using SQL Server CE provider which is not able to translate functions from SqlFunctions class.
Btw. third option is also not a solution because it will select whole customer table to memory and use linq-to-objects after that. You should use this:
public static IEnumerable<SelectListItem> xxGetCustomerList()
{
using (DatabaseEntities db = new DatabaseEntities())
{
// Linq to entities query
var query = from l in db.Customers
orderby l.CompanyName
select new { l.CustomerID, l.CompanyName };
// Result of linq to entities transformed by linq to objects
return query.AsEnumerable()
.Select(x => new SelectListItem
{
Value = x.CustomerID.ToString(),
Test = x.CompanyName
}).ToList();
}
}
Here is a simplified version I'm using now (specific for SQL CE):
public static IEnumerable<SelectListItem> GetBlogCategoryList()
{
using (SiteDataContext db = new SiteDataContext())
{
var list = from l in db.BlogCategories.AsEnumerable()
orderby l.CategoryName
select new SelectListItem { Value = l.CategoryID.ToString(), Text = l.CategoryName };
return list.ToList();
}
}
Note the db.BlogCategories.AsEnumerable() part
I have an enum for one of the properties of my view-model. I want to display a drop-down list that contains all the values of the enum. I can get this to work with the following code.
What I'm wondering is whether there is a simple way to convert from an enum to an IEnumerable? I can do it manually as in the following example, but when I add a new enum value the code breaks. I imagine that I can do it via reflection as per this example, but but are there other ways to do this?
public enum Currencies
{
CAD, USD, EUR
}
public ViewModel
{
[Required]
public Currencies SelectedCurrency {get; set;}
public SelectList Currencies
{
List<Currencies> c = new List<Currencies>();
c.Add(Currencies.CAD);
c.Add(Currencies.USD);
c.Add(Currencies.EUR);
return new SelectList(c);
}
}
I'm using a helper that i found here to populate my SelectLists with a generic enum type, i did a little modification to add the selected value though, here's how it looks like :
public static SelectList ToSelectList<T>(this T enumeration, string selected)
{
var source = Enum.GetValues(typeof(T));
var items = new Dictionary<object, string>();
var displayAttributeType = typeof(DisplayAttribute);
foreach (var value in source)
{
FieldInfo field = value.GetType().GetField(value.ToString());
DisplayAttribute attrs = (DisplayAttribute)field.
GetCustomAttributes(displayAttributeType, false).FirstOrDefault()
items.Add(value, attrs != null ? attrs.GetName() : value.ToString());
}
return new SelectList(items, "Key", "Value", selected);
}
The nice thing about it is that it reads the DisplayAttribute as the title rather than the enum name. (if your enums contain spaces or you need localization then it makes your life much easier)
So you will need to add the Display attirubete to your enums like this :
public enum User_Status
{
[Display(Name = "Waiting Activation")]
Pending, // User Account Is Pending. Can Login / Can't participate
[Display(Name = "Activated" )]
Active, // User Account Is Active. Can Logon
[Display(Name = "Disabled" )]
Disabled, // User Account Is Diabled. Can't Login
}
and this is how you use them in your views.
<%: Html.DropDownList("ChangeStatus" , ListExtensions.ToSelectList(Model.statusType, user.Status))%>
Model.statusType is just an enum object of type User_Status.
That's it , no more SelectLists in your ViewModels. In my example I'm refrencing an enum in my ViewModel but you can Refrence the enum type directly in your view though. I'm just doing it to make everything clean and nice.
Hope that was helpful.
Look at Enum.GetNames(typeof(Currencies))
I am very late on this one but I just found a really cool way to do this with one line of code, if you are happy to add the Unconstrained Melody NuGet package (a nice, small library from Jon Skeet).
This solution is better because:
It ensures (with generic type constraints) that the value really is an enum value (due to Unconstrained Melody)
It avoids unnecessary boxing (due to Unconstrained Melody)
It caches all the descriptions to avoid using reflection on every call (due to Unconstrained Melody)
It is less code than the other solutions!
So, here are the steps to get this working:
In Package Manager Console, "Install-Package UnconstrainedMelody"
Add a property on your model like so:
//Replace "YourEnum" with the type of your enum
public IEnumerable<SelectListItem> AllItems
{
get
{
return Enums.GetValues<YourEnum>().Select(enumValue => new SelectListItem { Value = enumValue.ToString(), Text = enumValue.GetDescription() });
}
}
Now that you have the List of SelectListItem exposed on your model, you can use the #Html.DropDownList or #Html.DropDownListFor using this property as the source.
So many good answers - I thought I'sd add my solution - I am building the SelectList in the view (and not in the Controller):
In my c#:
namespace ControlChart.Models
//My Enum
public enum FilterType {
[Display(Name = "Reportable")]
Reportable = 0,
[Display(Name = "Non-Reportable")]
NonReportable,
[Display(Name = "All")]
All };
//My model:
public class ChartModel {
[DisplayName("Filter")]
public FilterType Filter { get; set; }
}
In my cshtml:
#using System.ComponentModel.DataAnnotations
#using ControlChart.Models
#model ChartMode
#*..........*#
#Html.DropDownListFor(x => x.Filter,
from v in (ControlChart.Models.FilterType[])(Enum.GetValues(typeof(ControlChart.Models.FilterType)))
select new SelectListItem() {
Text = ((DisplayAttribute)(typeof(FilterType).GetField(v.ToString()).GetCustomAttributes(typeof(DisplayAttribute), false).First())).Name,
Value = v.ToString(),
Selected = v == Model.Filter
})
HTH
Maybe is too late, but i think it could be useful for people with the same problem.
I've found here that now with MVC 5 it's included an EnumDropDownListFor html helper that makes for no longer necesary the use of custom helpers or other workarounds.
In this particular case, just add this:
#Html.EnumDropDownListFor(x => x.SelectedCurrency)
and that's all!
You can also translate or change the displayed text via data annotations and resources files:
Add the following data annotations to your enum:
public enum Currencies
{
[Display(Name="Currencies_CAD", ResourceType=typeof(Resources.Enums)]
CAD,
[Display(Name="Currencies_USD", ResourceType=typeof(Resources.Enums)]
USD,
[Display(Name="Currencies_EUR", ResourceType=typeof(Resources.Enums)]
EUR
}
Create the corresponding resources file.