数据绑定WinForms ComboBox需要两次用户交互

时间:2014-06-06 00:44:48

标签: winforms data-binding

我在Windows窗体中有一个ComboBox(DropDownList样式),它的数据源设置为BindingList,并且SelectedValue属性绑定到viewmodel的属性。

请注意,绑定设置为OnPropertyChanged而不是OnValidate ,这是因为使用OnValidate时控件不一定会更新ViewModel表单已关闭或失去焦点(但控件仍认为它具有焦点。在Compact Framework上,无法强制验证,因此我必须使用OnPropertyChanged

在桌面Windows窗体和智能设备Windows窗体上都存在可重现的问题:当尝试在组合框中选择或设置当前项目时(使用鼠标或键盘),该值不会是"棒"直到它被设置两次 - 也就是说,你需要在组合框的值改变之前选择相同的项目两次。

没有抛出异常(甚至被捕获的异常),也没有可用的诊断报告。

我不认为这是框架中的错误,并且它在Desktop和Compact Framework上的发生方式很有趣。

这是我的代码:

Form1.cs的

public partial class Form1 : Form {

    private ViewModel _vm;

    public Form1() {
        InitializeComponent();

        this.bindingSource1.Add( _vm = new ViewModel() );
    }
}

Form1.Designer.cs(相关行)

        // 
        // bindingSource1
        // 
        this.bindingSource1.DataSource = typeof( WinForms.Shared.ViewModel );
        // 
        // comboBox1
        // 
        this.comboBox1.DataBindings.Add( new System.Windows.Forms.Binding( "SelectedValue", this.bindingSource1, "SelectedSomeTypeId", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged ) );
        this.comboBox1.DataSource = this.someTypeListBindingSource;
        this.comboBox1.DisplayMember = "DisplayText";
        this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
        this.comboBox1.FormattingEnabled = true;
        this.comboBox1.Location = new System.Drawing.Point( 12, 27 );
        this.comboBox1.Name = "comboBox1";
        this.comboBox1.Size = new System.Drawing.Size( 182, 21 );
        this.comboBox1.TabIndex = 0;
        this.comboBox1.ValueMember = "Id";
        // 
        // someTypeListBindingSource
        // 
        this.someTypeListBindingSource.DataMember = "SomeTypeList";
        this.someTypeListBindingSource.DataSource = this.bindingSource1;

ViewModel.cs

public class ViewModel : INotifyPropertyChanged {

    public ViewModel() {

        this.SomeTypeList = new BindingList<SomeType>();

        for(int i=0;i<5;i++) {
            this.SomeTypeList.Add( new SomeType() {
                Id = i + 1,
                Name = "Foo" + ((Char)( 'a' + i )).ToString()
            } );
        }

        this.SelectedSomeTypeId = 2;
    }

    public BindingList<SomeType> SomeTypeList { get; private set; }


    private Int64 _selectedSomeTypeId;
    public Int64 SelectedSomeTypeId {
        get { return _selectedSomeTypeId; }
        set {
            if( _selectedSomeTypeId != value ) {
                _selectedSomeTypeId = value;
                OnPropertyChanged("SelectedSomeTypeId");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(String propertyName) {

        PropertyChangedEventHandler handler = this.PropertyChanged;
        if( handler != null ) handler( this, new PropertyChangedEventArgs(propertyName) );
    }
}

public class SomeType {

    public String Name { get; set; }

    public Int64 Id { get; set; }

    public String DisplayText {
        get { return String.Format("{0} - {1}", this.Id, this.Name ); }
    }
}

1 个答案:

答案 0 :(得分:3)

我从未找到解决此问题的“正确”方法,并且通常使用以下两种方法之一来使事情发挥作用:

  • 直接:绕过这一条目的绑定机制

    combo1.SelectedIndexChanged += (s,e) _viewModel.Item = combo1.SelectedItem;

  • 通用绑定:创建自定义ComboBox并覆盖OnSelectedIndexChanged事件以强制进行绑定更新。

    public class BoundComboBox : ComboBox { protected override void OnSelectedIndexChanged(EventArgs e) { var binding = this.DataBindings["SelectedItem"]; if( binding != null ) binding.WriteValue(); base.OnSelectedIndexChanged(e); } }