赞助商
我们知道在html中有一个marquee标签,可以很方便的实现文字滚动的效果,比如如下简单的声明:
  1. <marquee loop="3" behavior="scroll">文本信息<marquee>
复制代码
在WPF里面,当然,我们可以用Animation和Storyboard来达到同样的效果,但是感觉总是不那么好,每次都需要做动画。而且设置动画的属性很麻烦。能不能就像HTML简单的声明就行了呢?比如:
  1. <l:Marquee C Direction="Right" Behavior="Scroll" ScrollAmount="20" ScrollDelay="500"/>
复制代码
能够这样简单的指定属性来使用。

下面我们就用开发CustomControl的方式来打造一个Marquee控件。(文/智者千虑

首先,我们添加一个自定义控件,这个控件继承自ContentControl,因为它的里面可以放任何东西,而不仅仅限于文本。

接着,仿造html中的marquee标签,定义一些必要的属性。这些属性都是DependencyProperty。

附件: Marquee_Classes.jpg

接下来我们需要写动画的逻辑了。有三种方式来写这个动画:

1、利用系统的Animation和Storyboard

2、在CompositionTarget的Rendering事件中写动画

3、利用DispatcherTimer写动画

我们这儿动画的行为有好几种,比较复杂,利用系统的动画方式来写会很麻烦,并且这儿有一个ScrollDelay属性来控制延时,在CompositionTarget的Rendering事件中不太好控制,因此,最后我选择用DispatcherTimer,在它的Tick事件中写动画逻辑。

动画的逻辑主要就是改变Content的位移,需要注意的是,这个位移是通过TranlateTransform来实现的。

部分代码如下:
  1. protected virtual void OnUpdateContentPosition()
  2.         {
  3.             if (_contentHost != null)
  4.             {
  5.                 double contentWidth = _contentHost.DesiredSize.Width;
  6.                 double leftBound = 0.0, rightBound = 0.0;

  7.                 // Alternate不飞出边界
  8.                 if (this.Behavior == MarqueeBehavior.Alternate || this.Behavior == MarqueeBehavior.Slide)
  9.                 {
  10.                     leftBound = 0;
  11.                     rightBound = this.ActualWidth - contentWidth;
  12.                 }
  13.                 // Scroll要飞出边界
  14.                 else if (this.Behavior == MarqueeBehavior.Scroll)
  15.                 {
  16.                     leftBound = -contentWidth;
  17.                     rightBound = this.ActualWidth;
  18.                 }

  19.                 // 循环次数的计数
  20.                 if (_currentLoop < this.Loop)
  21.                 {
  22.                     // 计算位移
  23.                     // 这里逻辑比较重复,可以考虑合并
  24.                     if (_currentDirection == Direction.Left)
  25.                     {
  26.                         _contentTranlate.X -= this.ScrollAmount;

  27.                         // 从右往左到头了
  28.                         if (_contentTranlate.X <= leftBound)
  29.                         {
  30.                             if (this.Behavior == MarqueeBehavior.Scroll || this.Behavior == MarqueeBehavior.Slide)
  31.                             {
  32.                                 _contentTranlate.X = rightBound;
  33.                             }
  34.                             else if (this.Behavior == MarqueeBehavior.Alternate)
  35.                             {
  36.                                 _currentDirection = Direction.Right;
  37.                             }
  38.                             _currentLoop += _loopCounter;
  39.                         }
  40.                     }
  41.                     else if (_currentDirection == Direction.Right)
  42.                     {
  43.                         _contentTranlate.X += this.ScrollAmount;
  44.                         // 从左往右到头了
  45.                         if (_contentTranlate.X >= rightBound)
  46.                         {
  47.                             if (this.Behavior == MarqueeBehavior.Scroll || this.Behavior == MarqueeBehavior.Slide)
  48.                             {
  49.                                 _contentTranlate.X = leftBound;

  50.                             }
  51.                             else if (this.Behavior == MarqueeBehavior.Alternate)
  52.                             {
  53.                                 _currentDirection = Direction.Left;
  54.                             }
  55.                             _currentLoop += _loopCounter;
  56.                         }
  57.                     }
  58.                 }// End of Loop
  59.                 else
  60.                 {
  61.                     // 保证动画结束后可见
  62.                     if (_contentTranlate.X < 0)
  63.                     {
  64.                         _contentTranlate.X = 0;
  65.                     }
  66.                     if (_contentTranlate.X > this.ActualWidth - contentWidth)
  67.                     {
  68.                         _contentTranlate.X = this.ActualWidth - contentWidth;
  69.                     }
  70.                 }
  71.             }
  72.         }
复制代码
更详尽的内容可以直接参看源码。

使用的时候非常简单
  1.             <!--文字可以-->
  2.             <l:Marquee Content="123" Direction="Right" Behavior="Scroll" ScrollAmount="20" ScrollDelay="500"/>
  3.             <!--图形也可以-->
  4.             <l:Marquee Direction="Right" Behavior="Slide">
  5.                 <Rectangle Width="20" Height="20" Fill="Blue"/>
  6.             </l:Marquee>
  7.             <!--控件也没有问题-->
  8.             <l:Marquee>
  9.                 <Button Content="Button" Width="50"/>
  10.             </l:Marquee>
复制代码
附件: Marquee_Result.jpg


需要注意的是,如果设定了Loop,当Loop结束的时候,我并没有停止Timer,这样在Window Resize之后动画会重新播放。读者可以将选择其Stop掉,而在ResetAnimation方法中重新启动。

源码下载:
赞助商
赞助商
TOP