【问题标题】:How do I bind a DataGridViewComboBoxColumn to a property/method of an object that returns a list?如何将 DataGridViewComboBoxColumn 绑定到返回列表的对象的属性/方法?
【发布时间】:2012-07-01 09:11:00
【问题描述】:

我有一个具有多个属性的自定义对象,其中一个返回一个列表。这是对象的代码:

public class SearchResult
{
    private int eventId;
    private String eventTitle;
    private int startDate;
    private List<String> tags;

    // Properties
    public int EventId { get { return this.eventId; } }

    public String EventTitle { get { return this.eventTitle; } }

    public int StartDate { get { return this.startDate; } }

    public List<String> Tags { get { return this.tags; } }

    public SearchResult(int eventId, String eventTitle, int startDate, List<String> tags)
    {
        // Constructor code
    }

    public List<String> GetTags()
    {
        return this.tags;
    }
}

我还有一个DataGridViewComboBoxColumn,我想将它绑定到Tags 属性。基本上,每个SearchResult 对象都将显示在自己的行中,我希望每个对象的Tags 属性中的List&lt;String&gt; 显示在该行的ComboBox 单元格中。这是我迄今为止为我的DataGridView 编写的代码:

BindingList<SearchResult> results = new BindingList<SearchResult>();
results.Add(new SearchResult(1, "This is a title", 2012, new List<String> { "Tag1", "Tag with a long name1" }));
results.Add(new SearchResult(2, "The quick brown fox", 2012, new List<String> { "Stack", "Overflow" }));
results.Add(new SearchResult(3, "In this tutorial, you create a class that is the type for each object in the object collection. ", 2012, new List<String> { "NYSE", "FTSE" }));
results.Add(new SearchResult(4, "another long piece of title text", -999, new List<String> { "Rabbits", "Chickens" }));

MyDataGrid.AutoGenerateColumns = false;
MyDataGrid.AllowUserToAddRows = false;
MyDataGrid.AllowUserToDeleteRows = false;
MyDataGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.None;
MyDataGrid.BackgroundColor = System.Drawing.SystemColors.Control;
MyDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
MyDataGrid.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders;
MyDataGrid.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.DisplayedCells;
MyDataGrid.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

DataGridViewTextBoxColumn eventIdColumn = new DataGridViewTextBoxColumn();
eventIdColumn.DataPropertyName = "EventId";
eventIdColumn.HeaderText = "Event ID";
eventIdColumn.ReadOnly = true;
eventIdColumn.Width = 84;

DataGridViewTextBoxColumn eventTitleColumn = new DataGridViewTextBoxColumn();
eventTitleColumn.DataPropertyName = "EventTitle";
eventTitleColumn.HeaderText = "Event Title";
eventTitleColumn.ReadOnly = true;
eventTitleColumn.Width = 368;

DataGridViewTextBoxColumn startDateColumn = new DataGridViewTextBoxColumn();
startDateColumn.DataPropertyName = "StartDate";
startDateColumn.HeaderText = "Start Date";
startDateColumn.ReadOnly = true;
startDateColumn.Width = 130;

//I think I need to insert the code for the tags column here, but I'm not sure

MyDataGrid.Columns.Add(eventIdColumn);
MyDataGrid.Columns.Add(eventTitleColumn);
MyDataGrid.Columns.Add(startDateColumn);
//MyDataGrid.Columns.Add(tagsColumn);

MyDataGrid.DataSource = results;

我从a tutorial I found online 派生了这段代码,它运行良好。

我一直在尝试将SearchResultTags 属性绑定到DataGridViewComboBoxColumn,但我不确定如何。我一直在看this question,它提供了这个代码:

column.DataPropertyName = "Foo";
column.DisplayMember = "SomeNameField"; 
column.ValueMember = "Bar"; // must do this, empty string causes it to be 
                            // of type string, basically the display value
                            // probably a bug in .NET
column.DataSource = from foo in Foo select foo;
grid.DataSource = data;

我遇到问题的原因是我不理解链接问题的一些细微差别。

  1. 根据the documentation 和链接的问题,DisplayMember 应该链接到“包含实例描述”的属性,但由于SearchResult 对象是动态添加的,并且没有任何相关的描述他们,我应该把它留空吗?
  2. ValueMember 给我带来了类似的问题,因为即使在阅读 its documentation 之后我也不确定该放什么。
  3. 在链接问题中,接受的答案使用 LINQ 一次性绑定整个数据网格。我应该这样做吗?我不确定如何根据我的情况修改该代码,但我认为应该是这样的。

tagsColumn.DataPropertyName = "Tags";
tagsColumn.DisplayMember = ""; // I'm unsure of what to put here
tagsColumn.ValueMember = ""; // Once again, I don't know what to set this to

我还认为我应该有一行为列设置DataSource,例如

tagsColumn.DataSource = <some LINQ query, perhaps?>

但我不知道,因为我能找到的唯一最相关的 C# 源代码就是这个问题。

更新:

我确实找到了a second question,它建议使用与此类似的代码进行数据绑定:

