c# .net asp.net data-binding listview

c# - Diccionario<T> de List<T> y ListViews en ASP.NET



data-binding (2)

Preámbulo

Estoy haciendo esta pregunta porque, aunque he leído muchos recursos de ListView, todavía no los estoy obteniendo.

Fondo

Tengo un montón de Foo ''s que tienen una lista de elementos asociados ( conocidos como Bar ), y los estoy extrayendo de la capa de Acceso a datos / Lógica empresarial como Diccionario que contiene un Foo y sus Bars asociadas. Me gustaría escupir estos elementos en la página web en un ListView que contenga Foo.Name a la izquierda y la List<Bar> a la derecha en una lista desplegable. (Se muestra con mi hermoso arte ASCII a continuación):

Vista de la lista

------------------------------------------------------------------ | Name Of Item | DropDownList (of List<T>) | |---------------------------------| _____________________ | | foo1 | | bar1 | v | | | | |_______________|___| | ------------------------------------------------------------------ | | DropDownList (of List<T>) | | | _____________________ | | foo2 | | bar2 | v | | | | |_______________|___| | ------------------------------------------------------------------

Bien, esto es lo que está pasando. Esto es un ListView; Los elementos se extraen de una base de datos en un Dictionary<Foo, List<Bar>> . Estoy tratando de obtener el valor clave del diccionario para que se muestre en ''Nombre del elemento'', y estoy tratando de que la `Lista <T> Barra ''se muestre como una DropDownList en el lado derecho de ListView.

Diagramas de clase

----------------- ----------------- | Foo | | Bar | ----------------- ----------------- | Id | | ItemName | | Name | | ItemValue | | BarID | | | ----------------- -----------------

Así que, para recapitular, quiero colocar Dictionary.Key "Name" en el lado izquierdo de ListView, y Dictionary.Value (que resulta ser una lista) en una DropdownList en el lado derecho.

De modo que, para cada par de clave / valor, habría un nombre y una lista desplegable que albergaría el valor de cada clave.

Pero tengo problemas (obviamente) y espero que alguien me diga lo que estoy haciendo mal.

Código detrás:

protected Dictionary<Foo, List<Bar>> FooDictionary { get; set; } protected void DataBindFooList(List<int> SelectedFoos) { System.Web.UI.WebControls.ListView lv = lvFooList; try { Dictionary<Foo, List<Bar>> fooDictionary = new Dictionary<Foo, List<Bar>>(); foreach (int Foo in SelectedFoos) { // Build List of Foos to add as Dictionary.Keys fooDictionary.Add(fooScalar, Bar) } FooDictionary = fooDictionary; lv.DataSource = FooDictionary; lv.DataBind(); ddlListOfBars.DataSource = FooDictionary; ddlListOfBars.DataValueField = "ItemValue"; ddlListOfBars.DataTextField = "ItemName"; ddlListOfBars.DataBind(); } catch (Exception ex) { SetMessage(divFooMsg, "Unable to retrieve Foos: " + ex.Message, true, true); }

El código ListView:

<asp:ListView ID="lvFooList" runat="server"> <LayoutTemplate> <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder> </LayoutTemplate> <ItemSeparatorTemplate> <hr /> </ItemSeparatorTemplate> <ItemTemplate> <%#Eval("Name") %> <asp:DropDownList ID="ddlListOfBars" runat="server"></asp:DropDownList> </ItemTemplate> </asp:ListView>

Las preguntas):

  1. ¿Es posible usar un diccionario de esta manera?
  2. ¿Alguna sugerencia sobre dónde me estoy equivocando? (Recursos, sugerencias, etc. ayudarían)
  3. ¿Hay una mejor manera de hacer esto?
  4. Si esta pregunta es clara como barro, por favor coméntala para que pueda descubrir cómo mejorarla.

Es algo así como lo que quieres:

<asp:ListView ID="lvFooList" runat="server"> <LayoutTemplate> <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder> </LayoutTemplate> <ItemSeparatorTemplate> <hr /> </ItemSeparatorTemplate> <ItemTemplate> <asp:Label ID="lbName" runat="server"/> <asp:DropDownList ID="ddlListOfBars" runat="server"></asp:DropDownList> </ItemTemplate> </asp:ListView>

Luego escribí una prueba desagradable muy rápida

public class Foo { public string Name; } public class Bar { public string ItemName { get; set; } public string ItemValue { get; set; } } protected void Page_Load(object sender, EventArgs e) { var fooKey1 = new Foo() {Name = "foo1"}; var barList1 = new List<Bar>() { new Bar() {ItemName = "bar1", ItemValue = "barV1"}, new Bar() {ItemName = "bar2", ItemValue = "barV2"} }; var fooKey2 = new Foo() {Name = "foo2"}; var barList2 = new List<Bar>() { new Bar() {ItemName = "bar3", ItemValue = "barV3"}, new Bar() {ItemName = "bar4", ItemValue = "barV4"} }; var dicFooBar = new Dictionary<Foo, List<Bar>>() {{fooKey1, barList1}, {fooKey2, barList2}}; lvFooList.ItemDataBound += lvFooList_ItemDataBound; lvFooList.DataSource = dicFooBar; lvFooList.DataBind(); } void lvFooList_ItemDataBound(object sender, ListViewItemEventArgs e) { var dataItem = (ListViewDataItem)e.Item; var fooBarList = (KeyValuePair<Foo, List<Bar>>)dataItem.DataItem; ((Label) dataItem.FindControl("lbName")).Text = fooBarList.Key.Name; var ddlListOfBars = (DropDownList) dataItem.FindControl("ddlListOfBars"); ddlListOfBars.DataTextField = "ItemName"; ddlListOfBars.DataValueField = "ItemValue"; ddlListOfBars.DataSource = fooBarList.Value; ddlListOfBars.DataBind(); }

Parece hacer lo que quieras, pero mi código es solo un código de prueba rápido, así que ten cuidado. Sin embargo, se procesó como se esperaba.


Su problema surge porque no tiene sentido enlazar con datos ddlListOfBars en DataBindFooList (), porque no hay una sola DropDownList con la que enlazar datos. Cuando llama a lv.DataBind (), ListView crea una copia de su ItemTemplate para cada Foo, cada uno contiene un ddlListOfBars. Debe decirle que vincule cada DropDownList con la Lista <Barra> correcta a medida que avanza. Puede hacer esto configurando el origen de datos de ddlListOfBars con expresiones de enlace de datos en lugar de en el código subyacente:

<ItemTemplate> <%#Eval("Key.Name") %> <asp:DropDownList ID="ddlListOfBars" runat="server" DataSource=''<%#Eval("Value")%>'' DataValueField="ItemValue" DataTextField="ItemName" /> </ItemTemplate>

Tenga en cuenta que también necesita usar "Key.Name" para obtener la propiedad de nombre de su Foo. Los elementos de diccionario individuales a los que se está vinculando son KeyValuePair <Foo, List <Bar >>, que tiene una propiedad Key y una propiedad Value para acceder al Foo y List <Bar>, respectivamente.

Editar
Si tiene mucho en juego en su ItemTemplate, puede ser útil mover el contenido a un control de usuario. El control de usuario puede tener una propiedad fuertemente tipada para acceder al DataItem y tiene un acceso fuertemente tipado a sus controles secundarios. Esto le brinda la flexibilidad del evento ItemDataBound sin todo el ruido de fundición y FindControl (). Dudo que me moleste en este caso, pero sería algo así como

<asp:ListView ID="lvFooList" runat="server"> <LayoutTemplate> <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder> </LayoutTemplate> <ItemSeparatorTemplate> <hr /> </ItemSeparatorTemplate> <ItemTemplate> <uc:ListViewContents DataItem=''<%# Container.DataItem %>'' /> </ItemTemplate>

ListViewContents.ascx ...

<asp:Label ID="lbName" runat="server"/> <asp:DropDownList ID="ddlListOfBars" runat="server"></asp:DropDownList>

ListViewContents.ascx.cs ...

public KeyValuePair<Foo,List<Bar>> DataItem { get; set; } protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); lbName.Text = DataItem.Key.Name; ddlListOfBars.DataTextField = "ItemName"; ddlListOfBars.DataValueField = "ItemValue"; ddlListOfBars.DataSource = DataItem.Value; ddlListOfBars.DataBind(); }