0
点赞
收藏
分享

微信扫一扫

WPF 消息提示 类似toast方式

芷兮离离 03-09 12:30 阅读 1
wpf

文章目录

一、前言

最近写的博文知识点都比较基础。
因为写项目时总会遇到一些奇怪的问题,于是按我的性子,就会回过去学一遍。
这归根到底还是自己基础太差,兜兜转转半天还得回来补习。
这不,WPF都用了快两年了,又回来学习依赖属性了。

原文地址


二、依赖属性

WPF提供了一组可用于扩展类型属性功能的服务,该服务统称为WPF属性系统。
由WPF属性系统支持的属性称为依赖属性(dependency property)。

本文介绍WPF属性系统和依赖属性的功能,包括如何在XAML和代码中使用现有的依赖属性。
本概述还介绍了依赖属性特有的内容,例如依赖属性元数据、以及如何在自定义类中创建你自己的依赖属性。

2.1 前置知识

假设你已有.NET类型系统和面向对象编程的基础知识。
那么,按照本文中的示例进行操作,有助于你理解XAML和了解如何编写WPF应用程序。

2.2 依赖属性与CLR属性

WPF属性通常会公开为标准的.NET属性。

平时,你可能会与这些属性进行交互,但你或许并不知道它们底下是实现为了依赖属性的。而熟悉WPF属性系统的功能,有助于你更好地去使用它们。(从这可以看出来,对使用者来讲,依赖属性其实和CLR属性一样,只是内部实现上有差别)

依赖属性的目的是提供一种方式,基于其他输入值来计算属性值。输入值包括:

  • 系统属性,例如主题或用户首选项/偏好。
  • 即时(JIT)属性确定机制,例如数据绑定和动画、故事板。
  • 多用模板(multiple-use template),例如资源和样式。
  • 通过与元素树中的其他元素的父子关系得到的值。

此外,依赖属性可提供:

  • 独立的验证。
  • 默认值。
  • 监视其他属性更改的回调。
  • 可根据运行时信息强制属性值的系统。

派生类可以通过重写依赖属性的元数据来更改现有属性的某些特征,而不需要重写现有属性的现有实现或创建新属性。

在SDK参考文档中,你可以通过该属性的托管参考页上是否存在依赖属性信息段来辨别一个依赖属性。依赖属性信息段包含指向该依赖属性的 DependencyProperty标识符字段的链接。它还包括该属性的元数据选项列表,每个类的覆盖信息以及其他详细信息。

2.3 依赖属性支持CLR属性

依赖属性和WPF属性系统通过提供支持属性的类型来扩展属性功能,以作为使用私有字段支持属性的标准模式的替代方案。该类型的名称是 DependencyProperty 。定义WPF属性系统另一个重要类型是 DependencyObject ,它定义了可注册和拥有依赖属性的基类。

以下是一些常用术语:

  • 依赖属性 Dependency property ,这是由 DependencyProperty 支持的属性。
  • 依赖属性标识符 Dependency property identifier ,它是注册依赖属性时作为返回值获取的 DependencyProperty 实例,然后存储为类的静态成员。许多与WPF属性系统交互的API使用依赖属性标识符作为参数。
  • CLR包装器 CLR “wrapper” ,是属性的get和set实现。这些实现通过在 GetValueSetValue 调用中使用依赖属性标识符来做合并。这样,WPF属性系统就为属性提供了支持。

以下示例定义 IsSpinning依赖属性以展示 DependencyProperty 标识符与其支持属性的关系。

// IsSpinningProperty是依赖属性标识符
public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register("IsSpinning", typeof(bool), typeof(MainWindow));

// IsSpinning是依赖属性
public bool IsSpinning
{
	get => (bool)GetValue(IsSpinningProperty);
	Set => SetValue(IsSpinningProperty, value);
}

属性的命名规则及其支持的 DependencyProperty 字段非常重要。字段的名称始终是属性的名称,并加上后缀 Property

2.4 设置属性值

在代码和XAML中都可以设置属性。

2.4.1 XAML中设置属性值

下面的XAML示例将一个按钮的背景色设置为红色。XAML attribute的字符串值由WPF XAML解析器进行类型转换为WPF类型。在生成的代码中,WPF类型是由 SolidColorBrush 实现的Color。

<Button Content="I am red" Background="Red"/>

XAML支持多种设置属性的语法形式。某种属性使用哪种语法取决于属性使用的值类型以及一些其他因素,例如是否存在类型转换器。

