with wingtip visual tutorial toys studio started net mvc getting formulario form asp and asp.net webforms model-binding

wingtip - Enlace de modelo de ASP.NET Web Forms 4.5 donde el modelo contiene una colección



getting started with asp net 4.5 web forms and visual studio 2013 wingtip toys c#) (5)

Intento actualizar una aplicación antigua de formularios web para usar las nuevas funciones de vinculación del modelo agregadas en 4.5, similar a las funciones de enlace de MVC.

Tengo problemas para crear un FormView editable que presenta un único modelo que contiene miembros simples más un miembro que es una colección de otros modelos. Necesito que el usuario pueda editar las propiedades simples del objeto principal y las propiedades de la colección hija.

El problema es que la colección secundaria ( ProductChoice.Extras ) siempre es nula después del enlace del modelo cuando el código intenta actualizar el modelo.

Aquí están mis modelos:

[Serializable] public class ProductChoice { public ProductChoice() { Extras = new List<ProductChoiceExtra>(); } public int Quantity { get; set; } public int ProductId { get; set; } public List<ProductChoiceExtra> Extras { get; set; } } [Serializable] public class ProductChoiceExtra { public int ExtraProductId { get; set; } public string ExtraName { get; set; } public int ExtraQuantity { get; set; } }

Y mi código de control de usuario detrás:

public partial class ProductDetails : System.Web.UI.UserControl { private Models.ProductChoice _productChoice; protected void Page_Load(object sender, EventArgs e) { _productChoice = new Models.ProductChoice() { Quantity = 1, ProductId = 1 }; _productChoice.Extras.Add(new Models.ProductChoiceExtra() { ExtraProductId = 101, ExtraName = "coke", ExtraQuantity = 1 }); _productChoice.Extras.Add(new Models.ProductChoiceExtra() { ExtraProductId = 104, ExtraName = "sprite", ExtraQuantity = 2 }); } public Models.ProductChoice GetProduct() { return _productChoice; } public void UpdateProduct(Models.ProductChoice model) { /* model.Extras is always null here, it should contain two ProductChoiceExtra objects */ if (TryUpdateModel(_productChoice) == true) { } } }

Mi marcado de control:

<div id="selectOptions"> <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" ItemType="Models.ProductChoice" SelectMethod="GetProduct" UpdateMethod="UpdateProduct" > <EditItemTemplate> <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> <asp:Repeater ID="Extras" ItemType="Models.ProductChoiceExtra" DataSource="<%# BindItem.Extras %>" runat="server"> <ItemTemplate> <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="Quantity" runat="server" /> </ItemTemplate> </asp:Repeater> </EditItemTemplate> </asp:FormView> </div>

He intentado hacer que la propiedad Extras sea ​​una List BindingList lugar de una List pero no ha BindingList nada, la colección Extras no está vinculada en el método UpdateProduct .


Al profundizar en System.Web.ModelBinding, se revela que CollectionModelBinder espera que los valores que se pasan al ProvideValueProvider estén en el mismo formato que para MVC, es decir: MyCollection [i]

public static string CreateIndexModelName(string parentName, string index) { if (parentName.Length != 0) { return (parentName + "[" + index + "]"); } return ("[" + index + "]"); }

Lamentablemente, los nombres de los elementos de su repetidor no coincidirán con ese criterio.

Aunque ciertamente no es ortodoxo, aún puede lograr esto escribiendo cajas de texto que no sean del servidor , y luego dándoles un nombre comenzando con su contenedor de nombres datalistico, seguido por el índice. Y gracias a "Request.Unvalidated" (también introducido en 4.5), tiene la capacidad de vincularse a estos datos a pesar de que no están representados por los controles del lado del servidor.


Debe especificar una Plantilla de Editar Elemento en un datalista interno ya que las Propiedades en la Plantilla de Artículo no se pueden devolver en el modelo que se construye automáticamente y se pasa al método de Actualización. No he tenido tiempo de probarlo pero debería funcionar ...

<div id="selectOptions"> <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" ItemType="Models.ProductChoice" SelectMethod="GetProduct" UpdateMethod="UpdateProduct" > <EditItemTemplate> <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> <asp:DataList ID="Extras" DataSource="<%# DataBinder.Eval(Container.DataItem, "Extras") %>" runat="server"> <EditItemTemplate> <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="TextBox1" runat="server" /> </EditItemTemplate> <ItemTemplate> <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="Quantity" runat="server" /> </ItemTemplate> </asp:Repeater> </EditItemTemplate> </asp:FormView> </div>


Lamentablemente, no sé exactamente cómo se hace esto con Web Forms, así que no estoy seguro de cómo reproducir esto con un repetidor, pero en MVC, el modelo de encuadernación requiere un índice para reconstruir la lista. Si tuviera que adivinar cómo se hace esto en formularios web, sería algo similar a esto:

<div id="selectOptions"> <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" ItemType="Models.ProductChoice" SelectMethod="GetProduct" UpdateMethod="UpdateProduct" > <EditItemTemplate> <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> <% for (int i = 0; i < BindItem.Extras.Count; i++) { %> <asp:HiddenField Value="<%# BindItem.Extras[i].ExtraProductId %>" ID="ExtraProductId" runat="server" /> <asp:Label Text="<%# BindItem.Extras[i].ExtraName %>" ID="Name" runat="server" /> <asp:TextBox Text="<%# BindItem.Extras[i].ExtraQuantity %>" ID="Quantity" runat="server" /> <% } %> </EditItemTemplate> </asp:FormView> </div>

Observe que reemplacé el repetidor con un ciclo for que itera a través de la colección con el índice utilizado para acceder a cada extra. Esto es similar a cómo debo hacer lo que desea en ASP.NET MVC. El índice se publica junto con el resto del formulario web cuando se envía el formulario, lo que permite que el archivador del modelo reconstruya la lista ordenada de objetos.

Espero que esto sea un poco de ayuda y perdónenme por cualquier error, ya que no tengo un proyecto de formularios web para probar esto en este momento.


Pase los valores extras en la cadena de consulta, agregue valores de parámetros en el método de actualización. Use los atributos de datos en HTML 5 para almacenar esos 3 valores en el elemento Vista de formulario

ejemplo

UpdateMethod="UpdateProduct/104/coke/2"

o

UpdateMethod="UpdateProduct/?ExtraProductId=104&ExtraName=coke&ExtraQuantity=2"

para la primera aproximación, debe escribir la regla de enrutamiento en Route Config.

Dentro del control del usuario como se muestra a continuación

public void UpdateProduct(Models.ProductChoice model, int ExtraProductId, string ExtraName, int ExtraQuantity) { /* model.Extras is always null here, it should contain two ProductChoiceExtra objects */ if (TryUpdateModel(_productChoice) == true) { model.Extras.Add(new Models.ProductChoiceExtra() { ExtraProductId = ExtraProductId, ExtraName = ExtraName, ExtraQuantity = ExtraQuantity }); } }


[Serializable] public class ProductChoice { public int Quantity { get; set; } public int ProductId { get; set; } public ProductChoiceExtra Extras { get; set; } } [Serializable] public class ProductChoiceExtra { public int ExtraProductId { get; set; } public string ExtraName { get; set; } public int ExtraQuantity { get; set; } public List<ProductChoiceExtra> listProducts{get;set;} }