在使用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以这种方式设计数据绑定仍然是未知的。
答案 0 :(得分:10)
我跑到this exact situation before。在控件第一次可行之前,一些后端初始化永远不会发生,初始化的一部分是启用数据绑定。在数据绑定工作之前,您必须调用CreateControl(true)
。但是,该方法是受保护的方法,因此您必须通过反射或扩展控件来执行此操作。
通过反思:
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。
答案 1 :(得分:1)
尝试使用此Add
重载:
this.checkBox_WorkDone.DataBindings.Add("Visible", WorkStatus, "Done",
true, DataSourceUpdateMode.OnPropertyChanged);
答案 2 :(得分:1)
更新代码
根据您的问题中给出的代码,这对我有用。
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
事件。
答案 3 :(得分:1)
我创建了一个测试工具(见下文),并尝试了您的代码。我需要使用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,则此绑定将无法更新。如果您的数据绑定正常工作,则没有理由手动指定初始可见性。这首先真的打败了数据绑定的目的。
答案 4 :(得分:1)
您可以做的是使控件可见,并在绑定更改后再次使其不可见。
this.checkBox_WorkDone.Visible = true;
this.checkBox_WorkDone.BindingContextChanged += (object sender, EventArgs e) => {
this.checkBox_WorkDone.Visible = false;
};
不是很漂亮,但它确实有效。
答案 5 :(得分:1)
我知道这有点晚了,但是我遇到了同样的问题 - 我想要绑定的控件在显示表单时设置为visible = false。我可能想在很多形式上做这个,而且我总是不愿意为每个绑定写代码。
所以我把一个小黑客放在一起。
我有一个带有面板的表单,我在构造函数中将其设置为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
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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时证明绑定,控件在加载时正确显示。
然后我们可以让按钮更改视图模型上的可见项,这将切换面板的状态:
private void button1_Click(object sender, EventArgs e)
{
this.ViewModel.Visible = false;
}
private void button2_Click(object sender, EventArgs e)
{
this.ViewModel.Visible = true;
}
<强>更新强>
这让我超越了第一道障碍。但是,我正在使用Telerik组件,因此我决定在表单上删除Telerik控件。这完全打破了一切。
而不是上面的HackIt方法,在load事件中调用以下RefreshDataBindings()。
我决定遍历表单上的所有控件,并以反射方式手动更新绑定。这太疯狂了!但它100%工作 - 即使我的表格上有Telerik控件。我的主要应用程序的性能还可以。这是一个右下角的黑客 - 但我把它放在基本形式或基本控制中 - 我并不担心我的绑定。
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);
}
答案 6 :(得分:0)
我的解决方法:
private void Form_Load(object sender, EventArgs e)
{
button.Visible = true;
button.DataBindings["Visible"].ReadValue();
}
button.Visible = true;
来强制创建控件(换句话说,它是创建窗口句柄)。
因为在创建控件之前数据绑定将不起作用。因此,首先创建控件。
然后通过调用Visible
从数据源重新加载实际的Binding.ReadValue()
值。