Подтвердить что ты не робот

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

Преамбула

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

Фон

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

ListView

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

Итак, чтобы повторить, я хочу поместить словаря. Name "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>

Вопрос (ы):

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

Ответ 1

Ваша проблема возникает, потому что не имеет смысла databind ddlListOfBars в DataBindFooList(), потому что для привязки данных не только один DropDownList. Когда вы вызываете lv.DataBind(), ListView создает копию вашего ItemTemplate для каждого Foo, каждый из которых содержит ddlListOfBars. Вы должны сказать ему, чтобы привязать каждый DropDownList к списку List <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> соответственно.

Edit
Если в вашем 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();   
}

Ответ 2

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

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

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