使用 XAML 和 C# 构建自定义控件
您可能已经知道,Windows 8 XAML 平台的最强大功能之一是平台为创建自定义控件所提供的灵活性。XAML 提供的诸如依赖项属性和控件模板等功能,可以令您方便地创建功能丰富且可自定义的控件。
在上一篇博文“采用 Windows JavaScript 库 (WinJS) 构建自定义控件”中,Jordan Matthiesen 向您介绍创建自定义 HelloWorld 控件的步骤。在本篇博文中,我将向您介绍使用 Xaml 创建同一控件的步骤。我会介绍一些技巧和概念,您可以用来创建可重用的自定义控件,同时还会说明如何创建模板来设定这些控件的样式。我会介绍一些诸如依赖项属性的概念,并介绍使用自定义的 Generic.xaml 文件来创建一个用于定义默认控件模板的隐式样式。
简单的 XAML 控件
首先,我们构建控件 Hello World:从 Windows.UI.XAML.Controls.Control 派生的类。在 Visual Studio 中使用空白项目模板创建一个新的项目。将您的项目命名为 CustomControls。使用“新增项目”模板向导添加您的自定义控件。
Visual Studio 包括一个项目模板,用于创建模板化控件。右键单击该项目,选择“添加”->“新增项目”
选择 Templated Control 项目,并将该控件命名为“HelloWorld.cs”
该模板创建此类:
public class HelloWorld : Control { public HelloWorld() { this.DefaultStyleKey = typeof(HelloWorld); } }
在这个短小的代码块中,我们指出了两种非常重要的详细信息。首先,HelloWorld 类派生自控件。其次,设置 DefaultStyleKey,就是向 XAML 平台指出,此类使用一个隐式样式。模板化控件模板还添加了一个名为 Themes 的文件夹,在该文件夹中,创建一个名为 Generic.xaml 的新文件。在项目模板中有更多关于此模板的信息。
控件的默认样式是在一个自定义的 Generic.xaml 文件中定义,该平台会自动加载该文件。在本文件中,您可以定义隐式样式,意味着我们可以定义一个样式,默认情况下该样式会应用到某一特定类型的所有控件。将突出显示的 XAML 添加到 Themes 文件夹中的新 Generic.xaml 文件:
<ResourceDictionary xmlns="https://schemas.microsoft.com/winfx/2006/XAML/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/XAML" xmlns:local="using:CustomControls"> <Style TargetType="local:HelloWorld"> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:HelloWorld"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <TextBlock Text="HelloWorld" FontFamily="Segoe UI Light" FontSize="36"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
这些新的 XAML 行会定义一个样式,默认情况下,该样式会应用到 HelloWorld 控件的所有实例。我们定义一个控件模板,此模板指定该控件只是一个带有文本“HelloWorld”的文本块。
现在,转至您的 MainPage.xaml 文件,然后添加下一个标记。您必须包括 “local:” 标志,以便令 XAML 知道在哪个命名空间中查找 HelloWorld 类。“local:” 标志是在该 XAML 文件的顶部定义。
<local:HelloWorld />
当您运行应用程序时,您将看到该控件已经加载,而且显示文本“Hello, World!”。
定义控件选项
当我们添加可配置的选项时,控件就变得更加有用,并且可重用。我们添加一个选项以允许控件可以设置为闪烁。
要添加此选项,我们为该控件添加一个依赖项属性 (DP)。您可以在依赖项属性概述中了解关于 DP 的更多信息。有一个 Visual Studio 代码段,令添加 DP 非常简单方便。将光标放在构造函数下,键入 “propdp”,然后按 Tab 键两次。您可以遍历代码段中的每个参数来填写规范。
public class HelloWorld : Control { public HelloWorld() { this.DefaultStyleKey = typeof(HelloWorld); } public bool Blink { get { return (bool)GetValue(BlinkProperty); } set { SetValue(BlinkProperty, value); } } // Using a DependencyProperty enables animation, styling, binding, etc. public static readonly DependencyProperty BlinkProperty = DependencyProperty.Register( "Blink", // The name of the DependencyProperty typeof(bool), // The type of the DependencyProperty typeof(HelloWorld), // The type of the owner of the DependencyProperty new PropertyMetadata( // OnBlinkChanged will be called when Blink changes false, // The default value of the DependencyProperty new PropertyChangedCallback(OnBlinkChanged) ) ); private DispatcherTimer __timer = null; private DispatcherTimer _timer { get { if (__timer == null) { __timer = new DispatcherTimer(); __timer.Interval = new TimeSpan(0,0,0,0,500); // 500 ms interval __timer.Tick += __timer_Tick; } return __timer; } } private static void OnBlinkChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { var instance = d as HelloWorld; if (instance != null) { if (instance._timer.IsEnabled != instance.Blink) { if (instance.Blink) { instance._timer.Start(); } else { instance._timer.Stop(); } } } } private void __timer_Tick(object sender, object e) { DoBlink(); } private void DoBlink() { this.Opacity = (this.Opacity + 1) % 2; } }
再回到 MainPage.xaml 中,将配置选项添加到该控件。
<local:HelloWorld Blink="True" />
运行该应用程序,并观看该控件闪烁。
为事件添加支持
通过向控件添加事件,您可以增强控件的功能。事件允许您在操作发生时可以中断控件,然后您可以运行代码以便对操作做出反应。我们添加一个控件每次闪烁时都会触发的事件。
public class HelloWorld : Control { ... ... private void __timer_Tick(object sender, object e) { DoBlink(); } private void DoBlink() { this.Opacity = (this.Opacity + 1) % 2; OnBlinked(); } public event EventHandler Blinked; private void OnBlinked() { EventHandler eh = Blinked; if (eh != null) { eh(this, new EventArgs()); } } }
再回到 MainPage.xaml 中,向该元素添加一个 x:Name 属性,这样我们可以以后在代码中检索该控件实例:
<local:HelloWorld x:Name="HelloWorldWithEvents" Blink="True" />
现在,在 MainPage.xaml.cs 中,添加一个事件侦听器函数委派,以便在触发该事件时可以打印调试输出。
HelloWorldWithEvents.Blinked += (object sender, EventArgs args) => { System.Diagnostics.Debug.WriteLine("HelloWorldWithEvents Blinked"); };
当运行该代码时,您每 500 毫秒将在 Visual Studio 内的输出控制台窗口中看到一条写出的消息。
揭示公共方法
我们已经编写了一个私有方法 DoBlink 以便令该控件闪烁,现在我们令该方法成为公共方法,以便允许您可以在任意时间选择令该控件闪烁。您所需做的就是将 DoBlink 方法的可见性更改为公开:
public class HelloWorld : Control { ... public void DoBlink() { this.Opacity = (this.Opacity + 1) % 2; OnBlinked(); } ... }
现在,您可以在 C# 代码隐藏中调用公开 DoBlink,可能在按钮单击处理程序中。
private void Button_Click_1(object sender, RoutedEventArgs e) { HelloWorldWithEvents.DoBlink(); }
重用控件
当您仅希望在一个项目中使用控件或者仅出于个人用途使用控件时,可能在包装和分发方面无需考虑太多。不过,如果您希望构建一个可重用的控件,以便与其他开发者共享,则您有几种不同的选项。一个选项就是创建 Visual Studio 扩展 SDK,其他开发者可以在自己的机器上安装该控件,并添加到自己的项目。有关如何实现上述目标的更多信息,请查阅使用 C# 或 Visual Basic 创建 SDK。
兼收并蓄
我们已经向您介绍了您希望在一个控件中实现的最常见的方面:
- 在页面中包含控件
- 传递配置选项
- 分派并响应事件
- 通过公共方法揭示功能
这样,您就了解了 XAML 自定义控件的基本知识。下一步骤就是查找某些功能,您可能希望将这些功能添加到现有 XAML 控件,然后更改样式或者设置控件的子类,以添加您自己的功能。由于我对自定义控件越来越得心应手,我对编写 XAML 应用程序也信心十足。我希望,您可以试着自己创建自定义控件,并且欢迎您在线公布您的体验。
我们希望本篇博文对您有用。如果您在构建自己的控件时遇到了任何疑问,请联系 Windows 开发人员中心,并在论坛中提问。
--Aaron Wroblewski
Windows 项目经理
Comments
- Anonymous
October 19, 2012
Any C++ example?