`
RednaxelaFX
  • 浏览: 3019308 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

[自用笔记] 简单数据与简单WinForm控件的数据绑定初探

    博客分类:
  • C#
阅读更多
啊,之前只用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>
        [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里成员的名字。毫无疑问这里是用了反射来实现的绑定访问。有意思……

============================================================

要让一个对象将自己的更新通知到绑定的控件上,就得实现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 楼 shawind 2007-11-29  
以前用bcb和mdb写个这样的,xml格式的,不过在书写介面上是个难题,我想即使没有有Word那么NB,最少得有像论坛上发贴子的编辑器那样的功能吧。最后没能完成,放弃了....

相关推荐

Global site tag (gtag.js) - Google Analytics