下面的XAML示例展示了使用 属性元素语法(property element syntax) 来替换上述的attribute语法。XAML将按钮背景设置为图像而不是简单的纯色。嵌套元素代表图像,元素的attribute指定图像源。

<Button Content="I have an image background">
	<Button.Background>
		<ImageBrush ImageSource="stripes.jpg"/>
	</Button.Background>
</Button>

2.4.2 代码中设置属性

在代码中设置依赖属性值通常就是调用由CLR包装器公开的 set 实现:

Button myButton = new ();
myButton.Width = 200.0;

获取属性值本质上是调用 get 包装器的实现:

double whatWidth = myButton.Width;

你也可以直接调用属性系统的API GetValue 和 SetValue。直接调用API适用于某些场景,但通常不适用于使用已有的属性。一般来讲,包装器更便捷,并且为开发人员工具提供了更好的属性公开。

属性也可以在XAML中设置,然后通过后置代码访问。

2.5 依赖属性提供的属性功能

与由字段支持的属性不同,依赖属性扩展了属性的功能。通常,扩展的功能如下:

  • 资源 Resources
  • 数据绑定 Data binding
  • 样式 Styles
  • 动画 Animations
  • 元数据重写 Metadata overrides
  • 属性值继承 Property value inheritance
  • WPF设计器集成 WPF Designer integration

2.5.1 资源

可以通过引用一个资源来设置属性值。
资源通常被指定为页面根元素或应用程序的 Resources 属性值,因为这些位置提供了对资源的便捷访问。该例中,我们定义了 SolidColorBrush 资源:

<StackPanel.Resources>
	<SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</StackPanel.Resources>

现在资源已被定义,我们可以引用该资源来为 Background 属性提供值:

<Button Background="{DynamicResource MyBrush}" Content="I am gold"/>

在WPF XAML中,你可以使用静态或动态资源引用。这里,资源被引用为了DynamicResource。动态资源引用只能用于设置依赖属性,因此它是由WPF属性系统专门启用的动态资源引用用法。

2.5.2 数据绑定

依赖属性可以通过数据绑定引用值。数据绑定通过XAML中专有的标记扩展语法或代码中的Binding对象来生效。使用数据绑定,最终属性值的确定被推迟到运行时,那时会从数据源获取值。

下面示例通过使用XAML中声明的绑定来设置Button的 Content 属性。该绑定使用继承的数据上下文和XmlDataProvider数据源(未显示)。绑定本身通过XPath指定数据源内的源属性。

<Button Content="{Binding Source={StaticResource TestData}, XPath=test[1]/@text}"/>

依赖属性和 DependencyObject 类本身并不支持 INotifyPropertyChanged 来通知数据绑定操作的 DependencyObject 源属性值的更改。(即需要使用者自己去继承并实现)

2.5.3 样式

样式和模板是使用依赖属性的有力理由。样式对设置定义应用程序UI的属性尤其有用。
样式通常会定义为XAML中的资源。样式与属性系统交互,因为它们通常包含属性的"setter",和能根据另一属性的运行时值更改属性值的"trigger"。

以下示例创建了一个简单样式,该样式将在Resources字典(未显示)内定义。然后将该样式直接应用于按钮的 Style 属性。样式中的 setter 将样式化的按钮的背景属性设置为绿色。

<Style x:Key="GreenButtonStyle">
	<Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}" Content="I am green"/>

2.5.4 动画

依赖属性可被动画化。当应用的动画运行时,动画的值优先级高于任何其他属性值(包括局部值)。

以下示例为Button的 Background 属性设置动画。技术上来讲,属性元素语法将空白的 SolidColorBrush 设置为背景,并且 SolidColorBrush的Color属性被动画化。

<Button Content="I am animated">
	<Button.Background>
		<SolidColorBrush x:Name="AnimBrush"/>
	</Button.Background>
	<Button.Triggers>
		<EventTrigger RoutedEvent="FrameworkElement.Loaded">
			<BeginStoryboard>
				<Storyboard>
					<ColorAnimation
						Storyboard.TargetName="AnimBrush"
						Storyboard.TargetProperty="(SolidColorBrush.Color)"
						From="Blue" To="White" Duration="0:0:1"
						AutoReverse="True" RepeatBehavior="Forever" />
				</Storyboard>
			</BeginStoryboard>
		</EventTrigger>
	</Button.Triggers>
