第3章 自定义控件

这一章实际上是在说明,为什么不要创建自定义控件。因为winForm的影响,实际上大部分情况就不要创建,而是利用好模板和附加属性。

创建WPF的挑战不是实现起来有多难,而是几百种选择中产生的选择困难症,最好的办法还是从定制或延伸现有控件。

WPF是一个丰富而强大的平台,它为您提供了许多现成的控件。 在大多数情况下,这些现成的控件应该能满足您的需求。 如果您对现有控件需要进行的更改仅仅是外观上的修饰,通常可以使用控件的属性来进行这些修改。例如,一个按钮暴露了Background、BorderThickness和BorderBrush等属性。这些属性可以用于快速而简单地更改按钮的外观。对于需要更高级改动的控件,涉及修改可视化树,您可以使用ControlTemplate或DataTemplate(或两者)来满足您的UI需求。

使用ControlTemplate可以彻底改变现有控件的外观。ControlTemplate允许您在修改后的可视树中包含WPF层次结构中的大多数类,从而实现丰富的外观和感觉。您可以使用不透明度、着色器效果和动画来增强控件的外观。

WPF中的大多数控件都遵循一个称为无外观控件的模式。这意味着控件的外观与其行为分离,控件对自身的外观不做任何假设。控件只根据逻辑属性、事件和行为来定义。一个控件的外观可以使用ControlTemplate和/或DataTemplate的组合来指定,也可以仅使用控件的属性来指定。控件中外观和行为的分离使开发人员能够增强外观甚至修改控件的行为,而无需使用子类化。

本章的这一部分概述了许多不同技术,让您能够创建所需的功能和外观,而无需创建新的控件类。在选择所需工具时,请考虑您想要的行为以及所需的基本外观,并从这些方面着手进行工作。

  1. 使用数据转换

有时候,ControlTemplate可以让你接近完成目标,但最终结果与你的要求还存在一些微小的差距。通常情况下,这是因为你试图绑定到一个不适合直接渲染的数据值。相反,你希望将你的GUI绑定到一个更适合用户的中间结果。转换不仅限于领域相关的数据,还可以在UI层中使用。在WPF中,你可以使用ValueConverter和TypeConverter来解决这个问题。

使用值转换器

值转换器非常适合将数据从一个域转换到另一个域。开发人员常常意识到,后端数据源优化得越多,内容就越不易读取。值转换器可以用于将简单的布尔值转换为是/否文本,将位掩码整数转换为一系列图形化的开关按钮,等等。您还可以使用值转换器将用户输入或用户手势转换为绑定数据源可使用的内容。

值转换器专门与WPF数据绑定设施配合使用。这意味着它们可以在XAML中的{Binding ...}语法中使用,也可以通过Binding类以编程方式创建。Binding类公开了一个名为Converter的属性,它的类型是IValueConverter。使用转换器,可以对绑定的值进行转换。

格式化是使用绑定值进行转换的经典示例。如果有需要以人可读的方式显示的数据值,那么值转换器是正确的工具。值转换器可以用于超出纯数据的范围,并可以在应用绑定的许多其他地方使用。在ControlTemplate中使用TemplateBinding是一个很好(但经常被忽视)使用值转换器的地方。

使用TypeConverters

TypeConverters在设置XAML中的复杂属性值时非常有用。通过将TypeConverter与特定类型关联,可以将开发人员在XAML中输入的字符串值转换为基础对象树使用的更具体类型。 例如,如果您创建的控件公开一个枚举类型的属性,您仍希望能够在该控件上设置该值,但在XAML中,您希望能够将其设置为简单的字符串,并使控件了解您的意图。TypeConverters使这成为可能。 以BrushConverter为例。在构建WPF应用程序时,我们大多数人每天都使用该转换器,但我们对其存在和实用性习以为常。BrushConverter使我们能够在设置Brush类型属性时键入自由格式的字符串,如“Red”,“Green”或“Blue”。

1
<Grid Background=”Blue” ... />

  Grid类的Background属性的类型是Brush。BrushConverter将字符串“Blue”转换为SolidColorBrush类的实例,并将颜色初始化为System.Windows.Media.Colors.Blue。 TypeConverter和ValueConverter为XAML中设置属性增加了一层间接性或抽象性,并大大增强了我们展示用户期望看到的内容的能力,而不强迫他们看到只有数据库才能喜欢的数据。 您还可以在这些转换器中添加条件逻辑,以创建一些强大的接口。 考虑一个示例,您想根据某些条件更改ListBox的ItemsPanel。换句话说,基于您的UI发生的某些事情,您希望动态更改容纳列表框项的面板容器。 您可以使用Style触发器来实现此功能,该触发器查看ListBox上的特定属性并更改ItemsPanel。在此示例中,普通的属性值不足以做出决策,我们需要更多的信息来决定合适的面板。我们可以使用ValueConverter添加此附加信息,并将其提供给样式触发器。

<Style.Triggers>
<Trigger Property=”SwitchToBigPanel”
Value=”True”>
<Setter Property=”ItemsPanel”
Value=”{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource Conv}}” />
</Trigger>
</Style.Triggers>

绑定中的{StaticResource Conv}是指一个实现了IValueConverter接口的实例,它执行一些逻辑,检查附加条件,并返回ItemsPanelTemplate类的实例(ItemsPanel属性的类型为ItemsPanelTemplate)。正如你所看到的,像这样使用值转换器对于WPF开发人员来说非常强大。

这样的转换器和触发器的一个实际用途可能是在ListBox中的项目数量超过某个阈值时动态更改包含面板。例如,当子项目少于50个时,你可以使用默认的容器面板,但是当数据集中有中等、大型和庞大的数据集时,你可以动态改变为不同的容器以提供用户最佳的项目导航体验。

ValueConverter可能还具有其他逻辑,用于检查可用的系统资源或用户偏好,以确定他们想要在任何给定时间处理的项目数量。可能性是无限的!

2 行为扩展

在决定从哪里开始自定义现有控件时,你首先需要找到具有你所寻找行为的控件。通常而言,相对于行为,可视元素更容易自定义和扩展。例如,你可以更改一个条目渲染控件中包含条目的面板,但你不应该尝试将列表类型的控件转变为单个类型的控件。

根据所代表的数据形状来定义WPF层次结构中的控件。以下是WPF控件支持的数据形状的简要概述:

. Singular controls—Button, RadioButton, CheckBox
. Range-based controls—Slider, ProgressBar
. List-based controls—ItemsControl, ListBox, ListView, ComboBox
. Hierarchy-based controls—TreeView
. Composite data-based controls—UserControl

开始在你的应用程序中表示数据时,这些控件是你首先开始使用的。如果你试图表示的数据的形状与直接映射到控件不符,仍然可以通过谨慎使用数据模板、控件模板和转换器来使用现有的控件。

posted @   cbaa  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示