• 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方便的生成。理解附加属性的意义及使用场合即可。

  • 相关阅读:
    USACO Spinning Wheels
    USACO Agri-Net
    NOIP 2009 最优贸易
    USACO Cow Contest
    USACO Subset Sums
    USACO Cow Cars
    USACO Making the Grade
    NOIP 2008 传纸条
    NOIP 2000 方格取数
    NOIP 1999 导弹拦截
  • 原文地址:https://www.cnblogs.com/DebugLZQ/p/2796021.html
Copyright © 2020-2023  润新知