Словарь ‹T› списка ‹T› и ListViews в ASP.NET

Преамбула

Я задаю этот вопрос, потому что, хотя я прочитал много ресурсов ListView, я все еще не «понимаю».

Задний план

У меня есть несколько Foo, у которых есть список связанных с ними элементов (известный как Bar), и я беру их из уровня доступа к данным / бизнес-логики в виде словаря, который содержит Foo и его связанный Bars. Я хотел бы разместить эти элементы на веб-странице в ListView, который содержит Foo.Name слева и List<Bar> справа в раскрывающемся списке. (Показано с моим красивым изображением ASCII ниже):

Посмотреть список

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

Хорошо, вот что происходит. Это ListView; Элементы извлекаются из базы данных в Dictionary<Foo, List<Bar>>. Я пытаюсь получить ключевое значение из словаря, чтобы оно отображалось в разделе «Имя элемента», и я пытаюсь заставить панель «Список ‹T› Bar» отображаться как DropDownList в правой части ListView.

Диаграммы классов

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

Итак, чтобы резюмировать, я хочу разместить Dictionary.Key «Name» в левой части ListView, а Dictionary.Value (который оказывается списком) в DropdownList с правой стороны.

Таким образом, для каждой пары ключ / значение будет имя и раскрывающийся список, в котором будет размещено значение каждого ключа.

Но у меня возникают проблемы (очевидно), и я надеюсь, что кто-нибудь скажет мне, что я делаю не так.

Код позади:

  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);
            }

Код 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>

Вопросы):

  1. Можно ли таким образом использовать Словарь?
  2. Любые указатели на то, где я ошибаюсь? (Ресурсы, подсказки и т. Д. Помогут)
  3. Есть лучший способ сделать это?
  4. Если этот вопрос ясен как грязь, прокомментируйте, чтобы я мог понять, как его улучшить.

person George Stocker    schedule 24.02.2009    source источник
comment
+1 за очень хорошо написанный вопрос. Я едва мог найти что-нибудь, что можно было бы рубить и рубить!   -  person GEOCHET    schedule 25.02.2009
comment
Откуда появился FooDictionary и почему вы назначаете FooDictionary = fooDictionary; и lv.DataSource = FooDictionary ;?   -  person Manu    schedule 25.02.2009
comment
Ману: В этом заявлении foreach много вуду. По сути, он проходит и создает словарь из Foo и Bars, связанных с этим foo. Я просто для простоты показал конечный продукт. Разве источником данных не должен быть FooDictionary, чтобы он мог получить имя?   -  person George Stocker    schedule 25.02.2009


Ответы (2)


Проблема возникает из-за того, что привязка ddlListOfBars к данным в DataBindFooList () не имеет смысла, потому что привязка к данным не только одного DropDownList. Когда вы вызываете lv.DataBind (), ListView создает копию вашего ItemTemplate для каждого Foo, каждый из которых содержит ddlListOfBars. Вы должны указать ему привязать каждый DropDownList к нужному списку ‹Bar› по мере продвижения. Вы можете сделать это, установив источник данных ddlListOfBars с выражениями привязки данных, а не в коде позади:

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

Обратите внимание, что вам также необходимо использовать «Key.Name», чтобы перейти к свойству name вашего Foo. Отдельные элементы словаря, к которым вы привязываетесь, - это KeyValuePair ‹Foo, List ‹Bar››, у которого есть свойство Key и свойство Value для доступа к Foo и List ‹Bar› соответственно.

Изменить
Если в вашем ItemTemplate много чего происходит, может быть полезно переместить содержимое в пользовательский элемент управления. Пользовательский элемент управления может иметь строго типизированное свойство для доступа к DataItem и строго типизированный доступ к своим дочерним элементам управления. Это дает вам гибкость события ItemDataBound без всякого шума приведения типов и FindControl (). Я сомневаюсь, что возьмусь за дело в этом случае, но это будет примерно так:

<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();   
}
person stevemegson    schedule 24.02.2009

Что-то вроде этого, что вы хотите:

   <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>

Затем я написал очень быстрый неприятный тест

    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();
    }

Кажется, вы делаете то, что хотите, но мой код - это просто код быстрого тестирования, так что будьте осторожны. Тем не менее, он отрисовался так, как ожидалось.

person Andrew Barrett    schedule 24.02.2009