[自用笔记] 简单数据与简单WinForm控件的数据绑定初探
啊,之前只用DataGridView跟DataSet做过数据绑定,还没试过其它控件跟些比较简单的数据的绑定时怎样的。一直以为都是跟DataGridView+DataSet一样能实现双向的自动更新……结果貌似不是的。
先来看程序代码:
Form1.cs
using System;using System.Windows.Forms;namespace TestControlBinding{ public partial class Form1 : Form { public Form1( ) { m_Message = "alpha"; InitializeComponent( ); } public string Message { get { return this.m_Message; } set { this.m_Message = value; } } private string m_Message; private void button1_Click( object sender, EventArgs e ) { this.Message += "Append"; this.form1BindingSource.ResetBindings( false ); // update the bound controls } }}
这……好吧这整一dummy class。几乎什么都没有。就一个string类型的Message属性,和一个奇怪的ResetBindings()调用。
因为诡异的部分都是designer自动生成的……:
Form1.designer.cs
namespace TestControlBinding{ 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.textBox1 = new System.Windows.Forms.TextBox( ); this.form1BindingSource = new System.Windows.Forms.BindingSource( this.components ); this.button1 = new System.Windows.Forms.Button( ); this.label1 = new System.Windows.Forms.Label( ); ( ( System.ComponentModel.ISupportInitialize ) ( this.form1BindingSource ) ).BeginInit( ); this.SuspendLayout( ); // // textBox1 // this.textBox1.DataBindings.Add( new System.Windows.Forms.Binding( "Text", this.form1BindingSource, "Message", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged ) ); this.textBox1.Location = new System.Drawing.Point( 12, 12 ); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size( 268, 21 ); this.textBox1.TabIndex = 0; // // form1BindingSource // this.form1BindingSource.DataSource = this; this.form1BindingSource.Position = 0; // // button1 // this.button1.Location = new System.Drawing.Point( 12, 68 ); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size( 268, 23 ); this.button1.TabIndex = 1; this.button1.Text = "Append Message"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler( this.button1_Click ); // // label1 // this.label1.AutoSize = true; this.label1.DataBindings.Add( new System.Windows.Forms.Binding( "Text", this.form1BindingSource, "Message", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged ) ); this.label1.Location = new System.Drawing.Point( 12, 44 ); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size( 0, 12 ); this.label1.TabIndex = 2; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 12F ); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size( 300, 103 ); this.Controls.Add( this.label1 ); this.Controls.Add( this.button1 ); this.Controls.Add( this.textBox1 ); this.Name = "Form1"; this.Text = "Test Simple Data Binding"; ( ( System.ComponentModel.ISupportInitialize ) ( this.form1BindingSource ) ).EndInit( ); this.ResumeLayout( false ); this.PerformLayout( ); } #endregion private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.BindingSource form1BindingSource; private System.Windows.Forms.Button button1; private System.Windows.Forms.Label label1; }}
关注点是第33、38、45-46、62、78、87这几行。
这几行与一个BindingSource实例的操作相关。将Form1里的Message属性分别与textBox1.Text和label1.Text绑定在了一起。这个BindingSource实例是简单属性与简单控件的可绑定属性之间的桥梁,将图形界面与背后的实际数据联系在一起。注意到Visual Studio的visual designer为Object的data source生成的代码是将BindingSource的DataSource指向typeof ...,而不是一个实例。那样在这里是行不通的。这里得写为this...
那么通过标准的那个启动程序来运行下这个程序:
Program.cs
using System;using System.Windows.Forms;namespace TestControlBinding{ static class Program { /// <summary> /// The main entry point for the application. /// </summary> static void Main( ) { Application.EnableVisualStyles( ); Application.SetCompatibleTextRenderingDefault( false ); Application.Run( new Form1( ) ); } }}
会看到在TextBox里的输入都被同步反应在了Label上。这些数据也同时被自动更新到了Form1实例里的Message属性上。
按下Append Message的按钮,可以看到TextBox与Label的文本都更新了。这却不是自动的,而是在最开始看到的event handler里的那句:
this.form1BindingSource.ResetBindings( false ); // update the bound controls
也就是说,在这种简单的数据绑定下,控件能将数据更新到数据源上,而数据源的值得改变却不能自动更新到控件上。只好手动调用ResetBindings来更新数据。
============================================================
另外一个例子,在System.Windows.Forms.ListControl里有DisplayMember和ValueMember这两个有趣的属性,类型都是string,用于指定相应绑定在这个ListControl上的data source object里成员的名字。毫无疑问这里是用了反射来实现的绑定访问。有意思……
============================================================
要让一个对象将自己的更新通知到绑定的控件上,就得实现System.ComponentModel.INotifyPropertyChanged接口。当然我们并不总想在自己的类型里每个property的setter里都加上个OnPropertyChange()的调用,这个时候可以考虑用些模板包装类。不过……还是得看具体情况到底怎么做比较方便吧。
一个简单的实现:
public class SomePlainObject : INotifyPropertyChanged{ private string m_message; public string Message { get { return this.m_message; } set { this.m_message = value; OnPropertyChanged( this, new PropertyChangedEventArgs( this.m_message ) ); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged( object sender, PropertyChangedEventArgs args ) { PropertyChangedEventHandler handler = PropertyChanged; if ( handler != null ) handler( sender, args ); }}
于是这样绑定到BindingSource时就能实现Message的改变自动更新到data bound control的功能了。代价是对data object有侵入性……
嗯,顺便记几个地址方便以后查询:
More thoughts on more thoughts on XAML, C# and WPF
WPF来了。让WinForms慢慢退休吧 ^ ^
页:
[1]