winforms 在Windows窗体中对对象使用DataSource

anauzrmj  于 6个月前  发布在  Windows
关注(0)|答案(3)|浏览(79)

我一直在尝试创建一个小型表单应用程序,我想尝试将DataGridView直接绑定到对象集合。
我创建了以下类

public class MyClassRepository
{
    public List<MyClass> MyClassList { get; set; } = new List<MyClass> { new MyClass { Name = "Test" } };
}

public class MyClass
{
    public string Name { get; set; }
}

字符串
我将下面的代码添加到一个表单中进行测试。在通过UI设置BindingSource之后,我在设计器中基于此代码(同时通过https://msdn.microsoft.com/en-us/library/ms171892.aspx进行此操作)

var tmp = new BindingSource();
tmp.DataMember = "MyClassList";
tmp.DataSource = typeof(MyClassRepository);


当这不起作用时,我开始运行BindingSource背后的代码,看看发生了什么。setter调用ResetList,它试图通过调用ListBindingHelper.GetListFromType来创建dataSourceInstance。这个调用最终调用SecurityUtils.SecureCreateInstance(Type),其中type是BindingList<MyClassRepository>。这将null传递给args,args传递给Activator.CreateInstanceActivator.CreateInstance返回一个空集合。
这个方法调用了ListBindingHelper.GetList(dataSourceInstance, this.dataMember),它会为我的MyClassList属性生成一个PropertyDescriptor,并将其赋值给dmProp
此时,GetList调用GetFirstItemByEnumerable(dataSource as IEnumerable),其中dataSource是之前创建的(空的)BindingList<MyClassRepository>示例,并返回(currentItem == null) ? null : dmProp.GetValue(currentItem);
dmProp/MyClassList的值永远不会被访问,BindingSource也永远不会被我创建的示例填充。我做错了什么吗?如果没有,源代码中是否有bug?在我看来,要么应该调用SecureCreateInstance(Type type, object[] args),MyClassList应该通过args传递,而不是现有的SecureCreateInstance(Type type)调用,要么应该使用dmProp的值。
如果这是不正确的,我如何让设计器自动生成的代码将DataSource设置为对象的示例?或者我必须从BindingSource继承吗?如果是后者,为什么它给予你选择一个不从BindingSource继承的类的选项?

gkl3eglg

gkl3eglg1#

正如Reza Aghaei所指出的,在设计器中,将BindingSource.DataSource设置为“MyClassRepository”可能会起作用,但是您仍然需要初始化(创建一个新的)MyClassRepository对象。我在发布的代码中没有看到这行代码:MyClassRepository myRepositiory = new MyClassRepository();数据源是空的,因为您还没有创建“MyClassRepository”的示例,正如Reza指出的那样,这通常以Load事件的形式完成。
为了保持简单,在设计器中删除BindingSourceDataSource,并在表单加载事件中简单地设置BindingSource’s数据源,如下所示。首先,创建MyClassRepository的新“示例”,然后使用其MyClassList属性作为BindingSource的数据源。我希望这能有所帮助。

MyClassRepository repOfMyClass;

public Form1() {
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e) {
  repOfMyClass = new MyClassRepository();
  bindingSource1.DataSource = repOfMyClass.MyClassList;
  dataGridView1.DataSource = bindingSource1;
}

字符串

编辑-

经过进一步的审查.我同意你应该能够做到你所描述的。我能够得到它的工作与预期的代码如下。

BindingSource bindingSource1;
private void Form1_Load(object sender, EventArgs e) {
  bindingSource1 = new BindingSource();
  bindingSource1.DataSource = typeof(MyClassRepository);
  bindingSource1.DataMember = "MyClassList";
  dataGridView1.DataSource = bindingSource1;
}


我在“设计器”中遵循了相同的步骤,它按预期工作。还有什么我错过了吗?正如你所说的.使用MyClassRepository mcr = new MyClassRepository()似乎是不必要的。此外,如果你不能使用上面的两种方法之一让它工作.

编辑2

没有创建一个“新的”MyClassRepository,对象是意外的,我没有意识到添加到列表/网格的新项目将进入bindingSource1。主要的一点是,没有示例化一个“新的”MyClassRepository对象,构造函数将永远不会运行。这意味着属性List<MyClass> MyClassList将永远不会被示例化。也不会设置默认值。
因此,MyClassList变量在此上下文中将不可访问。例如,在此特定情况下,如果将行添加到网格中,那么bindingSource1.Count属性将返回正确的行数。不仅MyClassList中的行数为零(0),而且更重要的是. new”MyClassRepository对象?由于这种不可访问性,MyClassList将永远不会被使用。

编辑3 -

你想要实现的可以通过无数种方式来实现。如果你想让MyClassList包含用户在网格中所做的真实的时间更改,那么使用你的代码和我发布的代码是行不通的。例如,在你的代码和我的代码中.如果用户向网格中添加了一行,它会将该项添加到“bindingSource”中,但不会将其添加到MyClassList中。我只能猜测这不是您想要。否则,MyClassList的用途是什么。下面的代码“将”按预期使用MyClassList。如果你删除“设计器”透视图......你可以在三(3)中做同样的事情代码行.如果你修复损坏的MyClassRepository类并在表单加载事件上创建一个新的类。恕我直言,这比摆弄设计器要容易得多。
MyClassRepository的更改.添加了一个构造函数,添加了一个size属性和一个方法作为示例。

class MyClassRepository {

  public List<MyClass> MyClassList { get; set; }
  public int MaxSize { get; set; }

  public MyClassRepository() {
    MyClassList = new List<MyClass>();
    MaxSize = 1000;
  }

  public void MyClassListSize() {
    MessageBox.Show("MyClassList.Count: " + MyClassList.Count);
  }

  // other list manager methods....

}


MyClass的更改...添加了一个属性作为示例。

class MyClass {
  public string Name { get; set; }
  public string Age { get; set; }
}


最后,表单加载事件创建一个新的MyClassRepository,设置绑定源指向MyClassList,最后将绑定源设置为网格的数据源。注意:使myClassRepositorygridBindingSource成为全局变量是不必要的,这样设置是为了检查MyClassList是否随用户在网格中的操作而真实的更新。在下面的按钮单击事件中完成。

MyClassRepository myClassRepository;
BindingSource gridBindingSource;

public Form1() {
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e) {
  try {
    myClassRepository = new MyClassRepository();
    gridBindingSource = new BindingSource(myClassRepository.MyClassList, "");
    dataGridView1.DataSource = gridBindingSource;
  }
  catch (Exception ex) {
    MessageBox.Show("Error: " + ex.Message); 
  }
}

private void button1_Click_1(object sender, EventArgs e) {
  MessageBox.Show("Binding source count:" + gridBindingSource.Count + Environment.NewLine +
                  "MyClassList count: " + myClassRepository.MyClassList.Count);
}


我希望这是有意义的;-)

