当Control.Visible == false时,无法将数据绑定到控件

在使用C#4.0 / C#2.0的WinForms中,如果控件的可见字段为false,则无法绑定到控件:

this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done"); 

我可以确认绑定已成功添加到控件的数据绑定列表中,但如果我更改绑定对象(WorkStatus),则不会发生任何事情。

这就是WorkStatus的样子:

 public class WorkStatus : INotifyPropertyChanged { private Boolean _done; public Boolean Done { get { return _done; } set { if (_done == value) return; _done = value; // fire event RaisePropertyChanged("Done"); } } private Int32 _time; public Int32 Time { get { return _time; } set { if (_time == value) return; _time = value; // fire event RaisePropertyChanged("Time"); } } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(String propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } } 

编辑
要重现,只需在设计器中设置Visible = false,或在数据绑定之前在构造函数中设置。
使用Add()方法的一个重载也会失败:

 this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done", true, DataSourceUpdateMode.OnPropertyChanged); 

我想隐藏控件的原因是我不希望用户在第一次显示表单时看到控件。


谢谢大家,我想我找到了解决方案:

只需在Form.Load()事件中设置Control.Visible = false。 在这种情况下,当显示表单时,控件不可见。

虽然,为什么MS以这种方式设计数据绑定仍然是未知的。

我之前遇到过这种情况 。 在控件第一次可行之前,一些后端初始化永远不会发生,初始化的一部分是启用数据绑定。 在数据绑定工作之前,必须调用CreateControl(true) 。 但是,该方法是受保护的方法,因此您必须通过reflection或扩展控件来执行此操作。

通过反思:

 private static void CreateControl( Control control ) { var method = control.GetType().GetMethod( "CreateControl", BindingFlags.Instance | BindingFlags.NonPublic ); var parameters = method.GetParameters(); Debug.Assert( parameters.Length == 1, "Looking only for the method with a single parameter" ); Debug.Assert( parameters[0].ParameterType == typeof ( bool ), "Single parameter is not of type boolean" ); method.Invoke( control, new object[] { true } ); } 

所有事件都将延迟,直到控件的Created设置为true。

尝试使用此Add重载:

 this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done", true, DataSourceUpdateMode.OnPropertyChanged); 

更新的代码

使用您的问题中给出的代码对我有用。

  private WorkStatus m_WorkStatus = new WorkStatus(); public Form1() { InitializeComponent(); this.checkBox_WorkDone.Visible = true; this.checkBox_WorkDone.DataBindings.Add("Visible", m_WorkStatus, "Done"); } private void btnToggle_Click(object sender, EventArgs e) { m_WorkStatus.Done = !m_WorkStatus.Done; } 

您可以在绑定之前将Control设置为visible = true。

如果控件是不可见的,我们执行以下代码它也会起作用:

  this.checkBox_WorkDone.DataBindings.Add("Visible", m_WorkStatus, "Done"); // Binding does not work till Visible is set to true once. this.checkBox_WorkDone.Visible = true; 

不需要DataSourceUpdateMode.OnPropertyChanged ! 当WorkStatus对象具有Done = false它不会显示控件但会触发VisibleChanged事件。

我创建了一个测试工具(见下文),并尝试了您的代码。 我需要使用Add方法的重载来设置DataSourceUpdateMode.OnPropertyChanged

 public partial class Form1 : Form { private readonly WorkStatus _status = new WorkStatus(); public Form1() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { var t = new Timer(); t.Interval = 1000; t.Tick += (s, ea) => { _status.Done = true; t.Enabled = false; }; t.Enabled = true; checkBox_WorkDone.DataBindings.Add("Visible", _status, "Done", true, DataSourceUpdateMode.OnPropertyChanged); base.OnLoad(e); } } 

编辑:如果您从窗体的构造函数中删除setter,这可以正常工作。 如果在窗体的构造函数中将可见性设置为false,则此绑定将无法更新。 如果您的数据绑定正常工作,则没有理由手动指定初始可见性。 这首先打破了数据绑定的目的。

您可以做的是使控件可见,并在绑定更改后再次使其不可见。

 this.checkBox_WorkDone.Visible = true; this.checkBox_WorkDone.BindingContextChanged += (object sender, EventArgs e) => { this.checkBox_WorkDone.Visible = false; }; 

不是很漂亮,但它有效。

我知道这有点晚了,但是我遇到了同样的问题 – 当显示表单时,我要绑定的控件设置为visible = false。 我可能想在很多forms上这样做,我总是不愿意为每个绑定编写代码的reems。

所以我把一个小黑客放在一起。

我有一个带有面板的表单,我在构造函数中将其设置为Visible = false。 我想将视图绑定到我写的自定义视图模型。 在表单中,我从工具箱中放入一个BindingSource。 我为我的视图模型的Project Data源绑定源的DataSource。

然后想法迭代表单上的控件,并从数据源(即视图模型)更新控件绑定值。

为此,我存储控件的可见值,将其设置为false,并读取绑定值。 然后恢复初始可见值。 这是在适当命名的方法HackIt()中完成的。

这是代码:

视图模型

 public class SimpleViewModel // : DomainModelBase - add your notify property changed { public SimpleViewModel() { _visible = true; } private bool _visible; public bool Visible { get { return _visible; } set { _visible = value; OnPropertyChanged("Visible"); } } } 

表单设计师代码

 partial class Form1 { ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.panel1 = new System.Windows.Forms.Panel(); this.bindingSource1 = new System.Windows.Forms.BindingSource(this.components); this.button1 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).BeginInit(); this.SuspendLayout(); // // panel1 // this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0))))); this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("Visible", this.bindingSource1, "Visible", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); this.panel1.Location = new System.Drawing.Point(94, 85); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(200, 100); this.panel1.TabIndex = 0; // // bindingSource1 // this.bindingSource1.DataSource = typeof(WindowsFormsBindVisible.SimpleViewModel); // // button1 // this.button1.Location = new System.Drawing.Point(74, 34); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 1; this.button1.Text = "button1"; this.button1.UseVisualStyleBackColor = true; // // button2 // this.button2.Location = new System.Drawing.Point(155, 34); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(75, 23); this.button2.TabIndex = 2; this.button2.Text = "button2"; this.button2.UseVisualStyleBackColor = true; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(500, 261); this.Controls.Add(this.button2); this.Controls.Add(this.button1); this.Controls.Add(this.panel1); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Panel panel1; private System.Windows.Forms.BindingSource bindingSource1; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; } 

表格代码

 public partial class Form1 : Form { public SimpleViewModel ViewModel = new SimpleViewModel(); public Form1() { InitializeComponent(); this.panel1.Visible = false; this.bindingSource1.DataSource = this.ViewModel; } private void Form1_Load(object sender, EventArgs e) { HackIt(); } void HackIt() { this.SuspendLayout(); foreach(Control control in this.Controls) { var v = control.Visible; control.Visible = true; foreach(Binding db in control.DataBindings) { db.ReadValue(); } control.Visible = v; } this.ResumeLayout(); } } 

使用上面的代码,表单启动并显示我的控件。 您可以更改视图模型构造函数,默认为false以隐藏。 无论哪种方式都可以。

在表单构造函数中 – 我想显式隐藏面板(this.panel1.Visible = false) – 只是为了在视图模型默认visible = true时certificate绑定,控件在加载时正确显示。

然后,我们可以使按钮更改视图模型上的可见,这将切换面板的可见状态:

  private void button1_Click(object sender, EventArgs e) { this.ViewModel.Visible = false; } private void button2_Click(object sender, EventArgs e) { this.ViewModel.Visible = true; } 

UPDATE

这让我超越了第一道障碍。 但是,我正在使用Telerik组件,因此我决定在表单上删除Telerik控件。 那完全打破了一切。

而不是上面的HackIt方法,在load事件中调用以下RefreshDataBindings()。

我决定遍历表单上的所有控件并手动更新绑定reflection方式。 这太疯狂了! 但它100%工作 – 即使我的表格上有Telerik控件。 我的主要应用程序的性能还可以。 这是一个右下角的黑客 – 但我把它放在基本forms或基本控制中 – 我并不担心我的绑定。

 protected void RefreshDataBindings() { foreach (Control control in this.Controls) RefreshControlBindingsRecursive(control); } private void RefreshControlBindingsRecursive(Control control) { if (!control.Visible || !control.Created) { foreach (Binding db in control.DataBindings) { if (db.PropertyName == "Visible") { try { object dataSource = db.DataSource is BindingSource ? (db.DataSource as BindingSource).DataSource : db.DataSource; PropertyInfo pi = dataSource.GetType().GetProperty(db.BindingMemberInfo.BindingMember); ; PropertyInfo piC = db.Control.GetType().GetProperty(db.PropertyName); piC.SetValue(db.Control, pi.GetValue(dataSource)); } catch (Exception ex) { string s = ""; // not bothered its too late at night } } } } foreach (Control child in control.Controls) RefreshControlBindingsRecursive(child); } 

我的解决方法: private void Form_Load(object sender, EventArgs e) { button.Visible = true; button.DataBindings["Visible"].ReadValue(); } private void Form_Load(object sender, EventArgs e) { button.Visible = true; button.DataBindings["Visible"].ReadValue(); }

button.Visible = true; 需要强制创建控件(换句话说,它是创建窗口句柄)。

因为在创建控件之前数据绑定不起作用。 所以首先创建控制。

然后通过调用Binding.ReadValue()从数据源重新加载实际的Visible值。