Scrolling NewsTicker is cutting off early - c#

I have a scrolling News Ticker in WPF that is used to convey alerts (their preference not mine). The implementation works but I have found an issue which is more noticeable as the text gets longer. The text scrolls and then abruptly stops and then restarts.
<Border Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="5" BorderBrush="White" BorderThickness="0,3,0,3">
<StackPanel Orientation="Horizontal" x:Name="Ticker" Background="Transparent" >
<StackPanel.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="NewsTicker">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation From="{Binding ElementName=Ticker, Path=ActualWidth}" To="{Binding ElementName=Ticker, Path=ActualWidth, Converter={StaticResource NegConverter}}" Storyboard.TargetName="translate" Storyboard.TargetProperty="X" Duration="0:0:35" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="WrapPanel.MouseEnter">
<PauseStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
<EventTrigger RoutedEvent="WrapPanel.MouseLeave">
<ResumeStoryboard BeginStoryboardName="NewsTicker" />
</EventTrigger>
</StackPanelPanel.Triggers>
<TextBlock Text="{Binding NewsTicker, IsAsync=True}" Margin="0,0,0,0" Padding="0,0,0,0" VerticalAlignment="Top" >
<TextBlock.RenderTransform>
<TranslateTransform x:Name="translate" />
</TextBlock.RenderTransform>
</TextBlock>
</StackPanelPanel>
</Border>
I would like the text to scroll from the Right to the Left until it scrolls off the screen and then come back in once again from the Right in an endless loop.
I tried a couple of different methods from SO, including animating Margins with Thickness animation and as well as an example using a canvas, but neither of them actually worked at all.
Any advice?

Okay, so the key to it not jumping is binding to the right things - we want to bind the To field to the ActualWidth of the TextBlock, not the ActualWidth of the StackPanel (which is smaller)
So we can change the DoubleAnimation to:
<DoubleAnimation From="{Binding ElementName=Ticker, Path=ActualWidth}" To="{Binding ElementName=TickerTextBlock, Path=ActualWidth, Converter={StaticResource NegConverter}}" Storyboard.TargetName="translate" Storyboard.TargetProperty="X" Duration="0:0:4" />
And add a x:Name="TickerTextBlock" to the TextBlock containing the Text.
Unfortunately, this doesn't work straight off (and instead it uses the initial value of 0 from the binding pre text loading for the animation! :|). Animations are often very fickle like this - I believe it has something to do with only being created and run once, and so the value of the binding is only used when the animation is first run. (but I may be mis-understanding)
Certainly one solution which worked on my copy of your code is to ensure that the text loads first, (ie we place the TextBlock above the animation in the XAML, and change the binding so that it is not async.)
If you wish to keep the async, then I believe we'd need to restart the double animation when the text changes. I would look into this further, but it's really late and I'm going to head to bed (sorry!)

Related

EventTrigger trigger on condition

I have a event trigger.
I want it to be enabled only if condition occures
for example only if Viewmodel.IsEnabled propery is true AND EventTrigger RoutedEvent="Window.Loaded" occured
my problem is that MultiDataTrigger and MultiTriggers cannot combine Event trigger with Data Trigger.
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Window.Loaded" SourceName="NotificationWindow">
<BeginStoryboard x:Name="FadeInStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NotificationWindow" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
In other words I have a trigger to load a story board when window is loaded.
I want to be able to enable/disable this trigger per item.
You can use Blend Interactivity for WPF for accomplishing you task. I do not know your whole DataTemplate, so in my sample I will use an invented one.
Let's suppose I have a collection of Person objects and I want to start the DoubleAnimation just per Person whose property IsEnabled is true. I bind my collection to an ItemsControl and I create a "conditional" DataTemplate:
<ItemsControl ItemsSource="{Binding Path=People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="Border" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Margin="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:Interaction.Behaviors>
<ei:ConditionBehavior>
<ei:ConditionalExpression>
<ei:ComparisonCondition LeftOperand="{Binding IsEnabled}" RightOperand="True"/>
</ei:ConditionalExpression>
</ei:ConditionBehavior>
</i:Interaction.Behaviors>
<ei:ControlStoryboardAction ControlStoryboardOption="Play">
<ei:ControlStoryboardAction.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Border" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</ei:ControlStoryboardAction.Storyboard>
</ei:ControlStoryboardAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Path=Surname, Mode=OneWay}" Margin="2" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Of course you have to declare those namespaces in your XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
The ConditionBehavior object evaluates the ComparisonCondition: if the former is true, it allows the ControlStoryboardAction to run.
I hope this small sample can give you an hint for solving your issue.

