WPF依赖属性的正确学习方法

前言html

我在学习WPF的早期,对依赖属性理解一直都很是的不到位,其恶果就是,我每次在写依赖属性的时候,须要翻过去的代码来复制黏贴。git

相信不少朋友有着和我相同的经历,因此这篇文章但愿能帮助到那些刚刚开始学依赖属性的朋友。github

那些[讨厌]的依赖属性的讲解文章ide

初学者确定会面临一件事,就是百度,谷歌,或者MSDN来查看依赖属性的定义和使用,而这些文章虽然都写的很好,但,那是相对于已经学会使用依赖属性的朋友而言。学习

而对于初学者而言,说是误导都不过度。this

好比,官网的这篇文章https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/advanced/dependency-properties-overviewspa

介绍依赖属性是这样。htm

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

他作了一个定义,而后告诉你,依赖属性的定义格式如此。对象

若是你是个初学者,你想不疑惑都很难。由于没人能把这种定义给背下来。blog

其结果就是,你要和我当初同样,每次定义依赖属性,都要去复制黏贴。但这并非最大的恶果,最大的恶果是,由于太过复杂的定义,让你放弃了对他理解,就记住了依赖属性要复制黏贴,从而致使了,你丧失了对依赖属性灵活运用的能力。

正确的理解依赖属性

如何正确的理解依赖属性呢?

很简单,拆分一下就能够理解了。

如今咱们来拆分依赖属性,首先拆分他的定义,将依赖和属性拆分。

咱们先看属性,以下,咱们定义了一个属性。

private bool _IsSpinning;
public bool IsSpinning
{
    get { return _IsSpinning; }
    set { _IsSpinning = value; }
}

而后咱们使用DependencyProperty类定义一个对象,这个对象将做为IsSpinning属性的依赖,以下:

public static readonly DependencyProperty IsSpinningProperty

而后,咱们在将这个依赖对象,注册到属性IsSpinning的所在类上,以下:

DependencyProperty.Register( "IsSpinning", typeof(bool),  typeof(你的属性所在的类的名称));

从注册代码中,咱们能够看到,他注册了三个信息:

1,当前DependencyProperty类定义的对象IsSpinningProperty,依赖于属性IsSpinning。

2,对象IsSpinningProperty的依赖类型与属性IsSpinning的类型同样都是bool。

3,对象IsSpinningProperty注册的类是声明属性IsSpinning的类,即,在其余类里,将看不到该依赖对象。

如今,咱们作最后的操做,修改属性,将依赖对象IsSpinningProperty与属性IsSpinning绑定。

如何绑定呢?很简单,将咱们属性定义里的【private bool _IsSpinning】替换为咱们刚刚定义的依赖【IsSpinningProperty】便可。

public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

这里咱们看到了,在给属性赋值和取值时,用到了GetValue和SetValue,他们俩是哪来的呢?

使用F12,咱们跟踪进去,发现它们是类DependencyProperty里定义的方法,那么为何咱们在窗体里也能够用呢?

很简单,咱们跟进一下Window的父类,发现最后的父类Visual继承了DependencyProperty,因此咱们能够直接使用GetValue和SetValue来赋值和获取依赖对象的值。也就是只要是继承了类DependencyProperty的子类,均可以使用依赖属性。

完整版依赖属性定义代码:

public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register("IsSpinning", typeof(bool), typeof(DependecyUserControl));
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

到这里,依赖属性的拆分就完事了,如今,你们应该很清楚依赖属性究竟是什么了吧。

如今你已经理解这些依赖属性的概念了,只要熟练一点点,实现手敲依赖属性已经不是梦了。 

PS:有没有人曾经告诉你,依赖属性的命名必须是 属性名+Property,而后你还信觉得真了。哈哈。

依赖属性的简单应用

如今让咱们来自定义一个带依赖属性的系统控件来加深记忆。

