【问题标题】:Databinding a WinForms ComboBox requires user-interaction twice数据绑定 WinForms ComboBox 需要两次用户交互
【发布时间】:2014-06-06 00:44:47
【问题描述】:

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

注意绑定设置为OnPropertyChanged而不是OnValidate,这是因为在使用OnValidate时,如果窗体关闭或失去焦点,控件不一定会更新ViewModel (但控件仍然认为它有焦点。在 Compact Framework 上,没有办法“强制验证”,所以我必须使用 OnPropertyChanged

在桌面 Windows 窗体和智能设备 Windows 窗体上都存在一个可重现的问题:当尝试选择或设置组合框中的当前项(使用鼠标或键盘)时,该值在设置之前不会“粘住”两次 - 也就是说,在组合框的值发生变化之前,您需要选择相同的项目两次。

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

我不认为这是框架中的错误,有趣的是它在桌面和紧凑型框架上是如何发生的。

这是我的代码:

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 ); }
    }
}

【问题讨论】:

    标签: winforms data-binding


    【解决方案1】:

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

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

      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); } }

    【讨论】:

      猜你喜欢
      • 2014-09-05
      • 2010-09-05
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 1970-01-01
      • 2017-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多