WPF字幕文本动画在其他控件之间滚动



我在下面给出XAML的窗口中实现了这个问题的解决方案。我正在尝试为标签制作滚动字幕文本效果:

<Window x:Class="WpfMarqueeText.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfMarqueeText="clr-namespace:WpfMarqueeText"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="500"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Row="0" Grid.Column="0" Background="Aqua">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200"/>
                <ColumnDefinition Width="300"/>
            </Grid.ColumnDefinitions>
            <Ellipse Grid.Column="0" Margin="5,3,5,3" Fill="#b933ad"/>
            <Label Grid.Column="0" Content="Z" Foreground="White" FontFamily="HelveticaBold" FontSize="150" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,3,5,3"/>
            <Grid Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Grid Grid.Row="0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Label Grid.Row="0" Grid.Column="0" Content="Some Info:" FontFamily="HelveticaBold" FontSize="18" FontWeight="Bold" Margin="5,3,5,3"/>
                    <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" x:Name="stack">
                        <StackPanel.Resources>
                            <wpfMarqueeText:NegatingConverter x:Key="NegatingConverter" />
                            <Storyboard x:Key="slide">
                                <DoubleAnimation From="0" To="{Binding Width, ElementName=canvas, Converter={StaticResource NegatingConverter}}" Duration="00:00:10"
                                Storyboard.TargetProperty="X"
                                Storyboard.TargetName="transferCurreny"
                                RepeatBehavior="Forever"/>
                            </Storyboard>
                        </StackPanel.Resources>
                        <StackPanel.RenderTransform>
                            <TranslateTransform x:Name="transferCurreny" X="0"/>
                        </StackPanel.RenderTransform>
                        <StackPanel.Triggers>
                            <EventTrigger RoutedEvent="StackPanel.Loaded">
                                <BeginStoryboard Storyboard="{StaticResource slide}" />
                            </EventTrigger>
                            <EventTrigger RoutedEvent="StackPanel.SizeChanged">
                                <BeginStoryboard Storyboard="{StaticResource slide}" />
                            </EventTrigger>
                        </StackPanel.Triggers>
                        <Canvas x:Name="canvas" Width="{Binding ActualWidth, ElementName=stack}">
                            <Label FontFamily="HelveticaBold" FontSize="18" Margin="5,3,5,3"  x:Name="Label1" Content="Blah blah blah" Canvas.Left="0"/>
                            <Label Name="Label2" Content="{Binding Content, ElementName=Label1}" FontFamily="HelveticaBold" FontSize="18" Margin="5,3,5,3" Canvas.Left="{Binding ActualWidth, ElementName=stack}"/>
                        </Canvas>
                    </StackPanel>
                </Grid>
            </Grid>
        </Grid>
    </Grid>
</Window>

您还必须在代码背后定义NegatingConverter类:

public class NegatingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double)
        {
            return -((double)value);
        }
        return value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double)
        {
            return +(double)value;
        }
        return value;
    }
}

这会产生所需的效果,但文本动画会滚动到其他UI元素上,如下图所示(对不起,我没有足够的代表来发布图像):

http://tinypic.com/r/df8zeu/9http://tinypic.com/r/2inc3r/9

那么,有没有任何方法可以修复动画,使文本只在包含它的栅格列的边界内滚动,或者在标签本身的边界内?谢谢你的帮助!

这是一个快速而肮脏的解决方案:

Label更改为

<Label Grid.Row="0" Grid.Column="0" Content="Some Info:" FontFamily="HelveticaBold" FontSize="18" FontWeight="Bold" Margin="5,3,5,3" Panel.ZIndex="99" Background="Aqua"/>

Panel.ZIndexLabel带到前面。并且使Background不透明可以得到所需的外观。边界仍然不完美,但这应该会给你一个如何处理Layers 的线索

Akanksha为回应我的OP而链接的文章展示了如何创建一个能产生干净滚动文本效果的用户控件。您还可以指定4个不同的滚动方向,向左<->向右和向上<->向下我将在这里为其他人提供我的实现:

MarqueTextUserControl:的XAML

