【问题标题】:The difference between ListBox.DataSource collection versus ListBox.Items?ListBox.DataSource 集合与 ListBox.Items 的区别?
【发布时间】:2013-03-04 18:26:55
【问题描述】:

我正在动态创建 Winforms 多选 ListBox 并将其添加到流程面板控件中。我从我创建的对象绑定数据源,并验证 DataSource 实际上确实有大约 14 个元素。当我执行listBox.SetSelected(0, true) 时,会抛出System.ArgumentOutOfRangeException 错误。

我已经确定问题在于,虽然 DataSource 有 14 个元素,但 Item 集合没有 (0),因此引发了异常。我的问题是为什么这两个彼此不同,为什么我不简单地将数据源中的 foreach 项目添加到项目集合中?

以下是我目前的代码:

case InsertableItemParameter.ParameterType.ListBox:
    //note: two-way bindings are not possible with multiple-select listboxes
    Label lblListBox = new Label();
    lblListBox.Text = param.DisplayText;
    ListBox listBox = new ListBox();
    listBox.DataSource = param.Values;
    listBox.DisplayMember = "Value";
    listBox.SelectionMode = SelectionMode.MultiExtended;
    listBox.Size = new System.Drawing.Size(flowPanel.Size.Width - lblListBox.Size.Width - 10, 100);
    listBox.SetSelected(0, true);   //will throw argument out of range exception here!
    listBox.SetSelected(1, true);
    flowPanel.Controls.Add(lblListBox);
    flowPanel.Controls.Add(listBox);
    flowPanel.SetFlowBreak(listBox, true);
    break;

以下是我尝试和工作的替代解决方案,但为什么我要使用 DataSource 与 Items 集合?

case InsertableItemParameter.ParameterType.ListBox:
    //note: two-way bindings are not possible with multiple-select listboxes
    Label lblListBox = new Label();
    lblListBox.Text = param.DisplayText;
    ListBox listBox = new ListBox();
    //listBox.DataSource = param.Values;
    listBox.DisplayMember = "Value";
    listBox.SelectionMode = SelectionMode.MultiExtended;
    listBox.Size = new System.Drawing.Size(flowPanel.Size.Width - lblListBox.Size.Width - 10, 100);
    listBox.BeginUpdate();
    foreach (String paramater in param.Values)
    {
        listBox.Items.Add(paramater);
    }
    listBox.EndUpdate();
    listBox.SetSelected(0, true);
    listBox.SetSelected(1, true);
    flowPanel.Controls.Add(lblListBox);
    flowPanel.Controls.Add(listBox);
    flowPanel.SetFlowBreak(listBox, true);
    break;

回答:感谢所有回复。这里的问题是可见性和 win-form 渲染。虽然 DataSource 和 Items 集合之间的差异并没有真正解决(除了少数人),但我的问题的真正根源是通过在表单完成绘制后调用 SetSelected() 方法解决的。这在我的应用程序设计中引起了很多我必须解决的问题,但这就是问题所在。查看我标记为答案的回复。

【问题讨论】:

  • 根据我对您帖子的了解,ListBox.DataSource 您的数据来自 来自ListBox.Item 是您的数据已经有了。
  • @Brian 实际上,DataSource 是我拥有数据的地方,而 ListBox.Item 是完全空的。当我尝试使用简单的 listBox.Items.Add(paramater); 将我的 DataSource 添加到我的 Item 集合中时,我收到一条错误消息,指出在设置 DataSource 时我无法将项目添加到 Item 集合中。
  • 你能把所有的代码贴出来吗?
  • 检查下面的答案,我相信它会帮助你,问题是你的控件在 DataSource 设置时不可见,这是一个已知的issue。只需将控件添加到父控件(以便将其设置为可见),然后您就可以设置选定的项目。
  • @Brad:我的猜测是因为许多控件在设计时是未知的,并且可以从 0 到无穷大不等。

标签: c# winforms


【解决方案1】:

您的问题可能出在其他地方,因为这段代码可以正常工作:

string[] ds = {"123","321"};
listBox1.DataSource = ds;
listBox1.SetSelected(1, true);
MessageBox.Show(listBox1.Items.Count.ToString()); //returns 2

在一个全新的 C# 项目中进行了测试,在表单上放置了 listBox1,上面的代码位于 Form_Load

编辑:我没有意识到在运行时创建 ListBox 会产生影响,尤其是因为设置所选项目的时间很重要。此代码有效:

string[] ds = { "123", "321" };
ListBox lst = new ListBox();
lst.DataSource = ds;
lst.Size = new Size(100,100);            
this.Controls.Add(lst);
//make sure to call SetSelected after adding the ListBox to the parent
lst.SetSelected(1, true);

感谢@Brad 指出这一点。所以回到原来的问题,替换这个:

listBox.SetSelected(0, true);
listBox.SetSelected(1, true);
flowPanel.Controls.Add(lblListBox);
flowPanel.Controls.Add(listBox);

用这个:

flowPanel.Controls.Add(lblListBox);
flowPanel.Controls.Add(listBox);
listBox.SetSelected(0, true);
listBox.SetSelected(1, true);

它应该可以工作。

