Silverlight自定义控件

      最近需要实现一个立方体的功能,刚开始用的第三方控件Telerik中的RadCube,但是发现很难用,似乎长高宽都要设置为一致,而且不能
控制立方体转动的方式,所以自己想写个组件来实现。去看了下Silverlight的组件化,就开始动手了。
Silverlight的组件化,需要2个文件一个是组建的类文件:MyCube.cs,另外一个是Generic.xaml文件。
MyCube.cs
 
  
ContractedBlock.gif ExpandedBlockStart.gif 代码
 
    
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Tecamo.Silverlight
{
[TemplatePart(Name
= MyCube.RootElement, Type = typeof (FrameworkElement))]
[TemplatePart(Name
= MyCube.segment1, Type = typeof (Grid))]
[TemplatePart(Name
= MyCube.segment2, Type = typeof (Grid))]
[TemplatePart(Name
= MyCube.segment3, Type = typeof (Grid))]
[TemplatePart(Name
= MyCube.storyboard, Type = typeof (Storyboard))]

public class MyCube : ContentControl
{
private const string RootElement = " RootElement " ;
private const string segment1 = " segment1 " ;
private const string segment2 = " segment2 " ;
private const string segment3 = " segment3 " ;
private const string storyboard = " storyboard " ;



private FrameworkElement _root;
private Grid _segment1;
private Grid _segment2;
private Grid _segment3;
private Storyboard _storyboard;
private DoubleAnimation _DAnimation1;
private DoubleAnimation _DAnimation2;
private DoubleAnimation _DAnimation3;
private PlaneProjection _projection1;
private PlaneProjection _projection2;
private PlaneProjection _projection3;

public UIElement Slide1 { get ; set ; }
public UIElement Slide2 { get ; set ; }
public UIElement Slide3 { get ; set ; }

private int temp;
private bool noMoreTriggers;



public MyCube()
{
DefaultStyleKey
= typeof (MyCube);
noMoreTriggers
= false ;
temp
= 1 ;

this .SizeChanged += new SizeChangedEventHandler(MyCube_SizeChanged);
this .Loaded += new RoutedEventHandler(MyCube_Loaded);
}

void MyCube_Loaded( object sender, RoutedEventArgs e)
{
double d = ( this ._root.ActualWidth) * 0.2885 ;
this ._projection1.CenterOfRotationZ = - d;
this ._projection2.CenterOfRotationZ = - d;
this ._projection3.CenterOfRotationZ = - d;
}

void MyCube_SizeChanged( object sender, SizeChangedEventArgs e)
{
double d = ( this ._root.ActualWidth) * 0.2885 ;
this ._projection1.CenterOfRotationZ = - d;
this ._projection2.CenterOfRotationZ = - d;
this ._projection3.CenterOfRotationZ = - d;
}


public void Spin( int x, int y)
{

if ( this .noMoreTriggers)
return ;
noMoreTriggers
= true ;
temp
= y;
if ((x - y) == 1 || (x - y) == - 2 )
{
this ._DAnimation1.By = - 120 ;
this ._DAnimation2.By = - 120 ;
this ._DAnimation3.By = - 120 ;
}
else if ((x - y) == - 1 || (x - y) == 2 )
{
this ._DAnimation1.By = 120 ;
this ._DAnimation2.By = 120 ;
this ._DAnimation3.By = 120 ;
}
switch (y)
{
case 1 :
Canvas.SetZIndex(
this ._segment1, 2 );
break ;
case 2 :
Canvas.SetZIndex(
this ._segment2, 2 );
break ;
case 3 :
Canvas.SetZIndex(
this ._segment3, 2 );
break ;
default :
break ;
}

this ._storyboard.Begin();
}

private void OnStoryboardCompleted1( object sender, EventArgs args)
{
this .noMoreTriggers = false ;
}

public override void OnApplyTemplate()
{
base .OnApplyTemplate();

_root
= (FrameworkElement)GetTemplateChild(MyCube.RootElement);
_segment1
= (Grid)GetTemplateChild(MyCube.segment1);
_segment2
= (Grid)GetTemplateChild(MyCube.segment2);
_segment3
= (Grid)GetTemplateChild(MyCube.segment3);

if (_segment1 != null )
{
Canvas.SetZIndex(_segment1,
2 );
_projection1
= new PlaneProjection();
_segment1.Projection
= _projection1;

_DAnimation1
= new DoubleAnimation();
_DAnimation1.By
= 120 ;
Storyboard.SetTarget(_DAnimation1, _projection1);
Storyboard.SetTargetProperty(_DAnimation1,
new PropertyPath(PlaneProjection.RotationYProperty));
if (_segment1.Children.Count > 0 )
this .Slide1 = _segment1.Children[ 0 ];
}
if (_segment2 != null )
{
Canvas.SetZIndex(_segment2,
1 );
_projection2
= new PlaneProjection();
_projection2.RotationY
= - 120 ;
_segment2.Projection
= _projection2;
_DAnimation2
= new DoubleAnimation();
_DAnimation2.By
= 120 ;
Storyboard.SetTarget(_DAnimation2, _projection2);
Storyboard.SetTargetProperty(_DAnimation2,
new PropertyPath(PlaneProjection.RotationYProperty));
if (_segment2.Children.Count > 0 )
this .Slide2 = _segment2.Children[ 0 ];
}
if (_segment3 != null )
{
Canvas.SetZIndex(_segment3,
0 );
_projection3
= new PlaneProjection();
_projection3.RotationY
= 120 ;
_segment3.Projection
= _projection3;
_DAnimation3
= new DoubleAnimation();
_DAnimation3.By
= 120 ;
Storyboard.SetTarget(_DAnimation3, _projection3);
Storyboard.SetTargetProperty(_DAnimation3,
new PropertyPath(PlaneProjection.RotationYProperty));
if (_segment3.Children.Count > 0 )
this .Slide3 = _segment3.Children[ 0 ];
}
if (_root != null )
{
_storyboard
= new Storyboard();
_storyboard.Children.Clear();
if (_DAnimation1 != null )
_storyboard.Children.Add(_DAnimation1);
if (_DAnimation2 != null )
_storyboard.Children.Add(_DAnimation2);
if (_DAnimation3 != null )
_storyboard.Children.Add(_DAnimation3);
this ._storyboard.Completed += new EventHandler( this .OnStoryboardCompleted1);
}
}
}
}

 

