为 Silverlight 2.0 开发自定义控件






4.73/5 (21投票s)
在本文中,我将介绍为 Silverlight 2.0 开发自定义控件的关键步骤
 
 
引言
本文的重点是向您展示构建 Silverlight 2.0 自定义控件并将其重用于您的 Silverlight 项目所需的步骤。
本文基于今天在 MIX08 上宣布的 Silverlight 2.0 Beta1。
在本文中,我们将创建一个控件库,并实现我们自己的 Button 类,该类除了具有不同的默认样式外,没有任何对默认实现的额外功能。通过重用此控件,您无需在应用程序或复合控件的所有 Button 实例中添加 Style 属性。
让我们开始吧。
创建项目
在 Visual Studio 2008 中创建一个 Silverlight 类库项目。
 
 
由于这是一个控件库,我们需要添加对 System.Windows.Controls 程序集的引用。对于 Silverlight 2.0 Beta1,此程序集的版本号将为 1.0.0.0(别问为什么 :-)),并且默认情况下将来自“C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\System.Windows.Controls.dll”。
在下一步中,我们将向我们的控件程序集添加 XmlnsDefinitionAttribute。
此属性有助于 XAML 处理器查找 XML 命名空间和 CLR 命名空间并将它们配对。因此,请添加具有这些参数的属性:
[assembly: System.Windows.Markup.XmlnsDefinition 
 ("http://schemas.eyedea.hu/silverlight/2008/xaml/presentation", "Eyedea.Controls")]
创建控件类
在解决方案资源管理器中将 *Class1.cs* 重命名为 *MediaButton.cs*,并允许 Visual Studio 重构项目中的 Class1 类的名称引用。
接下来,我们将添加包含此控件库中控件默认样式的 XAML 文件。
让我们向项目添加一个文本文件类型的项,名为 *generic.xaml*。
 
 
在解决方案资源管理器中选择 *generic.xaml*,然后设置属性以将此文件以我们需要的格式嵌入:Resource。
您需要删除“自定义工具”属性的值,并将“生成操作”设置为“Resource”。
 
 
现在是时候编辑我们的主要目标:*MediaButton.cs*,所以打开它。
添加一个 using 语句来包含 System.Windows.Controls 命名空间,并让我们的 MediaButton 控件继承,因为它必须继承自内置的 Button 类。
此时,我们的 MediaButton 类将如下所示:
using System.Windows.Controls;
namespace Eyedea.Controls
{
    public class MediaButton : Button
    {
            public MediaButton()
            {
            }
    }
}
为控件添加默认样式
打开 *generic.xaml*。
首先,向 XAML 文件添加默认内容,并添加对我们的 XML 命名空间的引用。
<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Eyedea.Controls;assembly=Eyedea.Controls">
</ResourceDictionary>
请注意 local XML 命名空间前缀定义,它将在样式中使用来引用此控件库中的类型。
添加 Style 标签来定义我们样式的占位符。在 Style 标签的 TargetType 属性中,我们需要指定目标控件。在我们的例子中,它是我们的 MediaButton 控件。您还必须为 ControlTemplate 属性分配 TargetType。
<Style TargetType="local:MediaButton">
    <Setter property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MediaButton">
                <Grid x:Name="RootElement">
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
在 Style 中,您可以定义控件的完整外观,包括用于视觉状态转换的 StoryBoard。视觉元素和 Storyboard 的名称非常重要,因为它们是 Silverlight 2.0 样式工作方式的基本组成部分,但由于现在超出了范围,将在另一篇文章中讨论。
当前按钮设计具有固定的宽度和高度,因此我们需要通过属性设置器来设置它们。
我们还指定了最小和最大尺寸以“保护”按钮的设计。
提示:简单的属性设置器应直接放在 Style 标签之后,Template 属性之前。
<!-- Common properties -->
<Setter property="IsEnabled" Value="true" />
<Setter property="IsTabStop" Value="true" />
<Setter property="Margin" Value="0" />
<Setter property="HorizontalContentAlignment" Value="Center" />
<Setter property="VerticalContentAlignment" Value="Center" />
<Setter property="Cursor" Value="Arrow" />
<Setter property="Foreground" Value="#CC808080" />
<!-- Text related properties -->
<Setter property="TextAlignment" Value="Left" />
<Setter property="TextWrapping" Value="NoWrap" />
<Setter property="FontSize" Value="11" />
<!-- Default Size Constraints -->
<Setter property="Width" Value="50" />
<Setter property="MinWidth" Value="50" />
<Setter property="MaxWidth" Value="50" />
<Setter property="Height" Value="22" />
<Setter property="MinHeight" Value="22" />
<Setter property="MaxHeight" Value="22" />
通过复制我们第一个 Expression Design 操作指南视频的结果样式 XAML 内容,将视觉元素添加到模板中。
设计包含一个背景矩形,一个轮廓矩形,以及两个将在用户交互期间动画的亮点。
底部有一个 ContentPresenter 元素,它是按钮 Content 属性的占位符。
将以下内容添加到 Grid 标签内:
<Grid.Resources>
    <Storyboard x:Key="MouseOver State">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="HighlightTop" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="HighlightBottom" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="Border"
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.7"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="Pressed State">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="HighlightTop" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="HighlightBottom" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="Border" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.5"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="Normal State" />
    <Storyboard x:Key="Disabled State">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="HighlightTop" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.1500000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="HighlightBottom" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.1500000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
    Storyboard.TargetName="ContentPresenter" 
    Storyboard.Targetproperty="(UIElement.Opacity)">
            <SplineDoubleKeyFrame KeyTime="00:00:00.1500000" Value="0.7"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Grid.Resources>
