RednaxelaFX 发表于 2013-1-27 05:42:36

[自用笔记] 简单数据与简单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]
查看完整版本: [自用笔记] 简单数据与简单WinForm控件的数据绑定初探