public class KButton : Button 
{
    public static readonly DependencyProperty ForeImageProperty;
    public static readonly DependencyProperty BackImageProperty;
    public static readonly DependencyProperty MouseOverBackColorProperty;
    public static readonly DependencyProperty StretchProperty; 
    static KButton()
    {
        ForeImageProperty = DependencyProperty.Register("ForeImage", typeof(string), typeof(KButton),null);
        ForeImageProperty = DependencyProperty.Register("BackImage", typeof(string), typeof(KButton),null);
        MouseOverBackColorProperty = DependencyProperty.Register("MouseOverBackColor", typeof(Brush), typeof(KButton), null);
        StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(KButton), null);

        DefaultStyleKeyProperty.OverrideMetadata(typeof(KButton), new FrameworkPropertyMetadata(typeof(KButton)));//使KButton去读取KButton类型的样式,而不是去读取Button的样式
    }
   
    public string ForeImage
    {
        get { return (string)GetValue(ForeImageProperty); }
        set { SetValue(ForeImageProperty, value); }
    }
    public string BackImage
    {
        get { return (string)GetValue(BackImageProperty); }
        set { SetValue(BackImageProperty, value); }
    }
    public Brush MouseOverBackColor
    {
        get { return (Brush)GetValue(MouseOverBackColorProperty); }
        set { SetValue(MouseOverBackColorProperty, value); }
    }
    public Stretch Stretch
    {
        get { return (Stretch)GetValue(StretchProperty); }
        set { SetValue(StretchProperty, value); }
    } 
}

如上述代码所示,咱们定义了一个继承至Button的类KButton。

在KButtion中,咱们定义了四个依赖属性:

ForeImageProperty:按钮的前景图片。

BackImageProperty:按钮的背景图片。

MouseOverBackColorProperty:按钮在鼠标通过时的颜色。

StretchProperty:按钮图片的拉伸模式。

代码很是简洁,除了四个依赖属性以外,什么也没有;如今咱们去定义Kbutton类型的样式。

为了演示方便,我直接将样式定义在了App.xaml文件内。

        <Style TargetType="{x:Type local:KButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel Name="dpCon" Width="{Binding Width, RelativeSource={x:Static RelativeSource.TemplatedParent}}" 
                               Height="{Binding Height, RelativeSource={x:Static RelativeSource.TemplatedParent}}"
                               Background="{Binding Background, RelativeSource={x:Static RelativeSource.TemplatedParent}}"
                               ToolTip="{Binding ToolTip, RelativeSource={x:Static RelativeSource.TemplatedParent}}"
                               >
                            <DockPanel DockPanel.Dock="Top" Name="dpBtn">
                                <DockPanel.Background>
                                    <ImageBrush ImageSource="{Binding ForeImage, RelativeSource={x:Static RelativeSource.TemplatedParent}}" Stretch="{Binding Stretch,RelativeSource={x:Static RelativeSource.TemplatedParent}}"/>
                                </DockPanel.Background>
                                <TextBlock FontSize="15" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#f9fcff" Text="{Binding Content, RelativeSource={x:Static RelativeSource.TemplatedParent}}"></TextBlock>
                            </DockPanel>
                        </DockPanel>
                        <ControlTemplate.Triggers>  
                            <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={x:Static RelativeSource.Self}}" Value="True">
                                <Setter Property="Background" TargetName="dpBtn">
                                    <Setter.Value>
                                        <ImageBrush ImageSource="{Binding BackImage, RelativeSource={x:Static RelativeSource.TemplatedParent}}" Stretch="{Binding Stretch,RelativeSource={x:Static RelativeSource.TemplatedParent}}"/>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Background" TargetName="dpCon" Value="{Binding MouseOverBackColor, RelativeSource={x:Static RelativeSource.TemplatedParent}}"></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding BackImage,RelativeSource={x:Static RelativeSource.Self},Mode=TwoWay}" Value="{x:Null}">
                                <Setter Property="Background" TargetName="dpBtn">
                                    <Setter.Value>
                                        <ImageBrush ImageSource="{Binding ForeImage, RelativeSource={x:Static RelativeSource.TemplatedParent}}" Stretch="{Binding Stretch,RelativeSource={x:Static RelativeSource.TemplatedParent}}"/>
                                    </Setter.Value>
                                </Setter> 
                            </DataTrigger>
                            <Trigger Property="IsEnabled" Value="true"/>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="Gray"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