<UserControl x:Class="AaronLuna.Common.UI.UserControls.MarqueeTextUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" Loaded="UserControl_Loaded">
    <Canvas ClipToBounds="True" Name="CanvasMain">
        <TextBlock Name="TextBlockMain"/>
    </Canvas>
</UserControl>

MarqueTextUserControl:的代码

namespace AaronLuna.Common.UI.UserControls
{        
    public partial class MarqueeTextUserControl
    {
        public MarqueeTextUserControl()
        {
            InitializeComponent();
            CanvasMain.Height = Height;
            CanvasMain.Width = Width;
        }
        public ScrollDirection ScrollDirection { get; set; }
        public double ScrollDurationInSeconds { get; set; }
        public String Text { set { TextBlockMain.Text = value; }}
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            ScrollText(ScrollDirection);
        }
        public void ScrollText(ScrollDirection scrollDirection)
        {
            switch (scrollDirection)
            {
                case ScrollDirection.LeftToRight:
                    LeftToRightMarquee();
                    break;
                case ScrollDirection.RightToLeft:
                    RightToLeftMarquee();
                    break;
                case ScrollDirection.TopToBottom:
                    TopToBottomMarquee();
                    break;
                case ScrollDirection.BottomToTop:
                    BottomToTopMarquee();
                    break;
            }
        }
        private void LeftToRightMarquee()
        {
            double height = CanvasMain.ActualHeight - TextBlockMain.ActualHeight;
            TextBlockMain.Margin = new Thickness(0, height/2, 0, 0);
            var doubleAnimation = new DoubleAnimation
            {
                From = -TextBlockMain.ActualWidth,
                To = CanvasMain.ActualWidth,
                RepeatBehavior = RepeatBehavior.Forever,
                Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
            };
            TextBlockMain.BeginAnimation(Canvas.LeftProperty, doubleAnimation);
        }
        private void RightToLeftMarquee()
        {
            double height = CanvasMain.ActualHeight - TextBlockMain.ActualHeight;
            TextBlockMain.Margin = new Thickness(0, height/2, 0, 0);
            var doubleAnimation = new DoubleAnimation
            {
                From = -TextBlockMain.ActualWidth,
                To = CanvasMain.ActualWidth,
                RepeatBehavior = RepeatBehavior.Forever,
                Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
            };
            TextBlockMain.BeginAnimation(Canvas.RightProperty, doubleAnimation);
        }
        private void TopToBottomMarquee()
        {
            double width = CanvasMain.ActualWidth - TextBlockMain.ActualWidth;
            TextBlockMain.Margin = new Thickness(width/2, 0, 0, 0);
            var doubleAnimation = new DoubleAnimation
            {
                From = -TextBlockMain.ActualHeight,
                To = CanvasMain.ActualHeight,
                RepeatBehavior = RepeatBehavior.Forever,
                Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
            };
            TextBlockMain.BeginAnimation(Canvas.TopProperty, doubleAnimation);
        }
        private void BottomToTopMarquee()
        {
            double width = CanvasMain.ActualWidth - TextBlockMain.ActualWidth;
            TextBlockMain.Margin = new Thickness(width/2, 0, 0, 0);
            var doubleAnimation = new DoubleAnimation
            {
                From = -TextBlockMain.ActualHeight,
                To = CanvasMain.ActualHeight,
                RepeatBehavior = RepeatBehavior.Forever,
                Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
            };
            TextBlockMain.BeginAnimation(Canvas.BottomProperty, doubleAnimation);
        }
    }
    public enum ScrollDirection
    {
        LeftToRight,
        RightToLeft,
        TopToBottom,
        BottomToTop
    }
}

客户端XAML:

<UserControl x:Class="MarqueeTextExampleUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:userControls="clr-namespace:AaronLuna.Common.UI.UserControls;assembly=AaronLuna.Common"
         mc:Ignorable="d">
    <DockPanel>
        <Label Content="Some Info:"/>
        <userControls:MarqueeTextUserControl x:Name="MarqueeTextBlock"/>
    </DockPanel>
</UserControl>

客户代码:

MarqueeTextBlock.Text = "Blah blah blah";
MarqueeTextBlock.ScrollDirection = ScrollDirection.RightToLeft;
MarqueeTextBlock.ScrollDurationInSeconds = 10;

最新更新