Generic.xaml
 
  
ContractedBlock.gif ExpandedBlockStart.gif 代码
 
    
< ResourceDictionary
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mycube
="clr-namespace:Tecamo.Silverlight;assembly=Tecamo.Silverlight" >
< Style TargetType ="mycube:MyCube" >
< Setter Property ="Width" Value ="380" />
< Setter Property ="Height" Value ="200" />
< Setter Property ="Template" >
< Setter.Value >
< ControlTemplate TargetType ="mycube:MyCube" >
< Grid x:Name ="RootElement" Background =" {TemplateBinding Background} "
Width
=" {TemplateBinding Width} " Height =" {TemplateBinding Height} " >
< Grid Name ="segment1"
Background
=" {TemplateBinding Background} " >

</ Grid >

< Grid Name ="segment2"
Background
=" {TemplateBinding Background} " >

</ Grid >

< Grid Name ="segment3"
Background
=" {TemplateBinding Background} " >

</ Grid >
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
</ Style >
</ ResourceDictionary >
MyCube.cs中写了个MyCube类,写了个OnApplyTemplate方法,当在其他的xaml中实现这个MyCube类的时候会调用到这个方法。
 
  
ContractedBlock.gif ExpandedBlockStart.gif 代码
 
    
< cube:MyCube x:Name ="MyCube1" Margin ="5,35,5,5" >
< cube:MyCube.Template >
< ControlTemplate >
< Grid x:Name ="RootElement" >
< Grid x:Name ="segment1" Background ="#F5BAD5C2" >
< Button />
</ Grid >
< Grid x:Name ="segment2" Background ="#F5BAD5C2" >
< TextBlock />
</ Grid >
< Grid x:Name ="segment3" Background ="#F5BAD5C2" >
< Border /> </ Grid >
</ Grid >
</ ControlTemplate >
</ cube:MyCube.Template >
</ cube:MyCube >

 

通过_root = (FrameworkElement)GetTemplateChild(MyCube.RootElement);就可以取到这个Grid,当然也能取到它的Children。
         segment1、segment2、segment3为这个控件的三个面,当然也可以是四个面,六个面,三个面的代码写起来最方便,所以我写的控件是个三棱柱。
通过设置这三个Grid的Projection属性,可以设置其RotationY属性,就是Y轴上的旋转角度,通过控制这个属性来做三棱柱的转动的动画。
   当然还需要设置Canvas.SetZIndex属性,这个属性决定哪个面显示在其他2个面的上方,值最大的在最上方。
另外还需要设置CenterOfRotationZ属性,这个是每个面离围绕的旋转轴的距离,这个属性只与三棱柱的每个面的宽有关,具体的运算公式为
CenterOfRotationZ=Width*0.2885 至于这个公式怎么来的,因为我数学很惨不忍睹,所以不知道这个公式的数学原理,其中Width为每个面的宽度。
而且在每次 控件的每个面的宽度发生变化时都要重新计算CenterOfRotationZ。要控制其旋转只要调用  Spin(1,2)方法就行了,带的参数为三棱柱的面
的编号,Spin(1,2)则是从第1面转动到第2面。
这样就大致实现所需要的功能了,这里可以看下示例

5542838620100630155730015_640.jpg
在线演示

但是在用到这个控件的时候格式必须为

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
    
< cube:MyCube x:Name ="MyCube1" Margin ="5,35,5,5" >
< cube:MyCube.Template >
< ControlTemplate >
< Grid x:Name ="RootElement" >
< Grid x:Name ="segment1" Background ="#F5BAD5C2" >
< Button />
</ Grid >
< Grid x:Name ="segment2" Background ="#F5BAD5C2" >
< TextBlock />
</ Grid >
< Grid x:Name ="segment3" Background ="#F5BAD5C2" >
< Border /> </ Grid >
</ Grid >
</ ControlTemplate >
</ cube:MyCube.Template >
</ cube:MyCube >
如果想像用其他控件那样方便,就要写UI自动化了,我简单看了下,没什么头绪,这里还请知道的朋友解决下这个问题。另外,写在MyCube 中的子元素,
就算命名了业不能在后台代码中设置这个子元素,所以我写了写个Public UIElement属性来get;set;这个也是写 了 UI自动化就能解决的。

转载于:https://www.cnblogs.com/yifan_he/archive/2010/07/30/1788973.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值