样式代码如上所示,也很是简单,就是定义了一个模板,而后在模板里摆放好按钮背景图和按钮文字的位置。而后将咱们以前定义好的依赖属性绑定到对应的值上。

其中须要注意的是,在模板中绑定自定义依赖属性,是使用RelativeSource.TemplatedParent的,如{Binding ForeImage, RelativeSource={x:Static RelativeSource.TemplatedParent}}。

而在模板的数据事件DataTrigger中,绑定依赖属性的模式倒是分两种的。

第一种,绑定数据事件DataTrigger的条件时,使用RelativeSource.Self,如{Binding IsMouseOver,RelativeSource={x:Static RelativeSource.Self}}。

第二种,条件成立,触发模板变化时,使用RelativeSource.TemplatedParent,如{Binding BackImage, RelativeSource={x:Static RelativeSource.TemplatedParent}}。

----------------------------------------------------------------------------------------------------

如今咱们使用下咱们制做好的自定义控件,代码以下所示:

<DockPanel>
    <StackPanel>
        <local:KButton  Height="50" Width="50" Stretch="None" ForeImage="/Image/关闭.png" BackImage="/Image/关闭退出.png" Background="Gray" MouseOverBackColor="Brown"/>
        <local:KButton  Height="50" Width="50" Margin="0,10,0,0" Stretch="None" ForeImage="/Image/关闭.png" Background="Gray" MouseOverBackColor="Brown"/>
        <local:KButton  Height="100" Width="100" Margin="0,10,0,0" Content="篮子" Stretch="Fill"  ForeImage="/Image/篮子.png" Background="Gray" MouseOverBackColor="Brown"/>
    </StackPanel> 
</DockPanel>

界面效果以下:

自定义用户控件中使用依赖属性

首先咱们添加新项,而后选择用户控件。

而后,咱们添加一个依赖属性HeaderTitle,同时设置当前控件的DataContext为自身—this.DataContext = this。

public string HeaderTitle
{
    get { return (string)GetValue(HeaderTitleProperty); }
    set { SetValue(HeaderTitleProperty, value); }
} 
public static readonly DependencyProperty HeaderTitleProperty = DependencyProperty.Register("HeaderTitle", typeof(string), typeof(DependecyUserControl), null); 
public DependecyUserControl()
{
    this.DataContext = this;
    InitializeComponent();
}

如今,咱们在用户控件的Xaml页面添加一个TextBlock,并绑定他的Text为咱们刚刚定义的HeaderTitle,代码以下所示。

<Grid>
    <TextBlock Text = "{Binding HeaderTitle}" TextAlignment="Center"></TextBlock>
</Grid>

接着咱们回到主窗体,引用这个用户控件,代码以下所示:

<local:DependecyUserControl Height = "30" HeaderTitle="我是Header"  DockPanel.Dock="Top"></local:DependecyUserControl>

运行结果:

能够看到,咱们成功在主页面设置了用户控件的依赖属性,并让他成功的绑定到了用户控件中的TextBlock的Text属性。也就是说,咱们简单的实现了Header的Title动态设置。

结语

WPF拥有很是强大的自定义能力,而,正确的学会了依赖属性是体会到它强大的第一步。

----------------------------------------------------------------------------------------------------

到此WPF依赖属性的正确学习方法就已经讲解完成了。

代码已经传到Github上了,欢迎你们下载。

Github地址:https://github.com/kiba518/WpfDependency

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文连接!
若您以为这篇文章还不错,请点击下方的推荐】,很是感谢!

http://www.javashuo.com/article/p-qgwhsjox-dk.html

相关文章
相关标签/搜索