// reference the combobox column
DataGridViewComboBoxColumn cboBoxColumn = (DataGridViewComboBoxColumn)dataGridView1.Columns[0];
cboBoxColumn.DataSource = Choice.GetChoices();
cboBoxColumn.DisplayMember = "Name";  // the Name property in Choice class
cboBoxColumn.ValueMember = "Value";  // ditto for the Value property

基于此,我 a) 将 GetTags() 方法添加到 SearchResult 并将此代码添加到我的 DataGridView 初始化代码中:

        DataGridViewComboBoxColumn tagsColumn = new DataGridViewComboBoxColumn();
        tagsColumn.DataSource = SearchResult.GetTags(); // ERROR 
        tagsColumn.DisplayMember = ""; // Still not sure
        tagsColumn.ValueMember = ""; // ??

但是,当我尝试运行此命令时,Visual Studio 在第二行给我一个错误:

An object reference is required for the non-static field, method, or property 'SearchResult.GetTags()'

更新 2:

我仍在寻找这个没有成功。我不明白如何使用其他属性(例如EventId)我可以简单地将数据属性名称声明为EventId,它将显示在表格中,但我不能使用ComboBox 列来执行此操作。

由于对象是在一个单独的类中实例化并放入一个列表中,因此我应该遍历整个对象数组(其中可能有数百个)对我来说似乎没有意义将Tags 属性绑定到每个实例的ComboBox 列,当我不需要遍历SearchResult 对象列表来绑定其他属性时,例如EventId.

为什么这个 binding-properties-by-name 只适用于 一些 属性而不适用于其他属性?

【问题讨论】:

  • 错误是由于您调用 SearchResult.GetTags();您需要将其设为静态方法,或先实例化 SearchResult(新...)
  • @blearn 在第二个链接问题的答案中,对象未实例化。此外,由于SearchResult 对象将被动态添加到此列表中,在单独类中的方法中,除非我遍历它们,否则我不一定有权访问它们,在这种情况下,我不妨跳过数据绑定和手动填充DataGridView,我试图避免这样做。

标签: c# winforms data-binding datagridview


【解决方案1】:

对于这个错误,我可以建议你创建一个类的实例,然后调用该方法,因为它不是静态的,或者你可以让你的方法静态。

此外,当您需要组合框列时,

DataGridViewComboBoxColumn tagsColumn = new DataGridViewComboBoxColumn();
        tagsColumn.DataSource = SearchResult.GetTags(); // ERROR 
        tagsColumn.DisplayMember = ""; // Still not sure
        tagsColumn.ValueMember = ""; // ??

大多数情况下,我们有 Country(id,name) 等对象的下拉列表,因此 DisplayMember = name 将在下拉列表中显示为文本,而 ValueMember = id 将在数据库中的引用表中使用。但这不是您的情况。

这里有一个要在下拉列表中显示的字符串列表,因此您无需设置它们。 正如写的here

如果 DataSource 属性设置为字符串数组,则 ValueMember 和 DisplayMember 不需要设置,因为每个字符串在 数组将用于值和显示。

【讨论】:

  • 除了使用对象的实例之外,没有其他方法可以做到这一点吗?我不能像其他属性一样自动完成吗?请参阅我对原始问题的评论。
【解决方案2】:

我不太明白你为什么要使用DataGridViewComboBoxColumn 来显示元素列表。此列类型旨在允许用户选择多种可能性中的一种。它看起来不是你的情况,因为你没有 public string SelectedTag{get;set;} 属性来存储它。据我了解您的模型,您已经为 SearchResult 选择了许多标签,并且您希望在网格中显示它们。

如文档所述: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewcomboboxcolumn.datasource

获取或设置此 [DataSource] 属性获取或设置由 CellTemplate 属性返回的对象的 DataSource 属性。设置此属性还会设置列中每个单元格的 DataSource 属性并刷新列显示。要覆盖单个单元格的指定值,请在设置列值后设置单元格值。

DataGridViewComboBoxColumn 根本无法将 items 属性绑定到数据源,因为它假定只有一个元素列表用作数据网格所有行的数据源。

我还假设您会为该列设置ReadOnly = true 属性,就像为所有其他列设置一样。如果是这样,它将阻止用户表单看到标签列表,因为永远不会显示下拉列表。

如果您想以只读模式显示字符串列表,我建议将此标签列表展平为单个字符串:

public string Tags { get { return string.Join(", ", tags); } }

并将其显示在文本列中。

【讨论】:

  • 我最初想这样做是因为我不确定其他方式,但我最终使用了类似于您发布的代码(使用Join)并创建了我自己的用户控件。
  • 很抱歉,超级迟到的评论,但如果你还在,我有一个类似于 OP 的对象列表,它们确实具有“SelectedTag”和“AvailableTags”属性,并希望给每一行它是拥有不同选项的“AvailableTags”列表。有没有办法使用设计器/无需手动绑定所有内容?
猜你喜欢
  • 1970-01-01
  • 2014-08-04
  • 2021-11-22
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多