为 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*