Winforms ComboBox数据绑定DisplayMember到子对象属性

10

我搜寻了2个小时还是找不到答案,所以我在这里尝试求助:

我想知道怎样(如果可能)可以将一个模型列表数据绑定到WinForms下拉框,并使用列表中某个模型的属性中嵌套的属性作为显示成员(DisplayMember)? 代码如下:

public partial class Form1 : Form
{
    private List<UserDataModel> userData = new List<UserDataModel>();

    public Form1()
    {
        InitializeComponent();

        MyInit();
    }

    public void MyInit()
    {
        var userDataModel1 = new UserDataModel();
        userDataModel1.Name = "Mike";
        userDataModel1.Phone = "555-666";
        userDataModel1.Home = new HomeDataModel();
        userDataModel1.Home.StreetName = "MikeStreet";
        userDataModel1.Home.GeoLocationX = 111;
        userDataModel1.Home.GeoLocationY = 222;

        var userDataModel2 = new UserDataModel();
        userDataModel2.Name = "Jonathan";
        userDataModel2.Phone = "777-888";
        userDataModel2.Home = new HomeDataModel();
        userDataModel2.Home.StreetName = "JonathanStreet";
        userDataModel2.Home.GeoLocationX = 333;
        userDataModel2.Home.GeoLocationY = 444;

        userData.Add(userDataModel1);
        userData.Add(userDataModel2);

        // This works as usually:
        /*
        comboBox1.DisplayMember = "Name";
        comboBox1.ValueMember = "Home";
        comboBox1.DataSource = userData;
        */

        // But this works not (either with comboBox1.DataBindings.Add() nor with BindingSource):
        comboBox1.DisplayMember = "Home.StreetName";
        comboBox1.ValueMember = "Home";
        comboBox1.DataSource = userData;

        // To drive me crazy, THAT shit works:
        textBox1.DataBindings.Add("Text", userData, "Home.StreetName");

        /*
        So how can i use a String-Property of a SubObject as ComboBox-DisplayMember ???

        BTW: To rebuild the sample, you only need a normal Forms Application and
        then drop a ComboBox and a TextBox on it. Copy that code here, and run it.
        */
    }
}

internal sealed class UserDataModel
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public HomeDataModel Home { get; set; }
}

internal sealed class HomeDataModel
{
    public string StreetName { get; set; }
    public int GeoLocationX { get; set; }
    public int GeoLocationY { get; set; }
}

你想将什么作为ValueMember呈现? - Hamlet Hakobyan
作为 ValueMember,我使用 Home 本身。这总是很好的工作,因为 ValueMember =“Home”,而不是 ValueMember =“Home.GeoLocationX”等。 - MBODM
4个回答

7

你的代码中新增了一个方法(其实是事件),而且它已经能够正常工作了。

public partial class Form1 : Form
{
    private List<UserDataModel> userData = new List<UserDataModel>();
    public Form1()
    {
        InitializeComponent();

        MyInit();
    }

    public void MyInit()
    {
        var userDataModel1 = new UserDataModel();
        userDataModel1.Name = "Mike";
        userDataModel1.Phone = "555-666";
        userDataModel1.Home = new HomeDataModel();
        userDataModel1.Home.StreetName = "MikeStreet";
        userDataModel1.Home.GeoLocationX = 111;
        userDataModel1.Home.GeoLocationY = 222;

        var userDataModel2 = new UserDataModel();
        userDataModel2.Name = "Jonathan";
        userDataModel2.Phone = "777-888";
        userDataModel2.Home = new HomeDataModel();
        userDataModel2.Home.StreetName = "JonathanStreet";
        userDataModel2.Home.GeoLocationX = 333;
        userDataModel2.Home.GeoLocationY = 444;

        userData.Add(userDataModel1);
        userData.Add(userDataModel2);

        // This works as usually:
        /*
        comboBox1.DisplayMember = "Name";
        comboBox1.ValueMember = "Home";
        comboBox1.DataSource = userData;
        */

        // But this works not (either with comboBox1.DataBindings.Add() nor with BindingSource):
        comboBox1.DisplayMember = "Home.StreetName";
        comboBox1.ValueMember = "Home";
        comboBox1.DataSource = userData;

        // To drive me crazy, THAT shit works:
        textBox1.DataBindings.Add("Text", userData, "Home.StreetName");

        /*
        So how can i use a String-Property of a SubObject as ComboBox-DisplayMember ???

        BTW: To rebuild the sample, you only need a normal Forms Application and
        then drop a ComboBox and a TextBox on it. Copy that code here, and run it.
        */
    }

