18

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

Cosmin Prund
  • 24,972
  • 2
  • 56
  • 102
meymetkaplan
  • 183
  • 1
  • 1
  • 8
  • What's the question? What's the desired behavior, what's the behavior you get? – Cosmin Prund Mar 10 '13 at 14:38
  • Question is; i want to marquee text but like as snake – meymetkaplan Mar 10 '13 at 14:44
  • I still don't understand. Can you paint a picture? Or point us to a place where you've seen this effect before? – Cosmin Prund Mar 10 '13 at 14:47
  • Picture; http://postimage.org/image/k0x636tgx/ – meymetkaplan Mar 10 '13 at 15:20
  • +1 and edited for clarity. I don't think you can do it with one ``, you probably need two or more. I tried putting something together but for me the translate animation would only show stuff that was visible on screen when the animation started. Kind of useless this using two TextBlock's requires one to be offscreen when the animation starts. – Cosmin Prund Mar 10 '13 at 16:04
  • can anyone please help me with this: http://stackoverflow.com/questions/21933660/wpf-text-marquee-animation/21934028 –  Feb 21 '14 at 15:45

4 Answers4

16

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>
sa_ddam213
  • 39,994
  • 7
  • 93
  • 106
  • Hi It's really great. I'm working on it buttom to top :) but i have one problem about this; when first block to top (actually one image) it's going to top=0 but image is almost top=50, i didn't fix this. Thank you! – meymetkaplan Mar 19 '13 at 18:32
  • There is a problem here - The animation does not start. I fixed it in the code behind by adding a Loaded event and in it: Storyboard sb = (Storyboard) this.stack.FindResource("slide"); stack.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { sb.Begin(); })); – Tal Segal Jun 03 '13 at 11:49
  • 1
    Remove the converter from the animations `To` property and add it to the `Canvas.Left` property of the last `TextBlock`, added example above – sa_ddam213 Nov 19 '13 at 07:56
  • A quick heads-up : in the ConvertBack method of the converter in your solution the + (unary) operator does nothing. You should use the same code as in Convert to ConvertBack. – Andrei Rînea Mar 05 '14 at 09:27
  • @sa_ddam213 I try with your code . but i got error in local:NegatingConverter x:Key="NegatingConverter" ----The name "NegatingConverter" does not exist in the namespace. how to solve it? – Gurunathan Mar 13 '14 at 07:17
  • 3
    What about when the text is long and cannot fit in the TextBlock? Then the two TextBlock overlap and things get ugly. – SepehrM Jun 10 '14 at 06:46
  • 1
    Shouldn't the `ConvertBack` method be the same as `Convert` in the NegatingConverter? – Alfie Mar 13 '19 at 16:30
  • 1
    Hi this works well but there is an overlap when the text is quite long. Do you know how to fix that? –  Aug 03 '20 at 16:32
3

The code in above answer does not produce continuous scroll. Here is the code for continuous smooth scroll.

XAML:

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Canvas Margin="6,83,9,0" Name="ViewingBox" Background="YellowGreen" Height="35" VerticalAlignment="Top">
            <Label Canvas.Left="263" Canvas.Top="-2" Height="49" Name="BoxOne" FontSize="20">I need breakfast.</Label>
            <Label Canvas.Left="263" Canvas.Top="-2" Height="49" HorizontalAlignment="Stretch" Name="BoxTwo" VerticalAlignment="Top" FontSize="20">You can have oranges and egg.</Label>
        </Canvas>   
    </Grid>
</Window>

VB Code Behind:

Imports System.Windows.Media.Animation

Public Enum Texts
    BoxOne
    BoxTwo
End Enum