<Rectangle Fill="#FF000000" Margin="2,2,2,2" RadiusX="1" RadiusY="1" Opacity="0.3"/>
<Rectangle x:Name="Border" Stroke="#FF808080" RadiusX="2" RadiusY="2" Opacity="0.3"/>
<Path x:Name="HighlightTop" Margin="2,2,2,11" Opacity="0.2" 
    Data="M0,1 C0,0.45 0.45,0 1,0 L45,0 C45.55,0 46,0.45 46,1 C46,1 46,9 46,9 
    C46,9 0,9 0,9 C0,9 0,1 0,1 z">
    <Path.Fill>
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFFFFFFF" Offset="0"/>
            <GradientStop Color="#FFE1E1E1" Offset="1"/>
        </LinearGradientBrush>
    </Path.Fill>
</Path>
<Path x:Name="HighlightBottom" Margin="2,11,2,2" Opacity="0" Data="M0,0 C0,0 31,0 
    46,0 C46,0 46,8 46,8 C46,8.55 45.55,9 45,9 L1,9 C0.45,9 0,8.55 0,8 C0,8 
    0,0 0,0 z">
    <Path.Fill>
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFD6D6D6" Offset="0"/>
            <GradientStop Color="#FFFFFFFF" Offset="1"/>
        </LinearGradientBrush>
    </Path.Fill>
</Path>
<ContentPresenter x:Name="ContentPresenter"
    Content="{TemplateBinding Content}"
    ContentTemplate="{TemplateBinding ContentTemplate}"
    FontFamily="{TemplateBinding FontFamily}"
    FontSize="{TemplateBinding FontSize}"
    FontStretch="{TemplateBinding FontStretch}"
    FontStyle="{TemplateBinding FontStyle}"
    FontWeight="{TemplateBinding FontWeight}"
    Foreground="{TemplateBinding Foreground}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    Padding="{TemplateBinding Padding}"
    TextAlignment="{TemplateBinding TextAlignment}"
    TextDecorations="{TemplateBinding TextDecorations}"
    TextWrapping="{TemplateBinding TextWrapping}"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
/>
生成项目。如果一切顺利,此时您将拥有一个可正常工作的自定义控件库。
测试控件
要测试此控件,我们需要创建一个 Silverlight 应用程序项目。在解决方案资源管理器中,右键单击解决方案节点,然后向 Silverlight 应用程序类型的解决方案添加新项目。
 
 
对于 Silverlight 应用程序,Visual Studio 会询问我们希望为此 Silverlight 应用程序使用哪种测试方法。目前,一个测试 HTML 页面对我们来说已经足够了。
 
 
对于实际场景,例如混搭应用,需要 Web 应用程序,因为从文件系统运行的 Silverlight 应用程序或从 Web 服务器运行的应用程序访问外部资源的机制不同。
通过右键单击项目节点并选择“设置为启动项目”,将 TestApplication 项目标记为我们的启动项目。
将控件添加到测试项目中
要在我们的 TestApplication 中使用该控件,我们需要添加对控件库项目的引用。
 
 
在设计器中打开 *Page.xaml* 并切换到 XAML 视图。要在页面中使用 MediaButton 控件,我们需要向 UserControl 标签添加 XML 命名空间定义。
测试页面将包含一个 4x3 的 Grid 和 2 个 MediaButton 实例,内容分别为“Play”和“Stop”。
用以下块覆盖 *Page.xaml* 的内容:
<UserControl x:Class="TestApplication.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:eyedea="clr-namespace:Eyedea.Controls;assembly=Eyedea.Controls"
    Width="320" Height="240">
        <Grid x:Name="LayoutRoot" Background="Black" Margin="50,50,50,50">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.RenderTransform>
                <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2" ScaleY="2" />
            </Grid.RenderTransform>
            <Rectangle Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1" 
        Stroke="#FF808080" RadiusX="2" RadiusY="2" Opacity="0.3"/>
            <eyedea:MediaButton Grid.Column="1" Grid.Row="1" Margin="2,2,2,2" 
        Content="Play">
            </eyedea:MediaButton>
            <eyedea:MediaButton Grid.Column="2" Grid.Row="1" Margin="2,2,2,2" 
        Content="Stop">
            </eyedea:MediaButton>
        </Grid>
</UserControl>
按 F5 并尝试该控件。对于 Silverlight 2.0,键盘支持比 Silverlight 1.0 好得多。现在您可以立即获得完整的 Tab 键支持。通过按几次 Tab 键进入浏览器中的 Silverlight 内容来尝试。
下一步
- 我计划写一篇基于本文知识的文章,从头开始构建一个可皮肤的控件。
- 以及更多关于 Silverlight 2.0 的内容 ;-)
关注点
- 我们的第一个操作指南视频:MediaButton 控件的设计(英语)
- 我们的第一个操作指南视频:MediaButton 控件的设计(匈牙利语)
历史
- 2008 年 5 月 3 日 - 原始提交
- 2008 年 5 月 6 日 - 添加了英语视频
- 2008 年 3 月 23 日 - 更新了源代码存档 - 删除了 *ButtonBase.cs*


