且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何将两个可编辑和可更新的DataGridView绑定到一个列表和子列表?

更新时间:2023-10-14 14:09:58

下面是一个简单的示例:

  • 第一个DataGridView使用您定义的BindingSource.此BindingSource的DataSource设置为 List< Input> 集合.此DGV只能显示 List< Input> 中的 Name 属性,因为您不能显示同时包含单个值和值集合的行.BindingSource的 DataMember 必须为空(或为null):如果将 Name 设置为 DataMember ,则会得到一个数组> Char 作为结果,而不是字符串.

  • 创建第二个BindingSource:其DataSource设置为现有的BindingSource.在这种情况下, DataMember 被显式设置为 Friends 属性,该属性表示 List< class> 对象(单个对象代表一个将数据提供给第二个DGV的行的集合).

这将在两个BindingSource对象之间生成活动绑定(关系):当第一个

样本对象初始化,用于测试:

  listInput.AddRange(new [] {新的Input(){名字=名字一",朋友=新列表<朋友>(){new Friend(){FirstName ="First",LastName =一个人的朋友"},new Friend(){FirstName ="Second",LastName =一个人的朋友"},new Friend(){FirstName ="Third",LastName =一个人的朋友"},}},新的Input(){名称=名称二",朋友=新列表<朋友>(){new Friend(){FirstName ="First",LastName ="Two of Friends"},new Friend(){FirstName ="Second",LastName ="Two of Friends"},new Friend(){FirstName ="Third",LastName ="Two of Friends"},}},新的Input(){名称=名称三",朋友=新列表<朋友>(){new Friend(){FirstName ="First",LastName =三个朋友"},new Friend(){FirstName ="Second",LastName =三个朋友"},new Friend(){FirstName ="Third",LastName =三个朋友"},}}}); 

I have a List<Input> named listInput that contains property of type a List<class> named friends:

List<Input> listInput=new List<Input>();
BindingList<Input> blInput;
BindingSource bsInput;

public class Input
{
    public string Name { get; set; }
    public List<Friend> friends{ get; set; }
}

public class Friend
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

I have two DataGridView controls, named dgInputand dgFriends.
dgInput is bound to a BindingList like this:

blInput = new BindingList<Input>(listInput);
bsInput = new BindingSource(blInput, null);
dgInput.DataSource = bsInput;

dgInput is editable and updatable at run-time after user changes any cell on dgInput.

The question is: how can I bind dgFriends to sublist friends, so that it will automatically update when the dgInput curretn Row changes?
It's also important that dgFriens can be updated when an User changes any cell on dgFriends (the properties of listInput must preserve the changes).

Here's a simple example:

  • The first DataGridView uses the BindingSource you defined. The DataSource of this BindingSource is set to the List<Input> collection. This DGV can only show the Name property from the List<Input>, since you cannot present Rows containing both single values and collections of values.
    The DataMember of the BindingSource must be empty (or null): if you set Name as DataMember, you'll get an array of Char as a result instead of a string.

  • A second BindingSource is created: its DataSource is set to the existing BindingSource. In this case, the DataMember is set explicitly to the Friends property, which represents a List<class> objects (a single object that represents a collection that will provide the data to the Rows of the second DGV).

This generates an active binding (a relationship) between the two BindingSource objects: when the first BindingSource.Current object changes, the second BindingSource will follow, showing its Current object (the List<Friend> linked to the current Name property).

All properties of the two classes are editable.

Note:
I've implemented the INotifyPropertyChanged interface in the Input class to notify changes of the Name property: it's not strictly required (or, it' not required at all) in this context, but you may want to have it there, you'll quite probably need it later.

List<Input> listInput = new List<Input>();
BindingSource bsInput = null;

public SomeForm()
{
    InitializeComponent();

    bsInput = new BindingSource(listInput, "");
    var bsFriends = new BindingSource(bsInput, "Friends");
    dataGridView1.DataSource = bsInput;
    dataGridView2.DataSource = bsFriends;
}

public class Input : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    private string m_Name = string.Empty;

    public string Name {
        get => m_Name;
        set { m_Name = value;
            NotifyPropertyChanged(nameof(this.Name));
        }
    }
    public List<Friend> Friends { get; set; } = new List<Friend>();

    private void NotifyPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public class Friend {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

This is how it works:

Sample object initialization, for testing:

listInput.AddRange(new[] {
    new Input() {
        Name = "Name One", Friends = new List<Friend>() {
            new Friend () { FirstName = "First", LastName = "Friend of One"},
            new Friend () { FirstName = "Second", LastName = "Friend of One"},
            new Friend () { FirstName = "Third", LastName = "Friend of One"},
        }
    },
    new Input() {
        Name = "Name Two", Friends = new List<Friend>() {
            new Friend () { FirstName = "First", LastName = "Friend of Two"},
            new Friend () { FirstName = "Second", LastName = "Friend of Two"},
            new Friend () { FirstName = "Third", LastName = "Friend of Two"},
        }
    },
    new Input() {
        Name = "Name Three", Friends = new List<Friend>() {
            new Friend () { FirstName = "First", LastName = "Friend of Three"},
            new Friend () { FirstName = "Second", LastName = "Friend of Three"},
            new Friend () { FirstName = "Third", LastName = "Friend of Three"},
        }
    }
});