Class Window1
    Private dubAnim As New DoubleAnimation()
    Private dubAnim2 As New DoubleAnimation()
    Private NewsTimer As New Windows.Threading.DispatcherTimer()
    Dim leadText As Texts = Texts.BoxOne

    Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        dubAnim.From = ViewingBox.ActualWidth
        dubAnim.To = -BoxOne.ActualWidth
        dubAnim.SpeedRatio = 0.05
        AddHandler dubAnim.Completed, AddressOf dubAnim_Completed
        Timeline.SetDesiredFrameRate(dubAnim, 320)
        BoxOne.BeginAnimation(Canvas.LeftProperty, dubAnim)

        dubAnim2.From = ViewingBox.ActualWidth
        dubAnim2.To = -BoxTwo.ActualWidth
        dubAnim2.SpeedRatio = 0.05
        Timeline.SetDesiredFrameRate(dubAnim2, 320)
        AddHandler dubAnim2.Completed, AddressOf dubAnim2_Completed

        AddHandler NewsTimer.Tick, AddressOf NewsTimer_Tick
        NewsTimer.Interval = New TimeSpan(0, 0, 0.9)
        NewsTimer.Start()
    End Sub

    Private Sub NewsTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
        Dim BoxOneLocation As Point = BoxOne.TranslatePoint(New Point(0, 0), ViewingBox)
        Dim BoxTwoLocation As Point = BoxTwo.TranslatePoint(New Point(0, 0), ViewingBox)

        If leadText = Texts.BoxOne Then
            Dim loc As Double = BoxOneLocation.X + BoxOne.ActualWidth
            If loc < ViewingBox.ActualWidth / 1.5 Then
                BoxTwo.BeginAnimation(Canvas.LeftProperty, dubAnim2)
                NewsTimer.Stop()
            End If
        Else
            Dim loc As Double = BoxTwoLocation.X + BoxTwo.ActualWidth
            If loc < ViewingBox.ActualWidth / 1.5 Then
                BoxOne.BeginAnimation(Canvas.LeftProperty, dubAnim)
                NewsTimer.Stop()
            End If
        End If
    End Sub

    Private Sub dubAnim_Completed(ByVal sender As Object, ByVal e As EventArgs)
        leadText = Texts.BoxTwo
        NewsTimer.Start()
    End Sub

    Private Sub dubAnim2_Completed(ByVal sender As Object, ByVal e As EventArgs)
        leadText = Texts.BoxOne
        NewsTimer.Start()
    End Sub
End Class
Leon Munir
  • 51
  • 9
1

Exteding the answer of sa_ddam213, this is a revise of the first animation(Right to Left). This will work for long strings. :)

<StackPanel Orientation="Horizontal"
                    x:Name="stack" 
                    Grid.Column="0"
                    Margin="0"                        >

            <StackPanel.Resources>
                <local1:NegatingConverter x:Key="NegatingConverter" />
                <Storyboard x:Key="slide">
                    <DoubleAnimation From="{Binding ActualWidth, ElementName=stack}" 
                                     To="{Binding ActualWidth, ElementName=txtKron, Converter={StaticResource NegatingConverter}}" 
                                     Duration="00:00:30"
                                     Storyboard.TargetProperty="X"
                                     Storyboard.TargetName="transferCurreny2"
                                     RepeatBehavior="Forever"/>
                </Storyboard>
            </StackPanel.Resources>

            <Label Content="{Binding Path=RSSFeed}" 
                       x:Name="txtKron" 
                       Canvas.Left="0"
                       Foreground="#E9D460"
                       Padding="0"
                       Margin="0"
                       VerticalAlignment="Center">

                <Label.Triggers>
                    <EventTrigger RoutedEvent="Label.Loaded">
                        <BeginStoryboard Storyboard="{StaticResource slide}"/>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Label.SizeChanged">
                        <BeginStoryboard Storyboard="{StaticResource slide}"/>
                    </EventTrigger>
                </Label.Triggers>

                <Label.RenderTransform>
                    <TranslateTransform x:Name="transferCurreny2" X="0"/>
                </Label.RenderTransform>
            </Label>

        </StackPanel>
Fluffy Potato
  • 47
  • 1
  • 9
0

To make this work for strings longer than the element, and hide the text which is overflowing out of the element, I modified previous answers further.

