Silverlight™ 2 有别于 Silverlight 1.0 的众多功能之一是支持控件。Silverlight 2 以丰富且强大可靠的控件模型闻名,该模型是平台中包括的控件和第三方控件包的基础。您也可以使用此控件模型构建自己的控件,但对于不熟悉 Windows® Presentation Foundation (WPF) 控件模型的开发人员来讲,第一次构建 Silverlight 自定义控件会令人生畏。撰写此专栏时,即在发布 Silverlight 2 Beta 2 之前,几乎没有文档可供参考,而在 Web 中快速搜索出的少数几篇教程为我指明了方向。虽然我正在讨论此主题,但应该提醒大家的是,我正在使用的 Beta 2 在其最终发布之前,可能会做进一步的更改。

{
public class SimpleButton : ContentControl
{
}
}
此时,您已实现了单纯的自定义控件,该控件可在 XAML 文档中通过声明进行实例化。为了说明此问题,将下列语句添加到 Page.xaml:
<custom:SimpleButton />
具体位置是<grid></grid>之间。
xmlns:custom="clr-namespace:SimpleButtonDemo; assembly=SimpleButtonDemo"
您可以看到,clr-namespace 能够识别在其中定义 SimpleButton 类的命名空间,而程序集可以识别包含此控件的程序集。在此示例中,控件程序集和应用程序程序集是同一个程序集。如果 SimpleButton 在名为 MyControls.dll 的单独程序集中实现,您需要将程序集设为与“MyControls”相同。图 2 中的代码显示了完成上述修改后的 Page.xaml 的内容。顺便说一下,您不必将 custom 作为自定义控件的前缀;只需直接使用 foo 或贵公司的名称作为前缀即可。

xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<custom:SimpleButton />
</Grid>
</UserControl>


< custom:SimpleButton.Template >
< ControlTemplate >
< Grid x:Name = " RootElement " >
< Rectangle x:Name = " BodyElement " Width = " 200 " Height = " 100 "
Fill = " Lavender " Stroke = " Purple " RadiusX = " 16 " RadiusY = " 16 " />
< TextBlock Text = " Click Me " HorizontalAlignment = " Center "
VerticalAlignment = " Center " />
</ Grid >
</ ControlTemplate >
</ custom:SimpleButton.Template >


xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo">
<Style TargetType="custom:SimpleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="custom:SimpleButton">
<Grid x:Name="RootElement">
<Rectangle x:Name="BodyElement" Width="200" Height="100"
Fill="Lavender" Stroke="Purple" RadiusX="16" RadiusY="16" />
<TextBlock Text="Click Me" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
this.DefaultStyleKey = typeof(SimpleButton);
然后打开 Page.xaml 并修改控件声明,使修改后的内容如下所示:
<custom:SimpleButton />
在浏览器中打开此测试页面,该控件的外观应与以前完全相同。但是这次,获取此外观将比较简单。
整个过程是先定义一个新的样式,然后在控件文件里进行绑定,则控件就会显示出样式。
<custom:SimpleButton Width="250" Height="150" />
从控件开发人员的角度而言,Silverlight 2 最重要的功能之一就是模板绑定。模板绑定允许分配给控件的属性值向下传递到控件模板,并且是使用 {TemplateBinding} 标记扩展在 XAML 中声明的。请不要使用类似下面的硬编码值定义构成 SimpleButton 主体的 Rectangle 的 Width 和 Height 属性:
Width="200" Height="100"
您应按如下方式定义上述属性:
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
现在,分配给控件的宽度和高度即是分配给 Rectangle 的宽度和高度。
图 7 显示了 Generic.xaml 修改后的版本,它将默认值分配给从基类继承来的 Width、Height 和 Background 属性,并使用模板绑定在控件模板中引用这些属性值。
图 7 修改后的 Generic.xaml
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:custom = " clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo " >
< Style TargetType = " custom:SimpleButton " >
< Setter Property = " Width " Value = " 200 " />
< Setter Property = " Height " Value = " 100 " />
< Setter Property = " Background " Value = " Lavender " />
< Setter Property = " Template " >
< Setter.Value >
< ControlTemplate TargetType = " custom:SimpleButton " >
< Grid x:Name = " RootElement " >
< Rectangle x:Name = " BodyElement "
Width = " {TemplateBinding Width} "
Height = " {TemplateBinding Height} "
Fill = " {TemplateBinding Background} "
Stroke = " Purple " RadiusX = " 16 " RadiusY = " 16 " />
< TextBlock Text = " Click Me "
HorizontalAlignment = " Center "
VerticalAlignment = " Center " />
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
</ Style >
</ResourceDictionary>
输出内容如图 8 所示。TemplateBindings 是正确进行操作的至关重要的步骤,因为现在 SimpleButton 的实例已支持为其分配的属性值。


< custom:SimpleButton.Content >
< StackPanel Orientation = " Horizontal " HorizontalAlignment = " Center " >
< Ellipse Width = " 75 " Height = " 75 " Margin = " 10 " >
< Ellipse.Fill >
< RadialGradientBrush GradientOrigin = " 0.25,0.25 " >
< GradientStop Offset = " 0.25 " Color = " White " />
< GradientStop Offset = " 1.0 " Color = " Red " />
</ RadialGradientBrush >
</ Ellipse.Fill >
</ Ellipse >
< TextBlock Text = " Click Me " VerticalAlignment = " Center " />
</ StackPanel >
</ custom:SimpleButton.Content >
</custom:SimpleButton>


xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:custom = " clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo " >
< Style TargetType = " custom:SimpleButton " >
< Setter Property = " Width " Value = " 200 " />
< Setter Property = " Height " Value = " 100 " />
< Setter Property = " Background " Value = " Lavender " />
< Setter Property = " FontSize " Value = " 11 " />
< Setter Property = " Template " >
< Setter.Value >
< ControlTemplate TargetType = " custom:SimpleButton " >
< Grid x:Name = " RootElement " >
< Rectangle x:Name = " BodyElement "
Width = " {TemplateBinding Width} "
Height = " {TemplateBinding Height} "
Fill = " {TemplateBinding Background} "
Stroke = " Purple " RadiusX = " 16 " RadiusY = " 16 " />
< ContentPresenter Content = " {TemplateBinding Content} "
HorizontalAlignment = " Center " VerticalAlignment = " Center " />
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
</ Style >
</ResourceDictionary>

< custom:SimpleButton.Content >
< StackPanel Orientation = " Horizontal " HorizontalAlignment = " Center " >
< Ellipse Width = " 75 " Height = " 75 " Margin = " 10 " >
< Ellipse.Fill >
< RadialGradientBrush GradientOrigin = " 0.25,0.25 " >
< GradientStop Offset = " 0.25 " Color = " White " />
< GradientStop Offset = " 1.0 " Color = " Red " />
</ RadialGradientBrush >
</ Ellipse.Fill >
</ Ellipse >
< TextBlock Text = " Click Me " VerticalAlignment = " Center " />
</ StackPanel >
</ custom:SimpleButton.Content >
</custom:SimpleButton>


public event RoutedEventHandler Click;//声明click
public SimpleButton()
{
this .DefaultStyleKey = typeof (SimpleButton);
this .MouseLeftButtonUp += new MouseButtonEventHandler//添加监控事件
(SimpleButton_MouseLeftButtonUp);
}
void SimpleButton_MouseLeftButtonUp( object sender,
MouseButtonEventArgs e)
{
if (Click != null )
Click( this , new RoutedEventArgs()); //激发监控事件
}
}
添加事件的思路是先在控件文件类里声明,然后添加监控函数,实现监控函数以激发事件 。
{
System.Windows.Browser.HtmlPage.Window.Alert( " Click! " );
}
步骤 8:添加可视状态

xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:custom = " clr-namespace:SimpleButtonDemo;assembly=SimpleButtonDemo "
xmlns:vsm = " clr-namespace:System.Windows;assembly=System.Windows " >
< Style TargetType = " custom:SimpleButton " >
< Setter Property = " Width " Value = " 200 " />
< Setter Property = " Height " Value = " 100 " />
< Setter Property = " Background " Value = " Lavender " />
< Setter Property = " FontSize " Value = " 11 " />
< Setter Property = " Template " >
< Setter.Value >
< ControlTemplate TargetType = " custom:SimpleButton " >
< Grid >
< vsm:VisualStateManager.VisualStateGroups >
< vsm:VisualStateGroup x:Name = " CommonStates " >
< vsm:VisualStateGroup.Transitions >
< vsm:VisualTransition To = " Normal " />
< vsm:VisualTransition To = " MouseOver " />
</ vsm:VisualStateGroup.Transitions >
< vsm:VisualState x:Name = " Normal " />
< vsm:VisualState x:Name = " MouseOver " >
< Storyboard >
< ColorAnimation Storyboard.TargetName = " BodyElement "
Storyboard.TargetProperty = " (Rectangle.Fill).(SolidColorBrush.Color) "
To = " Pink " Duration = " 0 " />
</ Storyboard >
</ vsm:VisualState >
</ vsm:VisualStateGroup >
</ vsm:VisualStateManager.VisualStateGroups >
< Rectangle x:Name = " BodyElement "
Width = " {TemplateBinding Width} "
Height = " {TemplateBinding Height} "
Fill = " {TemplateBinding Background} "
Stroke = " Purple " RadiusX = " 16 " RadiusY = " 16 " />
< ContentPresenter Content = " {TemplateBinding Content} "
HorizontalAlignment = " Center " VerticalAlignment = " Center " />
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
</ Style >
</ ResourceDictionary >

{
public event RoutedEventHandler Click;
public SimpleButton()
{
DefaultStyleKey = typeof (SimpleButton);
this .MouseLeftButtonUp +=
new MouseButtonEventHandler(SimpleButton_MouseLeftButtonUp);
this .MouseEnter +=
new MouseEventHandler(SimpleButton_MouseEnter);
this .MouseLeave +=
new MouseEventHandler(SimpleButton_MouseLeave);
}
void SimpleButton_MouseLeftButtonUp( object sender,
MouseButtonEventArgs e)
{
if (Click != null )
Click( this , new RoutedEventArgs());
}
void SimpleButton_MouseEnter( object sender, MouseEventArgs e)
{
VisualStateManager.GoToState( this , " MouseOver " , true );
}
void SimpleButton_MouseLeave( object sender, MouseEventArgs e)
{
VisualStateManager.GoToState( this , " Normal " , true );
}
}
完成!