服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#中WPF依赖属性的正确学习方法

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

2022-08-01 11:08Kiba518 C#

这篇文章主要介绍了C#中WPF依赖属性的正确学习方法 ,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

前言

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

相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学依赖属性的朋友。

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

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

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

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

介绍依赖属性是这样。

?
1
2
3
4
5
6
7
8
9
10
public static readonly DependencyProperty IsSpinningProperty =
  DependencyProperty.Register(
  "IsSpinning", typeof(Boolean),
  typeof(MyCode)
  );
public bool IsSpinning
{
  get { return (bool)GetValue(IsSpinningProperty); }
  set { SetValue(IsSpinningProperty, value); }
}

他做了一个定义,然后告诉你,依赖属性的定义格式如此。

如果你是个初学者,你想不疑惑都很难。因为没人能把这种定义给背下来。

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

正确的理解依赖属性

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

很简单,拆分一下就可以理解了。

现在我们来拆分依赖属性,首先拆分他的定义,将依赖和属性拆分。

我们先看属性,如下,我们定义了一个属性。

?
1
2
3
4
5
6
private bool _IsSpinning;
public bool IsSpinning
{
  get { return _IsSpinning; }
  set { _IsSpinning = value; }
}

然后我们使用DependencyProperty类定义一个对象,这个对象将作为IsSpinning属性的依赖,如下:

public static readonly DependencyProperty IsSpinningProperty

然后,我们在将这个依赖对象,注册到属性IsSpinning的所在类上,如下:

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

从注册代码中,我们可以看到,他注册了三个信息:

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

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

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

现在,我们做最后的操作,修改属性,将依赖对象IsSpinningProperty与属性IsSpinning绑定。

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

?
1
2
3
4
5
public bool IsSpinning
{
  get { return (bool)GetValue(IsSpinningProperty); }
  set { SetValue(IsSpinningProperty, value); }
}

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

使用F12,我们跟踪进去,发现它们是类DependencyProperty里定义的方法,那么为什么我们在窗体里也可以用呢?

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

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

?
1
2
3
4
5
6
7
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,然后你还信以为真了。哈哈。

依赖属性的简单应用

现在让我们来自定义一个带依赖属性的系统控件来加深记忆。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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文件内。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<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}}

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

现在我们使用下我们制作好的自定义控件,代码如下所示:

?
1
2
3
4
5
6
7
<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>

 

界面效果如下:

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
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,代码如下所示。

?
1
2
3
<Grid>
  <TextBlock Text = "{Binding HeaderTitle}" TextAlignment="Center"></TextBlock>
</Grid>

 

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

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

 

运行结果:

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

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

结语

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

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

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

代码已经传到Github上了,欢迎大家下载。

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

总结

以上所述是小编给大家介绍的C#中WPF依赖属性的正确学习方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

原文链接:https://www.cnblogs.com/kiba/p/11149147.html

延伸 · 阅读

精彩推荐
  • C#C#使用第三方组件生成二维码汇总

    C#使用第三方组件生成二维码汇总

    本文给大家汇总了几种C#使用第三方组件生成二维码的方法以及示例,非常的简单实用,都是项目中经常需要用到的,希望大家能够喜欢...

    C#教程网5432021-12-13
  • C#C#停止线程的方法

    C#停止线程的方法

    这篇文章主要介绍了C#停止线程的方法,实例分析了C#正确停止线程的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    我心依旧12132021-10-20
  • C#微信公众平台开发教程(三) 基础框架搭建

    微信公众平台开发教程(三) 基础框架搭建

    这篇文章主要介绍了微信公众平台开发教程(三) 基础框架搭建,具有一定的参考价值,有需要的可以了解一下。...

    停留的风7992021-12-11
  • C#C#中抽象类与接口的区别详解

    C#中抽象类与接口的区别详解

    本文主要介绍了C#中抽象类与接口的区别。具有很好的参考价值。下面跟着小编一起来看下吧...

    淡泊明志_宁静3742021-12-29
  • C#c#计算某段代码的执行时间实例方法

    c#计算某段代码的执行时间实例方法

    在本篇文章里我们给大家整理了关于c#计算某段代码的执行时间的方法和经验,有兴趣的朋友们学习下。...

    C#教程网11232022-07-07
  • C#C#编程实现带有Aero效果的窗体示例

    C#编程实现带有Aero效果的窗体示例

    这篇文章主要介绍了C#编程实现带有Aero效果的窗体,涉及C#调用动态链接库针对窗体属性的相关操作技巧,需要的朋友可以参考下...

    a7719485248412022-01-12
  • C#C#操作本地文件及保存文件到数据库的基本方法总结

    C#操作本地文件及保存文件到数据库的基本方法总结

    C#使用System.IO中的文件操作方法在Windows系统中处理本地文件相当顺手,这里我们还总结了在Oracle中保存文件的方法,嗯,接下来就来看看整理的C#操作本地文件...

    死神的丧钟6742021-11-22
  • C#WinForm限制窗体不能移到屏幕外的方法

    WinForm限制窗体不能移到屏幕外的方法

    这篇文章主要介绍了WinForm限制窗体不能移到屏幕外的方法,实例分析了C#中WinForm窗体操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    我心依旧9232021-10-20