使用 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 包括一个项目模板,用于创建模板化控件。右键单击该项目,选择“添加”->“新增项目”

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

兼收并蓄

我们已经向您介绍了您希望在一个控件中实现的最常见的方面:

  1. 在页面中包含控件
  2. 传递配置选项
  3. 分派并响应事件
  4. 通过公共方法揭示功能

这样,您就了解了 XAML 自定义控件的基本知识。下一步骤就是查找某些功能,您可能希望将这些功能添加到现有 XAML 控件,然后更改样式或者设置控件的子类,以添加您自己的功能。由于我对自定义控件越来越得心应手,我对编写 XAML 应用程序也信心十足。我希望,您可以试着自己创建自定义控件,并且欢迎您在线公布您的体验。

我们希望本篇博文对您有用。如果您在构建自己的控件时遇到了任何疑问,请联系 Windows 开发人员中心,并在论坛中提问。

--Aaron Wroblewski

Windows 项目经理