Search code examples
c#.netwpfanimationmarquee

WPF Marquee Text Animation


I can scroll text with TranslateTransform but when the animation is close to finishing I'd like it to begin again. Like a snake :)

This is what I've got:

<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
    <StackPanel.RenderTransform>
        <TranslateTransform x:Name="transferCurreny" X="-40"/>
    </StackPanel.RenderTransform>
    <StackPanel.Triggers>
        <EventTrigger RoutedEvent="StackPanel.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation From="0" To="-900" Duration="00:00:10"
                      Storyboard.TargetProperty="X"
                      Storyboard.TargetName="transferCurreny"
                      RepeatBehavior="Forever"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </StackPanel.Triggers>
    <TextBlock FontSize="25"  x:Name="txtKron" Margin="10,0,7,0"/>
</StackPanel>

This is what I'd like:

enter image description here


Solution

  • Something like this should do the trick.

    You can add a Canvas to the StackPanel with 2 TextBlocks one set to position 0 and one set to the ActualWidth of the StackPanel, then when the first block of text goes offscreen the other block will come into view.

    The reason I used Canvas is because Canvas is the only element that actually supports ClipToBounds="false" this allows the 2nd TextBlock to be visible even if its placed outside the bounds of the Canvas itself

    We also need a IValueConverter to get the correct negative value if you want to scroll from right to left.

    I also added event trigger on SizeChanged so if the window is resized the animation values will update correctly.

    Code:

    namespace WpfApplication9
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
    
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        public class NegatingConverter : IValueConverter
        {
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is double)
                {
                    return -((double)value);
                }
                return value;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is double)
                {
                    return +(double)value;
                }
                return value;
            }
        }
    }
    

    Xaml:

    <Window x:Class="WpfApplication9.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication9"
            Title="MainWindow" Height="83" Width="222" Name="UI" Tag="Tol Level">
        <StackPanel Orientation="Horizontal" x:Name="stack">
            <StackPanel.Resources>
                <local: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}">
                <TextBlock Text="StackOverflow" FontSize="25"  x:Name="txtKron" Canvas.Left="0"/>
                <TextBlock Text="{Binding Text, ElementName=txtKron}" FontSize="25" Canvas.Left="{Binding Width, ElementName=canvas}"/>
            </Canvas>
        </StackPanel>
    </Window>
    

    Result:

    enter image description here enter image description here

    Edit: Left to Right

     <StackPanel Orientation="Horizontal" x:Name="stack">
            <StackPanel.Resources>
                <local:NegatingConverter x:Key="NegatingConverter" />
                <Storyboard x:Key="slide">
                    <DoubleAnimation From="0" To="{Binding Width, ElementName=canvas}" 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}">
                <TextBlock Text="StackOverflow" FontSize="25"  x:Name="txtKron" Canvas.Left="0"/>
                <TextBlock Text="{Binding Text, ElementName=txtKron}" FontSize="25" Canvas.Left="{Binding Width, ElementName=canvas, Converter={StaticResource NegatingConverter}}"/>
            </Canvas>
        </StackPanel>