WPF Storyboarded Animation Freezes If Clicked Too Quickly

I am currently having an annoying issue with the simple XAML code below. The animation does fire correctly, and will always fire correctly if I click the button after the animation has completed. If I click quickly a few times, the animation will freeze, and the animation will never run again.
Thanks so much for the help!
<!-- Admin Login Button /-->
<ToggleButton Name="LoginButton" IsChecked="{Binding ElementName=LoginPopup, Path=IsOpen, Mode=TwoWay}" HorizontalAlignment="Right" Margin="0,35,32.334,0" VerticalAlignment="Top" Width="97" Height="77" BorderThickness="0" Grid.Column="1" Grid.ColumnSpan="2">
<ToggleButton.Background>
<ImageBrush ImageSource="/Resources/Images;component/Resources/titleAboutIcon.png"/>
</ToggleButton.Background>
<!-- <Frame x:Name="AdminFrame" Height="77" Width="97"/> /-->
<ToggleButton.RenderTransform>
<ScaleTransform x:Name="Buttonscale" ScaleX="1" ScaleY="1" CenterX="0" CenterY="{Binding ElementName=LoginButton, Path=ActualHeight}" />
</ToggleButton.RenderTransform>
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleX)" To="1.25" Duration="0:0:0.25" AutoReverse="True"/>
<DoubleAnimation Storyboard.TargetName="Buttonscale" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" To="1.25" Duration="0:0:0.25" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
<!-- Admin Login Button /-->
You can add FillBehavior="Stop" to your Storyboard so it will stop and reset animated properties values instead of freezing them (HoldEnd is the default value of FillBehavior, so it won't change values back to their default).
From MSDN:
Set an animations FillBehavior property to HoldEnd when you want the animation to hold its value after it reaches the end of its active period. An animation that has reached the end of its active period that has a FillBehavior setting of HoldEnd is said to be in its fill period. When you don't want an animation to hold its value after it reaches the end of its active period, set its
FillBehavior property to Stop.
https://msdn.microsoft.com/en-us/library/system.windows.media.animation.timeline.fillbehavior(v=vs.110).aspx

Move&Zoom Image

I have a pretty simple requirement (I thought), a window that shows an image at a certain start point & size and moves/zooms it to a certain end point and size. This is not the typical Image Viewer requirement for pan&zoom. More like a sprite animation.
I tried out a couple of ways, e.g. a ThicknessAnimation to alter the Margin to move the image, but the performance was just not good enough. I have to admit I don't have much experience with WPF, though.
Below is my last version which is ok performance-wise. The image element goes over the whole window and the size and position are set by ScaleTransform and TranslateTransform. It works well (and shows relatively smooth movement) if the end position is on the lower right of the start position, e.g. from 0,0 to 800,600. If it's the other way around, though, the image makes kind of a slingshot movement to the lower right corner of the window, leaves the window and then finally comes back to stop at its end position.
I would appreciate an explanation for this behavior as well as a solution. And if you know a different way that works and is similar or better performance-wise, I'd like to hear that too.
<Window x:Class="ViewBoxText.AnimatedImageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AnimatedImageWindow"
WindowStyle="None"
SizeToContent="WidthAndHeight"
AllowsTransparency="True"
ShowInTaskbar="False"
Left="0"
Top="0"
Height="{Binding TotalHeight}"
Width="{Binding TotalWidth}"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Image x:Name="image" HorizontalAlignment="Left" VerticalAlignment="Top" Width="{Binding TotalWidth}" Height="{Binding TotalHeight}" Stretch="Uniform">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding HorizontalStart}" Y="{Binding VerticalStart}" />
<ScaleTransform />
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard Completed="OnStoryBoardCompleted">
<DoubleAnimation Storyboard.TargetName="image"
Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"
From="{Binding ScaleStart}" To="{Binding ScaleEnd}" DesiredFrameRate="30"
Duration="0:0:2" BeginTime="0:0:0" />
<DoubleAnimation Storyboard.TargetName="image"
Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"
From="{Binding ScaleStart}" To="{Binding ScaleEnd}" DesiredFrameRate="30"
Duration="0:0:2" BeginTime="0:0:0" />
<DoubleAnimation Storyboard.TargetName="image"
Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"
By="{Binding HorizontalOffset}"
Duration="0:0:2" BeginTime="0:0:0" />
<DoubleAnimation Storyboard.TargetName="image"
Storyboard.TargetProperty="(Image.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"
From="{Binding VerticalStart}" To="{Binding VerticalEnd}"
Duration="0:0:2" BeginTime="0:0:0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Grid>
The problem is the scale is scaling about 0,0. I believe you want to scale about the point you are interested in, which may or may not be you Vertical/Horizontal end point.
Normally, you would translate so that the centre is on the origin, scale, then put the whole thing back.
e.g. to scale by X about point A,B
Translate -A,-B
Scale X
Translate A,B
Fortunately the scale has a couple of centre properties that make this easier.
So try adjusting the CenterX and CenterY properties of the ScaleTransform.

