WPF 自定義依賴屬性


DependencyObject和DependencyPorperty兩個類是WPF屬性系統的核心。

在WPF中,依賴對象的概念被DependencyObject類實現;依賴屬性的概念則由DependencyPorperty類實現。

必須使用依賴對象作為依賴屬性的宿主,二者結合起來,才能實現完整的Binding目標被數據所驅動。DependencyObject具有GetValue和SetValue兩個方法,用來獲取/設置依賴屬性的值。

DependencyObject是WPF系統中相當底層的一個基類,如下:

從這顆繼承樹可以看出,WPF的所有UI控件都是依賴對象。WPF的類庫在設計時充分利用了依賴屬性的優勢,UI空間的餓絕大多數屬性都已經依賴化了。

下面用一個簡單的實例來說明依賴屬性的使用方法。先准備好一個界面,順便復習下前面的Style和Template:

<Window x:Class="DependencyObjectProperty.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style x:Key="textStyle" TargetType="{x:Type TextBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <TextBlock Background="CadetBlue" Foreground="HotPink"  Text="{TemplateBinding Property=Text}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TextBox}">
                                <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                                    <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Background="AliceBlue"/>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <TextBox Style="{StaticResource textStyle}" Height="37" Name="textBox1" FontSize="26"    Margin="5" Width="439" />
        <TextBox Style="{StaticResource textStyle}" Height="37" Name="textBox2" FontSize="26"  Margin="5" Width="439" />
        <Button Content="Button" Height="39" Name="button1" Width="131" Click="button1_Click" />
    </StackPanel>
</Window>

前面說過,DependencyProperty必須以DependencyObject為宿主、借助它的SetValue和GetValue方法進行寫入和讀取。因此,想用自定義的DependencyProperty,宿主一定是DependencyObject的派生類。

DependencyProperty實例的聲明特點很明顯:變量由public static readonly三個修飾符修飾,實例使用DependencyProperty.Register方法生成。而非new操作符得到。

代碼如下:

using System.Windows;

namespace DependencyObjectProperty
{
    class Student:DependencyObject
    {
        //定義依賴屬性
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
    }
}

這是自定義DependencyProperty的最簡單代碼。
依賴屬性也是屬性,下面來使用它:

private void button1_Click(object sender, RoutedEventArgs e)
{
   Student stu = new Student();
   stu.SetValue(Student.NameProperty, textBox1.Text);
   textBox2.Text = (string)stu.GetValue(Student.NameProperty);
}

在textBox1中輸入值,點下Button1后效果如下:

 

上面我們使用的依賴屬性是靠GetValue和SetValue進行對外的暴露,而且在GetValue的時候需要進行類型的轉換,因此,在大多數的情況下我們會為依賴屬性添加一個CLR屬性的外包裝:

using System.Windows;

namespace DependencyObjectProperty
{
    class Student:DependencyObject
    {
        //CLR屬性進行封裝
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }
        
        //定義依賴屬性
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
    }
}

有了這個CLR屬性包裝,我們就可以和CLR屬性一樣訪問依賴屬性了:

private void button1_Click(object sender, RoutedEventArgs e)
{
   Student stu = new Student();
   stu.Name = textBox1.Text;
   textBox2.Text = stu.Name;            
}

如果不關心底層的實現,下游的程序員在使用依賴屬性時與使用單純的CLR屬性別無二致。

效果和上面相同:

 

 當然如果不用Binding,依賴屬性的設計就沒有意義,下面我們使用Binding把Student對象關聯到textBox1上,再把textBox2關聯到Student對象上。代碼如下: 

private void button1_Click(object sender, RoutedEventArgs e)
{
   Student stu = new Student();
Binding binding = new Binding("Text") { Source = textBox1 }; BindingOperations.SetBinding(stu, Student.NameProperty, binding); Binding binding2 = new Binding("Name") { Source = stu }; BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2); }

當然第二個Binding也可以這樣寫,下面兩者等效:

Binding binding2 = new Binding("Name") { Source = stu };
BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2);
textBox2.SetBinding(TextBox.TextProperty, binding2);

也可以在Student類中封裝FrameworkElement類的SetBinding方法,如下:

using System.Windows;
using System.Windows.Data;

namespace DependencyObjectProperty
{
    class Student:DependencyObject
    {
        //CLR屬性進行封裝
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }
        
        //定義依賴屬性
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));


        //SetBinding包裝
        public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
        {
            return BindingOperations.SetBinding(this, dp, binding);
        }
    }
}

則Binding可進一步寫成這樣:

private void button1_Click(object sender, RoutedEventArgs e)
{
   Student stu = new Student();
   stu.SetBinding(Student.NameProperty, new Binding("Text") { Source=textBox1 });
   textBox2.SetBinding(TextBox.TextProperty, new Binding("Name") { Source=stu});
}

效果如下:

//---------------------------------------------------------

自定義依賴屬性也可以不需要手動進行聲明、注冊並使用CLR屬性進行封裝,只需要輸入propdp,同時連按兩次Tab,一個標准的依賴屬性(帶CLR屬性包裝)就聲明好了。

prop:CLR屬性

propa:附加屬性

propdp:依賴屬性

附加屬性也是一種特別的依賴屬性,顧名思義,附加屬性是說一個屬性本來不屬於某個對象,但是由於某種需求而被后來附加上。也就是把對象放入一個特定的環境后對象才具有的屬性,比如Canvas.Left DockPanel.Dock Grid.Column等。

聲明時一樣用public static readonly三個關鍵詞修飾。唯一不同就是注冊附加屬性使用的是名為RegisterAttached的方法,但參數與Register方法相同。附加屬性的包裝器也與依賴屬性不同,依賴屬性使用CLR屬性對GetValue和SetValue兩個方法進行包裝,附加屬性則使用兩個方法分別進行包裝。

其可由propa+tab+tab方便的生成。理解附加屬性的意義及使用場合即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM