C# ASP.NET 中 List<T> 和 ListViews 的 Dictionary<T>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/583689/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:11:41  来源:igfitidea点击:

Dictionary<T> of List<T> and ListViews in ASP.NET

c#.netasp.netdata-bindinglistview

提问by George Stocker

Preamble

前言

I'm asking this question because even though I've read through a lot of ListView resources, I'm still not 'getting' it.

我问这个问题是因为即使我已经阅读了很多 ListView 资源,我仍然没有“理解”它。

Background

背景

I have a bunch of Foo's that have a list of items associated with them (known asBar), and I'm pulling them from the Data Access/Business Logic layer as Dictionary that holds a Fooand its associated Bars. I'd like to spit these items out in on the Webpage into a ListViewthat holds the Foo.Nameon the left, and the List<Bar>on the right in a dropdownlist. (Shown with my beautiful ASCII art below):

我有一堆Foo's ,它们有一个与它们相关联的项目列表(称为Bar),我将它们从数据访问/业务逻辑层中提取为包含 aFoo及其关联的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 |       |
|                                 |  |_______________|___|       |
------------------------------------------------------------------

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.

好的,这是怎么回事。这是一个列表视图;这些项目从数据库中提取到Dictionary<Foo, List<Bar>>. 我试图从字典中获取键值以显示在“项目名称”下,并试图让“List<T> Bar”在 ListView 的右侧显示为 DropDownList。

Class Diagrams

类图

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

所以回顾一下,我想将 Dictionary.Key“Name”放入 ListView 的左侧,将 Dictionary.Value(恰好是一个列表)放入右侧的 DropdownList。

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.

但是我遇到了问题(显然),希望有人能告诉我我做错了什么。

Code Behind:

背后的代码:

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

The ListView Code:

列表视图代码:

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


The Question(s):

问题:

  1. Is it possible to use a Dictionary in this way?
  2. Any pointers on where I'm going wrong? (Resources, hints, etc. would help)
  3. Is there a better way to do this?
  4. If this question is clear as mud, please comment so I can figure out how to improve it.
  1. 可以以这种方式使用字典吗?
  2. 关于我哪里出错的任何指示?(资源、提示等会有所帮助)
  3. 有一个更好的方法吗?
  4. 如果这个问题很清楚,请发表评论,以便我弄清楚如何改进它。

采纳答案by stevemegson

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:

出现您的问题是因为在 DataBindFooList() 中对 ddlListOfBars 进行数据绑定是没有意义的,因为要进行数据绑定的不仅仅是一个 DropDownList。当您调用 lv.DataBind() 时,ListView 会为每个 Foo 创建 ItemTemplate 的副本,每个副本都包含一个 ddlListOfBars。您需要告诉它在运行时将每个 DropDownList 绑定到正确的 List<Bar> 。您可以通过使用数据绑定表达式而不是在后面的代码中设置 ddlListOfBars 的数据源来做到这一点:

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

请注意,您还需要使用“Key.Name”来获取 Foo 的 name 属性。您绑定到的单个字典项是 KeyValuePair<Foo,List<Bar>>,它有一个 Key 属性和一个 Value 属性来分别访问 Foo 和 List<Bar>。

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

编辑
如果您在 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...

ListViewContents.ascx...

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

ListViewContents.ascx.cs...

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

回答by Andrew Barrett

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.

似乎做你想做的,但我的代码只是快速测试代码,所以请注意。不过,它确实按预期呈现。