gmxoilav

gmxoilav2#

设计工具会针对设计阶段支援设定DataSource = typeof(Something),例如,让您在设定数据系结时,从下拉式清单中选择DataMember,或从下拉式清单中选择数据来源属性。
如何使设计器自动生成的代码将DataSource设置为对象的示例?
强迫设计人员这样做没有多大意义,因为设计人员不知道您将使用什么样的真实的数据源来加载数据。它可以是Web服务、WCF服务或业务逻辑层类。
因此在运行时,需要将列表的一个示例赋给DataSource。例如,在表单的Load事件中。

o75abkj4

o75abkj43#

基于此处的其他答案,Visual Studio设计器将创建一个BindingSource对象,并将其DataSource属性设置为要绑定的对象的Type。它这样做的原因是通知设计器哪些属性可用于绑定。设计器没有对象示例的概念,因为您的程序当时未运行。
如果您遵循Microsoft的How to: Create a simple-bound control on a Windows Form,您可能会发现自己感到困惑,因为Form仍然看起来不像有任何绑定的属性。不幸的是,该指南特别缺乏,并且没有跟进代码以完成目标。作为程序员,您仍然有责任创建您想要绑定的对象的示例,然后执行任何代码,这些代码将导致对象填充最终将出现在绑定Form中的数据。
我认为,我们如何做到这一点,最好由Reza的answer来解释;在Form的类中创建一个private成员,该成员将保存要绑定的对象的示例。然后,您将使用Form的Load事件示例化对象(或者如果示例是在代码中的其他地方创建的,则通过其他方式获取示例),将其分配给Form中的私有成员,然后 * 将BindingSource.DataSource属性设置为新的私有对象示例 *。这是我和我认为许多其他人错过的关键步骤,Microsoft可以更好地记录这一点。例如,看看他们的BindingSource class文档。

binding1.DataSource = fonts
listBox1.DataSource = binding1
listBox1.DisplayMember = "Name"

字符串
您唯一需要担心的部分是binding1.DataSource行;这是您指定支持对象作为数据源的地方。

相关问题