注册了博客没有写一篇文章,浪费了空间。今天刚好跟同事聊到了silverlight 的控件自定义,写一篇 silverlight 2.0的也算是对以前的复习。
这个例子本身很简单,实现的是简单的带有动画的按钮,主要是谈谈其实现手法。其可分为几点一步一步实现:
1:创建一个silverlight 类库项目,我们将这个项目命名为:TestControl。然后在该项目下添加一个文件夹取名为“themes”,继续在文件下添加一个文本文件“generic.xaml”注意后缀名也修改了(具体为什么做下面说明)。大概样子如图:
接着该讨论这个控件的样式了,打开“generic.xaml”文件,默认是空白的,我们将在这边定义样式,先引入内嵌样式,导入命名空间:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml>
</ResourceDictionary>
完成这个步骤以后,在项目中添加一个类“TestControl.cs”,开发一个控件我们就要选择基类继承过来,这边选择的是Control,当然也可以从其他类继承具体是:
public class TestControl : Control
{
public TestControl()
{
this.DefaultStyleKey = typeof(TestControl);
}
}
这边添加了一个构造函数,在这边的作用是对默认样式的引用,上面我们提到了建立“themes”文件夹,.net会默认的从这个文件夹下面加载generic.xaml 作为控件的默认样式,这下清楚了(创建默认文件名称)吧。让我们继续添加样式吧,打开generic.xaml文件,把控件命名空间引用进来xmlns:local="clr-namespace:TestControl" 我把控件的引用命名为local,当然这边你可以根据自己的喜爱来命名这个。接下来就添加样式:
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid x:Name="RootElement" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Margin="0,0,0,0">
<Rectangle x:Name="bevelEllipse" Opacity="1" RadiusX="5" RadiusY="5">
<Rectangle.Fill>
<ImageBrush x:Name="ImageBrush1"></ImageBrush>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="bevelEllipse" Storyboard.TargetProperty="(Rectangle.Opacity)" From="1" To="0" RepeatBehavior="Forever"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这边如果有不解的可以去查看帮助文档,讲述的很详细。
这边矩形使用了一个图片的画刷,但是我们没有定义图片路径,这边就是接下来就说的在silverlight自定义属性,这个属性的定义可能有别与我们平时的属性定义,可以说有点复杂。在谈这个之前先说明一个特性 TemplatePart ,这个我们可以理解为我们定义一个复合控件,我们把控件分成很多部分,然而TemplatePart 就是用于描述每一个部分的定义。我这边只是为了简单的说所以控件我分成了两块,引用的代码:
[TemplatePart(Name = "bevelEllipse", Type = typeof(Rectangle))]
[TemplatePart(Name = "ImageBrush1", Type = typeof(ImageBrush))]
public class TestControl : Control
对这个特性有熟悉了,就可以继续定义属性这边具体就不多说了直接放上代码一看便知:
#region 定义属性
public static readonly DependencyProperty ImagePathProperty;
static TestControl()
{
ImagePathProperty = DependencyProperty.Register("ImagePath", typeof(ImageSource), typeof(TestControl),
new PropertyMetadata(null, new PropertyChangedCallback(TestControl.OnImagePropertyChanged)));
}
void OnImagePropertyChanged(DependencyPropertyChangedEventArgs e)
{
try
{
(GetTemplateChild("ImageBrush1") as ImageBrush).ImageSource = e.NewValue as ImageSource;
}
catch
{ }
}
private static void OnImagePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as TestControl).OnImagePropertyChanged(e);
}
public ImageSource ImagePath
{
get { return (ImageSource)base.GetValue(ImagePathProperty); }
set { base.SetValue(ImagePathProperty, value); }
}
#endregion
这边定义了静态构造函数,进行赋值,还有就是DependencyProperty.Register("ImagePath", typeof(ImageSource), typeof(TestControl)
这边的ImagePath 要和我们定义的属性名称一致,还有注册的类型对应。这样就定义了一个属性。
下面谈谈控件里面的事件是如果定义的,先说明这边跟自定义webControl 的事件处理是一样的方式,好了具体的描述一下:
定义一个参数的类型,都是从EventArgs 继承,代码如下:
public class TestEventArges : EventArgs
{
private Guid _guid;
public Guid TheGuid
{
get { return _guid; }
}
public TestEventArges(Guid guid)
{
this._guid = guid;
}
}
这边我们定义了一个Guid类型作为事件参数,定义这个其实没有什么实际的作用,只是为了测试我们定义的事件。
接下来我们声明委托:
public delegate void TestEventHander(object sender,TestEventArges e);
声明一个TestEventHander 的委托,用于自定义事件。
看看控件的事件定义:
#region 定义事件
public event TestEventHander TestEvented;
protected virtual void OnTestEvented(TestEventArges e)
{
if (TestEvented != null)
{
TestEvented(this, e);
}
}
#endregion
接着要做的事情就是把控件的事件,关联到触发事件上面,看看下面代码:
private Rectangle _clickEllipse;
private ImageBrush _imageBrush;
public TestControl()
{
this.DefaultStyleKey = typeof(TestControl);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_imageBrush = GetTemplateChild("ImageBrush1") as ImageBrush;
_imageBrush.ImageSource = this.ImagePath;
//添加Ellipse 引用
_clickEllipse = GetTemplateChild("bevelEllipse") as Rectangle;
_clickEllipse.MouseLeftButtonDown += new MouseButtonEventHandler(_clickEllipse_MouseLeftButtonDown);
}
void _clickEllipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Guid guid = Guid.NewGuid();
TestEventArges amyE = new TestEventArges(guid);
OnTestEvented(amyE);
}
这边重写基类的OnAppleTemplat()方法,这里面 声明了一个Rectangle 和ImageBrouh,对他们的引用是通过GetTemplateChild 方法,方法的参数是跟类的Templat特性定义的名称一样的,关联到样式里面的X:Name 名称的。
_clickEllipse = GetTemplateChild("bevelEllipse") as Rectangle;
_clickEllipse.MouseLeftButtonDown += new MouseButtonEventHandler(_clickEllipse_MouseLeftButtonDown);
}
void _clickEllipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Guid guid = Guid.NewGuid();
TestEventArges amyE = new TestEventArges(guid);
OnTestEvented(amyE);
}
取得Rectangle 的定义,定义出这个矩形的鼠标的事件,使用鼠标事件去触发我们自定义的事件,参数里面传入了一个Guid 。好的基本上这个控件算是完成了,下面是完全的代码:
1:样式:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestControl">
<Style TargetType="local:TestControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestControl">
<Grid x:Name="RootElement" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Margin="0,0,0,0">
<Rectangle x:Name="bevelEllipse" Opacity="1" RadiusX="5" RadiusY="5">
<Rectangle.Fill>
<ImageBrush x:Name="ImageBrush1"></ImageBrush>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="bevelEllipse" Storyboard.TargetProperty="(Rectangle.Opacity)" From="1" To="0" RepeatBehavior="Forever"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
2:后台定义
[TemplatePart(Name = "bevelEllipse", Type = typeof(Rectangle))]
[TemplatePart(Name = "ImageBrush1", Type = typeof(ImageBrush))]
public class TestControl : Control
{
#region 定义属性
public static readonly DependencyProperty ImagePathProperty;
static TestControl()
{
ImagePathProperty = DependencyProperty.Register("ImagePath", typeof(ImageSource), typeof(TestControl),
new PropertyMetadata(null, new PropertyChangedCallback(TestControl.OnImagePropertyChanged)));
}
void OnImagePropertyChanged(DependencyPropertyChangedEventArgs e)
{
try
{
(GetTemplateChild("ImageBrush1") as ImageBrush).ImageSource = e.NewValue as ImageSource;
}
catch
{ }
}
private static void OnImagePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as TestControl).OnImagePropertyChanged(e);
}
public ImageSource ImagePath
{
get { return (ImageSource)base.GetValue(ImagePathProperty); }
set { base.SetValue(ImagePathProperty, value); }
}
#endregion
private Rectangle _clickEllipse;
private ImageBrush _imageBrush;
public TestControl()
{
this.DefaultStyleKey = typeof(TestControl);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_imageBrush = GetTemplateChild("ImageBrush1") as ImageBrush;
_imageBrush.ImageSource = this.ImagePath;
//添加Ellipse 引用
_clickEllipse = GetTemplateChild("bevelEllipse") as Rectangle;
_clickEllipse.MouseLeftButtonDown += new MouseButtonEventHandler(_clickEllipse_MouseLeftButtonDown);
}
void _clickEllipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Guid guid = Guid.NewGuid();
TestEventArges amyE = new TestEventArges(guid);
OnTestEvented(amyE);
}
#region 定义事件
public event TestEventHander TestEvented;
protected virtual void OnTestEvented(TestEventArges e)
{
if (TestEvented != null)
{
TestEvented(this, e);
}
}
#endregion
}
public delegate void TestEventHander(object sender,TestEventArges e);
接下来就是测试我们的控件了,添加一个silverlight 应用程序 TestTimeClock ,然后添加项目引用。在默认的Page.xaml 添加如下测试代码:
<UserControl x:Class="TestTimeClock.page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myContron="clr-namespace:TestControl;assembly=TestControl">
<Grid x:Name="LayoutRoot">
<myContron:TestControl Width="400" Height="300" ImagePath="tu.jpg" TestEvented="TestControl_TestEvented">
</myContron:TestControl>
</Grid>
</UserControl>
后台代码:
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void TestControl_TestEvented(object sender, TestControl.TestEventArges e)
{
MessageBox.Show("控件的Guid是:" + e.TheGuid);
}
}
运行就可以看到效果了:
可以比较每次的输出Guid都不相同。