啊,之前只用DataGridView跟DataSet做过数据绑定,还没试过其它控件跟些比较简单的数据的绑定时怎样的。一直以为都是跟DataGridView+DataSet一样能实现双向的自动更新……
结果貌似不是的。
先来看程序代码:
Form1.cs
这……好吧这整一dummy class。几乎什么都没有。就一个string类型的Message属性,和一个奇怪的ResetBindings()调用。
因为诡异的部分都是designer自动生成的……:
Form1.designer.cs
关注点是第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 ...,而不是一个实例。那样在这里是行不通的。这里得写为[color=blue]this[/color]...
那么通过标准的那个启动程序来运行下这个程序:
Program.cs
会看到在TextBox里的输入都被同步反应在了Label上。这些数据也同时被自动更新到了Form1实例里的Message属性上。
按下Append Message的按钮,可以看到TextBox与Label的文本都更新了。这却不是自动的,而是在最开始看到的event handler里的那句:
也就是说,在这种简单的数据绑定下,控件能将数据更新到数据源上,而数据源的值得改变却不能自动更新到控件上。只好手动调用ResetBindings来更新数据。
============================================================
另外一个例子,在System.Windows.Forms.ListControl里有DisplayMember和ValueMember这两个有趣的属性,类型都是string,用于指定相应绑定在这个ListControl上的data source object里成员的[b]名字[/b]。毫无疑问这里是用了反射来实现的绑定访问。有意思……
============================================================
要让一个对象将自己的更新通知到绑定的控件上,就得实现System.ComponentModel.INotifyPropertyChanged接口。当然我们并不总想在自己的类型里每个property的setter里都加上个OnPropertyChange()的调用,这个时候可以考虑用些模板包装类。不过……还是得看具体情况到底怎么做比较方便吧。
一个简单的实现:
于是这样绑定到BindingSource时就能实现Message的改变自动更新到data bound control的功能了。代价是对data object有侵入性……
嗯,顺便记几个地址方便以后查询:
[url=http://www.pluralsight.com/blogs/dbox/archive/2007/05/10/47283.aspx]More thoughts on more thoughts on XAML, C# and WPF[/url]
WPF来了。让WinForms慢慢退休吧 ^ ^
结果貌似不是的。
先来看程序代码:
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 ...,而不是一个实例。那样在这里是行不通的。这里得写为[color=blue]this[/color]...
那么通过标准的那个启动程序来运行下这个程序:
Program.cs
using System;
using System.Windows.Forms;
namespace TestControlBinding
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
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里成员的[b]名字[/b]。毫无疑问这里是用了反射来实现的绑定访问。有意思……
============================================================
要让一个对象将自己的更新通知到绑定的控件上,就得实现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有侵入性……
嗯,顺便记几个地址方便以后查询:
[url=http://www.pluralsight.com/blogs/dbox/archive/2007/05/10/47283.aspx]More thoughts on more thoughts on XAML, C# and WPF[/url]
WPF来了。让WinForms慢慢退休吧 ^ ^