To use this directly, first create a project called WpfApp1

xaml:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

        <Border Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Height="25" ClipToBounds="True" BorderThickness="1.5" BorderBrush="Red">
            <Border x:Name="stack">
                <Border.Resources>
                    <local:NegatingConverter x:Key="NegatingConverter" />
                    <local:MarqueeMargin x:Key="MarqueeMargin" />
                    <local:NegMarqueeMargin x:Key="NegMarqueeMargin" />

                    <Storyboard x:Key="slide">
                        <DoubleAnimation From="0" To="{Binding ActualWidth, ElementName=txt_scroll, Converter={StaticResource NegMarqueeMargin}}" Duration="00:00:2"
                          Storyboard.TargetProperty="X"
                          Storyboard.TargetName="transferCurreny"
                          RepeatBehavior="Forever"/>
                    </Storyboard>
                </Border.Resources>
                <Border.RenderTransform>
                    <TranslateTransform x:Name="transferCurreny" X="0"/>
                </Border.RenderTransform>
                <Border.Triggers>
                    <EventTrigger RoutedEvent="StackPanel.Loaded">
                        <BeginStoryboard Storyboard="{StaticResource slide}" />
                    </EventTrigger>
                    <EventTrigger RoutedEvent="StackPanel.SizeChanged">
                        <BeginStoryboard Storyboard="{StaticResource slide}" />
                    </EventTrigger>
                </Border.Triggers>
                <Canvas Width="{Binding ActualWidth, ElementName=stack}">
                    <TextBlock Text="This text is too long to fit in the parent element." FontSize="15" Foreground="#F00" x:Name="txt_scroll" Canvas.Left="0"/>
                    <TextBlock Text="{Binding Text, ElementName=txt_scroll}" FontSize="15" Foreground="#F00" Canvas.Left="{Binding ActualWidth, ElementName=txt_scroll, Converter={StaticResource MarqueeMargin}}"/>
                </Canvas>
            </Border>
        </Border>

    </Grid>
</Window>

the c# code for this window:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

        <Border Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Height="25" ClipToBounds="True" BorderThickness="1.5" BorderBrush="Red">
            <Border x:Name="moving_border">
                <Border.Resources>
                    <local:NegatingConverter x:Key="NegatingConverter" />
                    <local:MarqueeMargin x:Key="MarqueeMargin" />
                    <local:NegMarqueeMargin x:Key="NegMarqueeMargin" />

                    <Storyboard x:Key="slide">
                        <DoubleAnimation From="0" To="{Binding ActualWidth, ElementName=txt_scroll, Converter={StaticResource NegMarqueeMargin}}" Duration="00:00:2"
                          Storyboard.TargetProperty="X"
                          Storyboard.TargetName="transferCurreny"
                          RepeatBehavior="Forever"/>
                    </Storyboard>
                </Border.Resources>
                <Border.RenderTransform>
                    <TranslateTransform x:Name="transferCurreny" X="0"/>
                </Border.RenderTransform>
                <Border.Triggers>
                    <EventTrigger RoutedEvent="Border.Loaded">
                        <BeginStoryboard Storyboard="{StaticResource slide}" />
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Border.SizeChanged">
                        <BeginStoryboard Storyboard="{StaticResource slide}" />
                    </EventTrigger>
                </Border.Triggers>
                <Canvas Width="{Binding ActualWidth, ElementName=moving_border}">
                    <TextBlock Text="This text is too long to fit in the parent element." FontSize="15" Foreground="#F00" x:Name="txt_scroll" Canvas.Left="0"/>
                    <TextBlock Text="{Binding Text, ElementName=txt_scroll}" FontSize="15" Foreground="#F00" Canvas.Left="{Binding ActualWidth, ElementName=txt_scroll, Converter={StaticResource MarqueeMargin}}"/>
                </Canvas>
            </Border>
        </Border>

    </Grid>
</Window>
DAG
  • 746
  • 6
  • 14