</Button>

2.5.5 元数据覆盖

当你从最初注册依赖属性的类派生时,可以通过覆盖其元数据来更改依赖属性的特定行为。覆盖元数据依赖于 DependencyProperty 标识符,且不需要重新实现该属性。元数据更改由属性系统处理。每个类都可能按类型持有从基类继承的所有属性的单独元数据。

以下示例覆盖DefaultStyleKey依赖属性的元数据。覆盖该特定依赖属性的元数据是创建可以使用主题默认样式的控件的实现模式的一部分。

public class SpinnerControl : ItemsControl
{
	static SpinnerControl() => DefaultStyleKeyProperty.OverrideMetadata(typeof(SpinnerControl), new FrameworkProertyMetadata(typeof(SpinnerControl)));
}

2.5.6 属性值继承

元素可以从对象树中的父元素继承依赖属性的值。

以下示例展示了一个绑定,其中包含用于指定绑定源的 DataContext 属性。因此,子对象中的绑定不需要指定源,就能从父StackPanel对象中的 DataContext 取得继承值。或者,子对象可以直接在Binding中指定自己的 DataContext或Source ,不使用继承的值。

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource TestData}}">
	<Button Content="{Binding XPath=test[2]/@text}"/>
</StackPanel>

2.5.7 WPF设计器集成

具有依赖属性实现的属性的自定义控件与VS的WPF设计器很好地集成。一个例子是能够在 属性 窗口中编辑直接的和附加的依赖属性。

2.5.8 小结

2.5节看下来,资源、数据绑定、样式、属性值继承,平时常用,其中资源的动态资源也没怎么用。
剩下几样,动画、元数据覆盖、设计器集成,没怎么用过。其中元数据覆盖,我认为可以单独开一篇讲讲。

2.6 依赖属性值优先级

WPF属性系统中任何基于属性的输入都可以设置依赖属性的值。依赖属性值优先级的存在,使得在不同场景下,属性获取值以一种可预测的方式进行。

下面示例包含一个适用于任何按钮 Background 属性的样式,但它指定了一个具有局部设置 Background 属性的按钮。从技术上讲,该按钮的 Background 属性设置了两次,尽管只会应用一个值——优先级最高的值。局部设置值具有最高优先级 (除了运行中的动画外),但此处不存在动画。因此,第二个按钮使用 Background 属性的局部设置值,而不是样式设置器的值。第一个按钮没有局部值或优先级高于样式设置器的其他值,因此使用 Background 属性的样式设置器值。

<StackPanel>
	<StackPanel.Resources>
		<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
			<Setter Property="Background" Value="Orange"/>
		</Style>
	</StackPanel.Resources>
	<Button>I am styled orange</Button>
	<Button Background="Pink">I am locally set to pink(not styled orange)</Button>
</StackPanel>

2.6.1 为什么存在依赖属性优先级?

局部值优先级高于样式设置器值,这使元素属性的局部控制得以支持。
(所以优先级存在可以简化编程,比如上面低优先级的全局生效,高优先级的局部生效,这样既能应用全局样式,又能控制局部)

2.7 学习更多有关依赖属性的内容

  • 组件开发人员或应用程序开发人员可能希望创建自己的依赖属性来添加一些功能,例如数据绑定、样式支持、验证或值转换。有关信息,查看 “自定义依赖属性” 章节。
  • 将依赖属性视为公开属性,任何有权访问实例的调用者都可以访问和发现。更多信息,查阅 “依赖属性安全性” 章节。
  • 附加属性是一种XAML中支持专门语法的属性类型。附加属性通常与CLR属性没有1:1对应关系,且不一定是依赖属性。它的主要目的是允许子元素向父元素报告属性值,即使父元素和子元素不包含该属性作为类成员列表的一部分。一种主要场景是使子元素能够通知父元素如何在UI中呈现它们。例如,Dock 和 Left。更多信息,查阅 “附加属性概述” 章节。

三、结尾

写这篇博客其实是源于一个问题,
我重写了一个按钮的样式,主要是控件模板,并且添加了一个触发器(鼠标进入改变背景色)。
我将新样式应用于两个按钮,一个按钮未显式设置Background,另一个按钮设置Background。
结果,未显式设置背景色的按钮,鼠标移入后会变色,另一个则不会。
现在看来,是依赖属性优先级的问题,局部设置值具有最高优先级。

举报

相关推荐

0 条评论