2012年12月25日 星期二

WPF DataBinding 資料繫結簡介

我想有兩種狀況會有這個需求。

1.如果你需要資料跟UI元件能即時隨時的互動,隨著資料改變,UI的顯示就跟著變(或者反向),這時候,你就需要他。

2.如果你需要在Thread或Task中顯示目前進行的狀況或是任何資訊在某種UI元件上,比方Label, TextBlock,為了避免跨執行緒UI操作這種神秘又討厭的Exception,而你又不想為此作Delegate,這時候,你也可以靠他幫助。


在WPF上的作法有兩種,一種是把Bnding寫在 XML 裡,一種是寫在C#程式裡,差異不大,因為最後資料的顯示也都用程式在控制居多。

首先,建立一個WPF 應用程式專案,然後在視窗上放一個按鈕、一個TextBlock、一個TextBox。名稱Name分別取為Btn_Bind、TxtBlk_Bind、TxtBx_Bind。位置隨便你放,能看得到就好了。

接下來,在程式當中,新增一個繼承INotifyPropertyChanged的Class,並且給他一個Member(當然,如果你有多個元件要跟多組數值作Binding,這裡就可以有多個Member),固定寫法如下,當Member要寫入新值,在set當中呼叫OnPropertyChanged(),他會產生PropertyChangedEventHandler 的事件,因此可讓UI可以知道該更新顯示了。


        public class MyData : INotifyPropertyChanged
        {
            private string m_TextMsg;
            public string TextMsg
            {
                set
                {
                    if ( m_TextMsg != value )
                    {
                        m_TextMsg = value;
                        OnPropertyChanged( "TextMsg" );
                    }
                }
                get
                {
                    return m_TextMsg;
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
            public void OnPropertyChanged( string propertyName )
            {
                if ( PropertyChanged != null )
                {
                    PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
                }
            }
        }



另外準備好按鈕的事件內容
        private void Bind_Click( object sender, RoutedEventArgs e )
        {
            m_MyData.TextMsg = "New Value";
        }



然後在CS檔中宣告一個全域變數,把你剛剛的INotifyPropertyChanged類型的Class宣告好

        private MyData m_MyData ;

然後在程式進入點函式中加入底下,產生物件實體與給予初始值
            m_MyData = new MyData();
            m_MyData .TextMsg = "Origin";



以上都準備好之後,開始進行Binding




第一種方法:在XML加入Binding 片段

XML會變成類似底下這樣:

<TextBlock x:Name="TxtBlk_Bind" HorizontalAlignment="Center" Margin="134,130,136,140" TextWrapping="Wrap" VerticalAlignment="Center" Height="50" Width="247" FontSize="20" Text="{Binding Path=TextMsg }" TextAlignment="Center"/>
        <TextBox x:Name="TxtBx_Bind" HorizontalAlignment="Left" Height="44" Margin="134,210,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="248" FontSize="20" Text="{Binding Path=TextMsg }"/>
        <Button x:Name="Btn_Bind" Content="改變數值" HorizontalAlignment="Left" Height="37" Margin="134,62,0,0" VerticalAlignment="Top" Width="99" Click="Bind_Click"/>



最後用程式告訴系統,你的Binding是哪一個


            TxtBlk_Bind.DataContext = m_BindData;
            TxtBx_Bind.DataContext = m_BindData;


(能不能把上面兩段寫成XAML呢?其實是可以的,可是,寫來頗複雜,而且還不知道要怎麼讓CS引用這個變數)


如此,你的TxtBlk_Bind跟TxtBx_Bind兩個元件就跟m_BindData 內的TextMsg 有了互動
不相信?你可以按下按鈕,就會看到TxtBlk_Bind跟TxtBx_Bind元件都變成了"New Value",而如果你在TxtBx_Bind輸入任何文字,然後按下[Tab]鍵,就可以看到TxtBlk_Bind也會跟著變成你輸入的東西了。(這是因為TxtBx_Bind輸入的數值會同步變更BindData內的TextMsg 數值,然後在同步更新到TxtBlk_Bind元件。




第二種方法:完全在程式中完成Binding

XML不用改,只要在程式當中加上底下四行:


            Binding BindingTxtBlk = new Binding() { Source = m_MyData , Path = new PropertyPath( "TextMsg" ) };
            TxtBlk_Bind.SetBinding( TextBlock.TextProperty, BindingTxtBlk );

            Binding BindingTxtBx = new Binding() { Source = m_MyData , Path = new PropertyPath( "TextMsg" ) };

            TxtBx_Bind.SetBinding( TextBox.TextProperty, BindingTxtBx );







太棒了,對吧!
我喜歡第二種,因為不用動到XML,感覺也挺直覺的。

2 則留言:

  1. 您好,我想用您第二種做法,
    去替換此網站的sample
    http://www.dotblogs.com.tw/ouch1978/archive/2011/07/29/wpf-globalization-xmldataprovider.aspx
    請問可以做得到嗎?
    若可以,麻煩可以提示一下要怎麼做嗎?
    謝謝您。

    回覆刪除
    回覆
    1. 應該就把我上面第二種作法的程式貼到MainWindow.xml.cs就好了

      但是事情準備工作:「以上都準備好之後,開始進行Binding」的那些準備工作應該還是要先寫好

      刪除