在带有格式的 WinForm Combox 上使用数据绑定来添加和删除项目

Using Data Binding on a WinForm Combox with formatting for adding and removing items

我在 Visual Basic 和 C# 中维护 NET Framework 3.5 中的一些旧 WinForms,但我很难尝试绑定到 POCOs 数据 class。

我知道了(使用 INotifyPropertyChanged):

public string DisplayUnits
{  
     get { return _displayUnits; }
     set
         { 
             _displayUnits = value;
             NotifyChange("DisplayUnits");
         }
}

public string SetUnit
{
    get { return _setUnit; }
    set
        {
          _setUnit = value;
          NotifyChange("SetUnit");
        }
}

设置 SetUnit 不是问题,因为我得到了这个:

ComboxBoxUnits.DataBindings.Add("SelectedItem", data, "SetUnit", False, DataSourceUpdateMode.OnPropertyChanged)

这是有效的,因为它是一个字符串到一个字符串,但是 data.DisplayUnits 是一个字符串,即。 "inch;feet;yard;mm;cm;m" 目前 ComboBox 的填充方式是这样的:

ComboBoxUnits.Items.AddRange(data.DisplayUnits.Split(";"));

此外,用户还可以通过一种方式在该列表中添加和删除项目。因此,当我需要执行 Items.Add 或 Items.Remove 时,如何将其绑定到 DisplayUnits 属性 并在组合框项目列表更新时触发更改?

相反,它必须像 WPF 中那样是双向数据绑定。当 data.DisplayUnits 被另一个进程(即:ft2;yard2;cm2;m2)从它们更改为不同的 "Products" 时更新(例如第一个产品将测量长度,第二个产品将测量近似面积),我需要更新 UI 以反映 Combox.Items

中的那些更改

在 MVVM 中,我可以尝试使用 ITypeConverter (Convert, ConvertBack),但我认为它在 WinForms 中不受支持;但是必须有一种方法可以将 Combobox.Items 格式化为数据 class 的 POCO 属性,并且将 POCO class 转换回相同的 Winform .

有什么想法吗?

更新:澄清一下——我将其保留在普通旧 CLR 对象 (POCO) 中的原因是因为 "data" 将用于 XML 序列化。 XML 文件将使用 PIC 处理器下载到自定义机器上。这 ”;”用于解析。因此,DisplayUnit 必须采用这种格式 "in;ft;yd".

此外,由于我们使用的触摸屏是运行 WinCE 6.5,因此将在VS2008中编译。一半将使用 C#,而另一半将使用 Visual Basic。

我不知道我在这里是否理解你的意思,但据我所知,你需要设置 ComboBox 的数据源 属性。这就是我在 Winforms 中的做法:

添加一个 class 来定义 ComboBox 的数据源:

class ComboDataSource 
{
    // Display is the property shown as item in your ComboBox (DisplayMember)
    public string Display { get; set; }

    // you could also add a ValueMember ID or something 
}

添加一个方法,将您的 DisplayUnits 作为参数并将您的字符串切割成字符串数组。遍历字符串并创建并添加您的 ComboDataSource。将列表分配给 ComboxBoxUnits.DataSource。定义DisplayMember(当然是ComboDataSource的Display属性)

private void UpdateDataSource(string data)
{
    var split = data.Split(';');

    List<ComboDataSource> list = new List<ComboDataSource>();
    foreach (var item in split)
        list.Add(new ComboDataSource() { Display = item });

    ComboxBoxUnits.DataSource = list;
    ComboxBoxUnits.DisplayMember = "Display";
}

对于这个例子,我创建了一个 class 来包装您的 DisplayUnits 和 SetUnit(s) 属性。 (我没有实现 SetUnit) 实现 INotifyPropertyChanged 接口。

class Data : INotifyPropertyChanged
{
    private string _displayUnits;
    public string DisplayUnits
    {
        get { return _displayUnits; }
        set
        {
            _displayUnits = value;
            OnPropertyChanged("DisplayUnits");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

然后您可以在代码中创建数据对象。像这样...

Data data = new Data();

订阅 PropertyChangedEvent...

data.PropertyChanged +=(sender, args) => UpdateDataSource(data.DisplayUnits); // pass in the DisplayUnits 

设置数据...

data.DisplayUnits = "cm;km;feet;yard";

PropertyChanged 事件将触发并更新您的 DataSource 以及 ComboBox 中显示的项目。

希望这对您有所帮助...

更新:从 ComboBox 中删除项目并重新绑定它。 (按钮点击或其他)

ComboDataSource selectedItem = comboBox1.SelectedItem as ComboDataSource;
if(selectedItem == null)
    throw new NullReferenceException("selectedItem");

// get the DataSource back from the ComboBox
List<ComboDataSource> dataSource = comboBox1.DataSource as List<ComboDataSource>;
if (dataSource != null)
{
    // remove it from the datasource
    dataSource.Remove(selectedItem);

    // so this is pretty 'straight forward' and certainly not best practice, but we put the datasource back up again the same way we have set it
    string[] comboBoxItems = dataSource.Select(item => item.Display).ToArray();
    // join the string (f.e. 'km;cm;feet') and update it again
    string newDataSource = string.Join(";", comboBoxItems);
    UpdateDataSource(newDataSource);
}