【讨论】:

  • 这段代码和他的不同之处在于,您在设计时将列表框放在表单上,​​就像 OP 在运行时将它放在那里一样。
  • 由于您在设计时将控件放在父控件上,使您的控件在设置 DataSource 时可见,因此它可以工作,OP 在尝试访问 Items 集合后设置父控件.
  • 所以这是一个渲染问题。问题是在表单绘制完成之前我无法将项目设置为选定项目,这是非常不幸的。我需要解决这个问题,否则我将不得不跟踪创建的所有列表框元素,以便稍后绑定到这些值:(
【解决方案2】:

您有两种选择如何获取ListBox 中可用的数据。您可以设置DataSource,也可以通过listBox.Items.Add(paramater) 手动添加项目。你不能两者都做,因为它们会互相踩踏,因此你的错误

...cannot add items to the Item collection when DataSource is set.

【讨论】:

  • 是的,我发现了这一点。我在我添加的水平线下方添加了一些附加代码,以演示禁用 .DataSource 对象并仅使用 Items 集合。但是,如果您查看 Neolisk 下面写的另一个答案,您会发现设置 DataSource 和使用 SetSelected 对他有用。我现在想弄清楚为什么
【解决方案3】:

来自MSDN的项目

此属性使您能够获取对当前存储在 ListBox 中的项目列表的引用。使用此引用,您可以添加项目、删除项目并获取集合中项目的计数。有关可以使用项目集合执行的任务的更多信息,请参阅 ListBox.ObjectCollection 类参考主题。

来自MSDN的数据源

实现 IList 或 IListSource 接口的对象,例如 DataSet 或 Array。默认为空

我不是这方面的专家,但从我读到的内容看来,Items 允许您添加/修改列表中的内容,而 Datasource 检索和设置内容。

【讨论】:

    【解决方案4】:

    仅当控件可见时才从数据源填充 Items 集合。由于您动态创建控件,因此它不会添加到父控件,因此不可见。因此,您首先需要有一个在屏幕上可见的控件。在您的代码中,您设置 DataSource,然后在 ControlFlowChart 上可见之前设置所选项目,因为它没有添加到 Parent 控件中。您应该更改语句的顺序。您应该将listBox 添加到FlowPanel,这将从DataSource 填充Items 集合,您可以在该集合上执行SetSelected() 方法。试试这个并注意初始代码的执行顺序发生了变化:

    ListBox listBox = new ListBox();
    listBox.DataSource = param.Values;
    listBox.DisplayMember = "Value";
    listBox.SelectionMode = SelectionMode.MultiExtended;
    listBox.Size = new System.Drawing.Size(flowPanel.Size.Width - lblListBox.Size.Width - 10, 100);
    flowPanel.Controls.Add(lblListBox);
    flowPanel.Controls.Add(listBox); //notice that you first add the listBox to the flowChart
    listBox.SetSelected(0, true);   //and then you have items in the Items collection which you can select
    listBox.SetSelected(1, true);
    

    【讨论】:

      【解决方案5】:

      我不确定为什么会有两个不同的集合。 Items 属性似乎更简单。

      我找到了异常的原因: 显然你必须按照特定的顺序做事,像这样:

          //init the listbox
          var listBox1 = new ListBox();
          listBox1.Location = new System.Drawing.Point(122, 61);
          listBox1.Size = new System.Drawing.Size(205, 147);
          listBox1.SelectionMode = SelectionMode.MultiExtended;
          Controls.Add(listBox1); //<-- point of interest
      
          //then set the DataSource
          listBox1.DataSource = lst;
          listBox1.DisplayMember = "Name";
          listBox1.ValueMember = "Age";
      
          //then set the selected values
          listBox1.SetSelected(0, true);
          listBox1.SetSelected(1, true);
      

      我的Test 类如下所示:

      public class Test
      {
          private static Random r = new Random();
          public Test (string name)
          {
              Name = name;
              Age = r.Next(16, 45);
          }
      
          public string Name { get; set; }
      
          public int Age{ get; set; }
      }
      

      lst 是这样声明的:

          var lst = new List<Test>()
                        {
                            new Test("jens"),
                            new Test("Tom"),
                            new Test("John"),
                            new Test("Don"),
                            new Test("Jenny"),
                        };
      

      【讨论】:

      • 我尝试了您的建议,即在执行 SetSelected 之前将列表框添加到流程面板控件。我开始怀疑这是一个 Winforms 渲染问题,其中集合直到绘制的某个点才更新?我正在努力验证这个怀疑。
      • @Magnum 我同意。也许有一些正确的放置BeginEdit() 和EndEdit()
      • @Magnum 但是你有没有尝试设置DataSource listBox被添加到flowPanel之后?
      • 查看我标记的答案,您的测试和我的问题之间的主要区别是我在运行时动态创建列表框。如果您使用设计器,则列表框会出现并以其他方式呈现,并且使用 DataSource 独占或 Items 都可以。
      猜你喜欢
      • 1970-01-01
      • 2019-05-07
      • 1970-01-01
      • 1970-01-01
      • 2014-06-29
      • 2012-03-12
      • 2020-08-25
      • 1970-01-01
      • 2013-10-24
      相关资源
      最近更新 更多