WPF控件开发之自定义控件(1)

本文探讨了Windows Presentation Foundation (WPF) 中的控件定制方法,包括利用样式、模板和触发器等特性来减少创建自定义控件的需求。同时介绍了三种控件创作模型:从UserControl、Control和FrameworkElement派生。
摘要由CSDN通过智能技术生成

Windows Presentation Foundation (WPF) 控件模型的扩展性极大减少了创建新控件的需要。但在某些情况下,仍可能需要创建自定义控件。本主题讨论可最大限度减少在 Windows Presentation Foundation (WPF) 中创建自定义控件以及其他控件创作模型的需要的功能。本主题还演示如何创建新控件。

编写新控件的替代方法
以前,如果要通过现有控件获取自定义体验,您只能更改控件的标准属性,例如背景色、边框宽度和字号。如果希望在这些预定义参数的基础之上扩展控件的外观或行为,则需要创建新的控件,通常的方法是继承现有控件并重写负责绘制该控件的方法。虽然这仍是一种可选方法,但也可以利用 WPF 丰富内容模型、样式、模板和触发器来自定义现有的控件。下面的列表提供了一些示例,演示如何在不创建新控件的情况下使用这些功能来实现统一的自定义体验。

丰富内容。 很多标准 WPF 控件支持丰富内容。例如,Button 的内容属性为 Object 类型,因此从理论上讲,任何内容都可以显示在 Button 上。若要让按钮显示图像和文本,可以将图像和 TextBlock 添加到 StackPanel 中,然后将 StackPanel 分配给 Content 属性。由于这些控件可以显示 WPF 可视化元素和任意数据,因此,减少了创建新控件或修改现有控件来支持复杂可视化效果的需要。

样式。 Style 是表示控件属性的值的集合。使用样式可创建所需控件外观和行为的可重用表示形式,而无需编写新控件。例如,假设希望所有 TextBlock 控件都呈现字号为 14 的红色 Airal 字体。您可以创建一个样式作为资源,然后相应地设置适当的属性。这样,添加到应用程序中的每个 TextBlock 都将具有相同的外观。

数据模板。 DataTemplate 可用于自定义数据在控件上的显示方式。例如,DataTemplate 可用于指定数据在 ListBox 中的显示方式。有关这种情况的示例,请参见数据模板概述。除了自定义数据外观之外,DataTemplate 还可以包含 UI 元素,这样大大增加了自定义 UI 的灵活性。例如,使用 DataTemplate 可以创建一个 ComboBox,其中每一项都包含一个复选框。

控件模板 WPF 中的很多控件都使用 ControlTemplate 来定义控件的结构和外观,这样可将控件外观和控件功能分离开。通过重新定义控件的 ControlTemplate,可以彻底更改控件的外观。例如,假设您希望控件看起来像一个交通信号灯。此控件具有简单的用户界面和功能。该控件有三个圆形,一次只能点亮其中的一个。经过考虑之后,您可能意识到 RadioButton 提供了一次只选中一项的功能,但是 RadioButton 的默认外观完全不像交通信号灯上的灯。由于 RadioButton 使用控件模板来定义其外观,因此很容易重新定义 ControlTemplate 以符合该控件的要求,从而使用单选按钮来制作交通信号灯。

说明:
尽管 RadioButton 可以使用 DataTemplate,但在本例中,只使用 DataTemplate 还不够。DataTemplate 定义控件内容的外观。对于 RadioButton,指示 RadioButton 是否选中的那个圆形右侧显示出来的全部都是该控件的内容。在交通信号灯的示例中,单选按钮只需要成为可“点亮”的圆形。由于交通信号灯的外观要求与 RadioButton 的默认外观存在很大差异,因此,有必要重新定义 ControlTemplate。一般而言,DataTemplate 用于定义控件的内容(或数据),ControlTemplate 用于定义控件的构成方式。

触发器。 Trigger 用于在不创建新控件的情况下动态更改控件的外观和行为。例如,假设应用程序中有多个 ListBox 控件,而您希望每个 ListBox 中的项在选中时都显示为红色粗体。您首先想到的可能是创建一个从 ListBox 继承的类,然后重写 OnSelectionChanged 方法,以更改选中项的外观,不过,更好的方法是向 ListBoxItem 的样式添加一个更改选中项外观的触发器。触发器用于更改属性值或根据属性值执行操作。EventTrigger 用于在发生事件时执行操作。

一般而言,如果控件完全复制现有控件的功能,但您希望该控件具有不同的外观,则应先考虑是否可以使用本节中讨论的某些方法来更改现有控件的外观。

