I'm asking this question because even though I've read through a lot of ListView resources, I'm still not 'getting' it.
I have a bunch of Foo
's that have a list of items associated with them (known as Bar
), and I'm pulling them from the Data Access/Business Logic layer as Dictionary that holds a Foo
and its associated Bars
. I'd like to spit these items out in on the Webpage into a ListView
that holds the Foo.Name
on the left, and the List<Bar>
on the right in a dropdownlist. (Shown with my beautiful ASCII art below):
------------------------------------------------------------------ | Name Of Item | DropDownList (of List<T>) | |---------------------------------| _____________________ | | foo1 | | bar1 | v | | | | |_______________|___| | ------------------------------------------------------------------ | | DropDownList (of List<T>) | | | _____________________ | | foo2 | | bar2 | v | | | | |_______________|___| | ------------------------------------------------------------------
Alright, here's what's going on. This is a ListView; The items are pulled from a database into a Dictionary<Foo, List<Bar>>
. I'm trying to get the Key Value from the dictionary to show up under 'Name of Item', and am trying to get the `List<T> Bar' to show up as a DropDownList on the right side of the ListView.
----------------- ----------------- | Foo | | Bar | ----------------- ----------------- | Id | | ItemName | | Name | | ItemValue | | BarID | | | ----------------- -----------------
So to recap, I want to place the Dictionary.Key "Name" into the left side of the ListView, and the Dictionary.Value (which happens to be a list) into a DropdownList on the right side.
So that, for every Key/Value pair, there'd be a Name and a dropdown list that would house each Key's Value.
But I'm running into problems (obviously), and am hoping someone can tell me what I'm doing wrong.
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);
}
<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>
Your problem arises because it doesn't make sense to databind ddlListOfBars in DataBindFooList(), because there isn't just one DropDownList to databind. When you call lv.DataBind(), the ListView creates a copy of your ItemTemplate for each Foo, each containing a ddlListOfBars. You need to tell it to bind each DropDownList to the right List<Bar> as it goes. You can do this by setting the datasource of ddlListOfBars with data binding expressions rather than in the code behind:
<ItemTemplate>
<%#Eval("Key.Name") %>
<asp:DropDownList
ID="ddlListOfBars"
runat="server"
DataSource='<%#Eval("Value")%>'
DataValueField="ItemValue"
DataTextField="ItemName" />
</ItemTemplate>
Note that you also need to use "Key.Name" to get to the name property of your Foo. The individual dictionary items that you're binding to are KeyValuePair<Foo,List<Bar>>, which has a Key property and a Value property to access the Foo and List<Bar> respectively.
Edit
If you've got a lot going on in your ItemTemplate then it can be useful to move the contents out into a user control. The user control can have a strongly-typed property to access the DataItem, and has strongly-typed access to its child controls. This gets you the flexibility of the ItemDataBound event without all the the casting and FindControl() noise. I doubt I'd bother in this case, but it would go something like
<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();
}
Is something like this what you want:
<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>
Then I wrote a very quick nasty test
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();
}
Seems to do what you want, but my code is just quick test code, so be warned. It did render as expected though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With