    // To add this method - follow to my instructions below
    private void ComboBoxFormat(object sender, ListControlConvertEventArgs e)
    {
        e.Value = ((UserDataModel)e.ListItem).Home.StreetName;
    }
}

internal sealed class UserDataModel
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public HomeDataModel Home { get; set; }
}

internal sealed class HomeDataModel
{
    public string StreetName { get; set; }
    public int GeoLocationX { get; set; }
    public int GeoLocationY { get; set; }
}

要创建这个方法(事件),请进入表单的[设计]模式,在ComboBox上单击右键 ->属性。
在属性窗口的顶部,单击事件(闪电图标)。
在下面的事件列表中(在属性更改下面),查找格式,并在其中输入一些事件名称,比如:ComboBoxFormat ,然后按Enter键。 就这样;)

0

好的,我已经让 DisplayMember 正常工作了。你不能使用 "Home" 作为 ValueMember,因为它是一个对象,你必须指向它的一个属性(StreetName、GeoLocationX、GeoLocationY)。

public partial class Form1 : Form
    {

        private List<UserDataModel> userData = new List<UserDataModel>();

        public Form1()
        {
            InitializeComponent();
            MyInit();
        }

        public void MyInit()
    {
        var mymodel = new UserDataModel("Derek", "999-999", new HomeDataModel("Sesame Street", 111, 222));

        var mymodel1 = new UserDataModel("John", "999-999", new HomeDataModel("Tin Can Alley", 333, 444));

        userData.Add(mymodel);
        userData.Add(mymodel1);

        BindingSource bs = new BindingSource();
        bs.DataSource = userData;

        comboBox1.DataSource = bs;
        comboBox1.DisplayMember = "Home.StreetName";

    }
}

internal sealed class UserDataModel
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public HomeDataModel Home { get; set; }

    public UserDataModel()
    {

    }

    public UserDataModel(string name, string phone, HomeDataModel home)
    {
        this.Name = name;
        this.Phone = phone;
        this.Home = home;
    }
}

internal sealed class HomeDataModel
{
    public string StreetName { get; set; }
    public int GeoLocationX { get; set; }
    public int GeoLocationY { get; set; }

    public HomeDataModel()
    {

    }

    public HomeDataModel(string streetname, int x, int y)
    {
        this.StreetName = streetname;
        this.GeoLocationX = x;
        this.GeoLocationY = y;

    }
}

嗨 Derek,我也尝试了那个解决方案。它编译并且...运行了。但实际上并没有真正起作用,因为我的comboBox1只显示了一个下拉字段:“芝麻街”。comboBox1.Items.Count也是1。所以它并没有真正地起作用。为什么?我不知道... - MBODM
抱歉,我会再次查看这个。 - Derek
问题出在HomeDataModel上。您能否不将其三个属性包含在UserDataModel类中?我认为这样可以解决问题。 - Derek
感谢您的努力,Derek!显然,我可以将属性添加到UserDataModel中,但这会伤害我的问题的根本。我还有许多其他需要SubProperty的ComboBox。如果我将所有这些类放入一个类中,您会称其为“良好的软件设计”吗? :) - MBODM
我对你的整个应用程序没有概念。@马塞尔。这是一个有趣的问题。我会在今天的某个时候再看一下它。 - Derek

0
澄清一下:ComboBox 默认情况下始终使用宿主窗体的 BindingContext。我用以下代码进行了测试:
            var bc = comboBox1.BindingContext;
        if (bc == this.BindingContext)
        {
            if (bc.Equals(this.BindingContext))
            {
                MessageBox.Show("combobox always use same binding context as his hosting form");
            }
        }

并且消息框已显示。


0

这里有一个类似的问题。在标记的答案中,您可以看到他们将FormBindingContext设置在ComboBox上,这似乎对他们有效...

//...
comboBox1.BindingContext = this.BindingContext;

希望这有所帮助...


嗨,我认为这与绑定上下文无关。我尝试了一下,确保ComboBox仍然使用表单的绑定上下文(它确实如此):<请参见下面我的答案中的代码(因为这里没有代码格式)> - MBODM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接