控件创作模型
通过丰富内容模型、样式、模板和触发器,最大程度地减少了创建新控件的需要。但是,如果确实需要创建新控件,那么理解 WPF 中的不同控件创作模型就显得非常重要。WPF 提供三个用于创建控件的一般模型,每个模型都提供不同的功能集和灵活度。这三个模型的基类分别为 UserControl、Control 和 FrameworkElement。

从 UserControl 派生
在 WPF 中创建控件的最简单方法是从 UserControl 派生。如果生成继承自 UserControl 的控件,需要将现有组件添加到 UserControl,命名这些组件,然后在 可扩展应用程序标记语言 (XAML) 中引用事件处理程序。执行这些操作之后,即可在代码中引用这些命名元素和定义事件处理程序。此开发模型非常类似于用于 WPF 应用程序开发的模型。

如果生成正确,UserControl 可以利用丰富内容、样式和触发器的优点。但是,如果控件继承自 UserControl,则使用该控件的用户将无法使用 DataTemplate 或 ControlTemplate 来自定义其外观。因此,有必要从 Control 类或其派生类(UserControl 除外)进行派生,以便创建支持模板的自定义控件。

从 UserControl 派生的优点
如果符合以下所有情况,请考虑从 UserControl 派生:

希望以类似于生成应用程序的方式生成控件。

控件仅由现有组件组成。

不需要支持复杂自定义项。

从 Control 派生
从 Control 类派生是大多数现有 WPF 控件使用的模型。在创建继承自 Control 类的控件时,可使用模板定义其外观。通过这种方式,可以将运算逻辑从可视化表示形式中分离出来。通过命令和绑定(而不是事件),也可确保分离 UI 和逻辑,尽可能避免引用 ControlTemplate 中的元素。如果控件的 UI 和逻辑正确分离,该控件的用户即可重新定义其 ControlTemplate,从而自定义其外观。尽管生成自定义 Control 不像生成 UserControl 那样简单,自定义 Control 还是提供了最大的灵活性。

从 Control 派生的优点
如果符合以下任一情况,请考虑从 Control 派生,而不要使用 UserControl 类:

希望控件外观能通过 ControlTemplate 进行自定义。

希望控件支持不同的主题。

从 FrameworkElement 派生
从 UserControl 或 Control 派生的控件依赖于组合现有元素。很多情况下,这是一种可接受的解决方案,因为从 FrameworkElement 继承的任何对象都可以位于 ControlTemplate 中。但是,某些时候,简单的元素组合不能满足控件的外观需要。对于这些情况,使组件基于 FrameworkElement 才是正确的选择。

生成基于 FrameworkElement 的组件有两种标准方法:直接呈现和自定义元素组合。直接呈现涉及的操作包括:重写 FrameworkElement 的 OnRender 方法,并提供显式定义组件视觉效果的 DrawingContext 操作。此方法由 Image 和 Border 使用。自定义元素组合涉及的操作包括使用 Visual 类型的对象组合组件的外观。有关示例,请参见使用 DrawingVisual 对象。Track 是 WPF 中使用自定义元素组合的控件示例。在同一控件中,也可以混合使用直接呈现和自定义元素组合。

从 FrameworkElement 派生的优点
如果符合以下任一情况,请考虑从 FrameworkElement 派生:

希望对控件的外观进行精确控制,而不仅仅是简单的元素组合提供的效果。

想要通过定义自己的呈现逻辑来定义控件的外观。

想要以一种 UserControl 和 Control 之外的新颖方式组合现有元素。

 控件创作基础知识
如前所述,WPF 最强大的功能之一是,无需创建自定义控件即可实现远比设置控件的基本属性来更改其外观和行为更强的功能。WPF 属性系统和 WPF 事件系统使样式、数据绑定和触发器功能成为可能。如果在控件中实现依赖项属性和路由事件,则无论使用什么模型创建自定义控件,自定义控件的用户都可以像对 WPF 随附的控件那样使用这些功能。

使用依赖项属性
当属性为依赖项属性时,可以进行下面的操作:

在样式中设置该属性。

将该属性绑定到数据源。

使用动态资源作为该属性的值。

动画处理该属性。

如果希望控件的属性支持以上任一功能,则应将该属性实现为依赖项属性。下面的示例定义一个依赖项属性。

 

这段代码执行下面的操作:

将一个名为 ValueProperty 的 DependencyProperty 标识符定义为 publicstaticreadonly 字段。

通过调用 DependencyProperty..::.Register 向属性系统注册该属性名,以指定以下内容:

属性的名称。

属性的类型。

拥有该属性的类型。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值