ListBox.DataSource 集合与 ListBox.Items 之间的区别?

The difference between ListBox.DataSource collection versus ListBox.Items?
2021-12-04
  •  译文(汉语)
  •  原文(英语)

我正在动态创建 Winforms 多选 ListBox 并将其添加到 flowpanel 控件中.我从我创建的对象绑定了一个数据源,并验证了 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()在表单绘制完成后调用该方法来解决的.这在我必须解决的应用程序设计中导致了很多问题,但这就是问题所在.请参阅我标记为答案的回复.

解决过程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在运行时创建 a可能会有所作为,尤其是因为何时设置所选项目很重要.此代码有效:

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

它应该工作.

解决过程2

您有两种方法可以让数据在ListBox. 您可以设置 ,DataSource也可以通过 手动添加项目listBox.Items.Add(paramater).你不能同时做,因为他们会互相踩踏,因此你的错误

...cannot add items to the Item collection when DataSource is set.
解决过程3

来自MSDN 的项目

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

来自MSDN 的数据源

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

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

解决过程4

仅当控件可见时才从 DataSource 填充 Items 集合.由于您动态创建控件,因此它不会添加到父控件中,因此不可见.因此,您首先需要有一个在屏幕上可见的控件.在您的代码中,您设置了 DataSource,然后在您Control显示之前设置了所选项目,FlowChart因为它没有添加到Parent控件中.您应该更改语句的顺序.您应该添加listBoxFlowPanel将填充 Items 集合DataSource,您可以在其上执行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"),
                  };

I am dynamically creating a Winforms multi-select ListBox and adding it into a flowpanel control. I bind a datasource from an object I created and verified that the DataSource has does in fact have approximately 14 elements. When I do a listBox.SetSelected(0, true) I get an System.ArgumentOutOfRangeException error thrown.

I have determined the problem is that while the DataSource has 14 elements, the Item collection has none (0) and is therefore throwing the exception. My question is why are these two different from one another, and why would I not simply do a foreach item in datasource add to the item collection?

The following is the code I have so far:

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;

Below is an alternative solution I attempted and worked, but again why would I use DataSource versus Items collection?

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;

ANSWER: Thanks for all the responses. The issue here is visibility and win-form rendering. While the difference between DataSource and Items collections were not really addressed save a few people, the true source of my problem was resolved by calling the SetSelected() method after the form was done drawing. This causes a lot of problems in the design of my application which I have to resolve, but this is was the problem. See the reply I marked as the answer.

Solutions1

Your problem probably lies elsewhere, because this code works fine:

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

Tested in a brand new C# project with a listBox1 put on the form, and the above code sitting in Form_Load.

EDIT: I did not realize that creating a ListBox in runtime could make a difference, and especially because it matters when to set selected items. This code works:

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

Thanks to @Brad for pointing this out. So back on the original question, replace this:

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

with this:

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

And it should work.

Solutions2

You have two options of how to get data to be available in a ListBox. You can set the DataSource or you can add the items manually via listBox.Items.Add(paramater). You cannot do both because they will step on each other hence your error

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

Items From MSDN

This property enables you to obtain a reference to the list of items that are currently stored in the ListBox. With this reference, you can add items, remove items, and obtain a count of the items in the collection. For more information about the tasks that can be performed with the item collection, see the ListBox.ObjectCollection class reference topics.

Datasource From MSDN

An object that implements the IList or IListSource interfaces, such as a DataSet or an Array. The default is null

I'm not an expert on this matter, but from what I read it appears that Items allows you to add/modify the contents in the list whereas Datasource retrieves and sets the content.

Solutions4

The Items collection is populated from the DataSource only when the Control is visible. since you create your control dynamically, it is not added to the parent control and therefore not visible. Therefore you first need to have a Control that is visible on the screen. In your code you set DataSource and then set the selected items before your Control is visible on the FlowChart since it isn't added to the Parent control. You should change the sequence of the statements. You should add the listBox to the FlowPanel which will populate Items collection from the DataSource upon which you can execute SetSelected() method. Try this and note the changed order of the execution of your initial code:

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

Im not sure why there are two different Collections. The Items property seems more simple.

I found the reason for the exception: apparently you have to do things in a specific order, like this:

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

My Test class look like this:

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

And lst is declared like this:

    var lst = new List<Test>()
                  {
                      new Test("jens"),
                      new Test("Tom"),
                      new Test("John"),
                      new Test("Don"),
                      new Test("Jenny"),
                  };
转载于:https://stackoverflow.com/questions/15208411/the-difference-between-listbox-datasource-collection-versus-listbox-items

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入