Reusable auto-scrolling canvas

Okay I made a Scolling canvas (sometimes I put buttons in it that open my programs or do other stuff) it is working perfectly. But I tried making it into a .dll but I noticed that the method I am using is worthless for a .dll as it autoscrolls upon drop and I can't add more to it. so what I want is a empty canvas (or anything really) that auto-scrolls the content and will let me add more items into it. example: buttons, text block, labels etc. and it will scroll all of them.
Edit: I want to be able to drag the control from the toolbox into the window and then add buttons etc onto it and it will auto-scroll them.
Example below. xaml only.. thinking to make it the way I want it has to be coded in c# not xaml. since once it is added as a user control you cannot add other controls, toolbox items to it and expect them to auto-scroll..
<Canvas ClipToBounds="True" x:Name="scrollerCanvas" Height="588">
<Grid x:Name="OurContainer" Height="588" Background="{x:Null}" ClipToBounds="False">
<Grid.Clip>
<RectangleGeometry Rect="0,0,1087,588" RadiusX="30" RadiusY="30" />
</Grid.Clip>
<StackPanel Height="800" Margin="1">
<StackPanel.RenderTransform>
<TranslateTransform x:Name="scroll" />
</StackPanel.RenderTransform>
</StackPanel>
<Grid.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation
From="620" To="-450"
Storyboard.TargetName="scroll" Storyboard.TargetProperty="Y"
Duration="0:0:28" SpeedRatio=".8" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Grid.Triggers>
</Grid>
</Canvas>
you can use a ScrollViewer with a Canvas inside it.

How to start a storyboard which is attached to a control inside a data template?

I have a storyboard inside a data template. I want to start this only for a certain condition. In my case whenever the no. of seconds in the clock is 59 the storyboard should start.
Below you can see the storyboard as well as the control to which the animation is applied :
<!-- Minute Hand -->
<Image
Source="{Binding Time, Converter={StaticResource MinHandBackground}}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Canvas.Left="118"
Canvas.Top="118">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform
X="-11"
Y="-90" />
<RotateTransform
x:Name="minHandTransform" />
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger
RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard
x:Name="myStoryboard2">
<DoubleAnimation
x:Name="minuteAnimation"
Storyboard.TargetName="minHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:0:1"
From="{Binding Time, Converter={StaticResource minuteHandTransform}}"
To="{Binding Time, Converter={StaticResource minuteHandToTransform}}"
RepeatBehavior="1x">
<DoubleAnimation.EasingFunction>
<SineEase
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Currently the BeginStoryboard starts it immediately on load. I want to start it when the number of seconds is 59 but i cant access the storyboard in the back end as the control is within a data template.
Can anyone please help me out here.....i have been banging my head about this problem for a lot of days now!!
The easiest way would be to move the entire contents of the DataTemplate to a separate UserControl so you would get easier access to all named elements.

Categories

Resources