我们知道在html中有一个marquee标签,可以很方便的实现文字滚动的效果,比如如下简单的声明:
- <marquee loop="3" behavior="scroll">文本信息<marquee>
复制代码在WPF里面,当然,我们可以用Animation和Storyboard来达到同样的效果,但是感觉总是不那么好,每次都需要做动画。而且设置动画的属性很麻烦。能不能就像HTML简单的声明就行了呢?比如:
- <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来实现的。
部分代码如下:
- protected virtual void OnUpdateContentPosition()
- {
- if (_contentHost != null)
- {
- double contentWidth = _contentHost.DesiredSize.Width;
- double leftBound = 0.0, rightBound = 0.0;
- // Alternate不飞出边界
- if (this.Behavior == MarqueeBehavior.Alternate || this.Behavior == MarqueeBehavior.Slide)
- {
- leftBound = 0;
- rightBound = this.ActualWidth - contentWidth;
- }
- // Scroll要飞出边界
- else if (this.Behavior == MarqueeBehavior.Scroll)
- {
- leftBound = -contentWidth;
- rightBound = this.ActualWidth;
- }
- // 循环次数的计数
- if (_currentLoop < this.Loop)
- {
- // 计算位移
- // 这里逻辑比较重复,可以考虑合并
- if (_currentDirection == Direction.Left)
- {
- _contentTranlate.X -= this.ScrollAmount;
- // 从右往左到头了
- if (_contentTranlate.X <= leftBound)
- {
- if (this.Behavior == MarqueeBehavior.Scroll || this.Behavior == MarqueeBehavior.Slide)
- {
- _contentTranlate.X = rightBound;
- }
- else if (this.Behavior == MarqueeBehavior.Alternate)
- {
- _currentDirection = Direction.Right;
- }
- _currentLoop += _loopCounter;
- }
- }
- else if (_currentDirection == Direction.Right)
- {
- _contentTranlate.X += this.ScrollAmount;
- // 从左往右到头了
- if (_contentTranlate.X >= rightBound)
- {
- if (this.Behavior == MarqueeBehavior.Scroll || this.Behavior == MarqueeBehavior.Slide)
- {
- _contentTranlate.X = leftBound;
- }
- else if (this.Behavior == MarqueeBehavior.Alternate)
- {
- _currentDirection = Direction.Left;
- }
- _currentLoop += _loopCounter;
- }
- }
- }// End of Loop
- else
- {
- // 保证动画结束后可见
- if (_contentTranlate.X < 0)
- {
- _contentTranlate.X = 0;
- }
- if (_contentTranlate.X > this.ActualWidth - contentWidth)
- {
- _contentTranlate.X = this.ActualWidth - contentWidth;
- }
- }
- }
- }
复制代码更详尽的内容可以直接参看源码。
使用的时候非常简单
- <!--文字可以-->
- <l:Marquee Content="123" Direction="Right" Behavior="Scroll" ScrollAmount="20" ScrollDelay="500"/>
- <!--图形也可以-->
- <l:Marquee Direction="Right" Behavior="Slide">
- <Rectangle Width="20" Height="20" Fill="Blue"/>
- </l:Marquee>
- <!--控件也没有问题-->
- <l:Marquee>
- <Button Content="Button" Width="50"/>
- </l:Marquee>
复制代码附件:
Marquee_Result.jpg 需要注意的是,如果设定了Loop,当Loop结束的时候,我并没有停止Timer,这样在Window Resize之后动画会重新播放。读者可以将选择其Stop掉,而在ResetAnimation方法中重新启动。
源码下载: