Ho trascorso la maggior parte della settimana scorsa a ginocchia profonde nella nuova funzionalità di template inserita in MVC2. Ho avuto difficoltà a provare a far funzionare un modello DropDownList. Il problema più grande che ho lavorato per risolvere è come ottenere i dati di origine per l'elenco a discesa sul modello. Ho visto molti esempi in cui è possibile inserire i dati di origine nel dizionario ViewData (ViewData ["DropDownSourceValuesKey"]) quindi recuperarli nel modello stesso (var sourceValues = ViewData ["DropDownSourceValuesKey"];) Funziona, ma l'ho fatto non come avere una stringa sciocca come il perno linciaggio per fare questo lavoro.MVC2 EditorTemplate for DropDownList
Di seguito un approccio che è venuta in mente e voleva ottenere pareri su questo approccio:
qui sono i miei obiettivi di progettazione:
- Il modello di vista dovrebbe contenere i dati di origine per la caduta lista
- Limite stupide Strings giù
- Non utilizzare il dizionario Viewdata
- controller è responsabile per il riempimento della proprietà con i dati di origine per la discesa Lista
Ecco la mia vista Modello:
public class CustomerViewModel
{
[ScaffoldColumn(false)]
public String CustomerCode{ get; set; }
[UIHint("DropDownList")]
[DropDownList(DropDownListTargetProperty = "CustomerCode"]
[DisplayName("Customer Code")]
public IEnumerable<SelectListItem> CustomerCodeList { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String PhoneNumber { get; set; }
public String Address1 { get; set; }
public String Address2 { get; set; }
public String City { get; set; }
public String State { get; set; }
public String Zip { get; set; }
}
My View modello ha una proprietà CustomerCode, che è un valore che l'utente seleziona da un elenco di valori. Ho una proprietà CustomerCodeList che è un elenco di possibili valori CustomerCode ed è la fonte per un elenco a discesa. Ho creato un attributo DropDownList con DropDownListTargetProperty. DropDownListTargetProperty punta alla proprietà che verrà popolata in base alla selezione dell'utente dal menu a discesa generato (in questo caso, la proprietà CustomerCode).
Avviso che la proprietà CustomerCode ha [ScaffoldColumn (false)] che forza il generatore a saltare il campo nell'output generato.
Il file DropDownList.ascx genererà un elemento del modulo elenco a discesa con i dati di origine dalla proprietà CustomerCodeList. L'elenco a discesa generato utilizzerà il valore di DropDownListTargetProperty dall'attributo DropDownList come Id e gli attributi Name dell'elemento Select form. Quindi il codice generato sarà simile a questa:
<select id="CustomerCode" name="CustomerCode">
<option>...
</select>
Questo funziona alla grande, perché quando il modulo viene inviato, MVC popolerà la proprietà di destinazione con il valore selezionato dal menu a tendina in quanto il nome della lista a discesa generato IS la proprietà di destinazione. Io lo visualizzo in quanto la proprietà CustomerCodeList è un'estensione della specie della proprietà CustomerCode. Ho accoppiato i dati di origine alla proprietà.
Ecco il mio codice per il controllore:
public ActionResult Create()
{
//retrieve CustomerCodes from a datasource of your choosing
List<CustomerCode> customerCodeList = modelService.GetCustomerCodeList();
CustomerViewModel viewModel= new CustomerViewModel();
viewModel.CustomerCodeList = customerCodeList.Select(s => new SelectListItem() { Text = s.CustomerCode, Value = s.CustomerCode, Selected = (s.CustomerCode == viewModel.CustomerCode) }).AsEnumerable();
return View(viewModel);
}
Ecco il mio codice per la DropDownListAttribute:
namespace AutoForm.Attributes
{
public class DropDownListAttribute : Attribute
{
public String DropDownListTargetProperty { get; set; }
}
}
Ecco il mio codice per il modello (DropDownList.ascx):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<SelectListItem>>" %>
<%@ Import Namespace="AutoForm.Attributes"%>
<script runat="server">
DropDownListAttribute GetDropDownListAttribute()
{
var dropDownListAttribute = new DropDownListAttribute();
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("DropDownListAttribute"))
{
dropDownListAttribute = (DropDownListAttribute)ViewData.ModelMetadata.AdditionalValues["DropDownListAttribute"];
}
return dropDownListAttribute;
}
</script>
<% DropDownListAttribute attribute = GetDropDownListAttribute();%>
<select id="<%= attribute.DropDownListTargetProperty %>" name="<%= attribute.DropDownListTargetProperty %>">
<% foreach(SelectListItem item in ViewData.Model)
{%>
<% if (item.Selected == true) {%>
<option value="<%= item.Value %>" selected="true"><%= item.Text %></option>
<% } %>
<% else {%>
<option value="<%= item.Value %>"><%= item.Text %></option>
<% } %>
<% } %>
</select>
Ho provato ad utilizzare l'helper Html.DropDownList, ma non mi avrebbe permesso di modificare l'ID e il nome attributi dell'elemento Select generato.
NOTA: è necessario eseguire l'override del metodo CreateMetadata di DataAnnotationsModelMetadataProvider per DropDownListAttribute. Ecco il codice per questo:
public class MetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var additionalValues = attributes.OfType<DropDownListAttribute>().FirstOrDefault();
if (additionalValues != null)
{
metadata.AdditionalValues.Add("DropDownListAttribute", additionalValues);
}
return metadata;
}
}
poi si deve effettuare una chiamata alla nuova MetadataProvider in Application_Start di Global.asax.cs:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new MetadataProvider();
}
Beh, spero che questo ha un senso e spero questo approccio potrebbe farti risparmiare tempo. Vorrei un feedback su questo approccio per favore. C'è un approccio migliore?
La prima cosa che viene in mente è quello di creare un modello personalizzato per editor di ServicePackageWithOwnerName. Il modello personalizzato ServicePackageWithOwnerName emetterebbe un dropdownbox per la proprietà OwnerName. In MaintainServicePackageViewModel, si adornerebbe CurrentServicePackage con UIHint ("[Nome tempalte nome server ServicePackageWithOwner]"). Sto pensando di esplorare questo concetto, ma non ho ancora avuto il tempo di farlo. – tschreck
Jack- Controlla questo link, potrebbe essere quello che ti serve: http://dotnetslackers.com/articles/aspnet/asp-net-mvc-2-0-templating.aspx – tschreck
Inoltre, controlla questo link. Brad Wilson ha creato una serie molto buona sul fare modelli. Questo collegamento riguarda i tipi complessi nidificati. http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html – tschreck