Label freezes and doesn't change it's Left property - c#

Basically I tried to make a marquee with 2 Labels that when one goes invisible, the second one starts it's animation so it goes smooth from left to right and the text is always visible. My problem is that it does work at start but when I retry that code (second time at run time so the old animation stops, it refresh the text and start the animation again) the second label freezes when it was and it's left property of it's canvas doesn't change (Canvas.SetLeft(L_Content_Second, -L_Content_Second.ActualWidth))
C#
it's a function called StartAnimation(); It's fired when content of L_Content and L_Content_Second changes
DoubleKeyFrameCollection collection = new DoubleKeyFrameCollection();
collection.Add(
new LinearDoubleKeyFrame(
-L_Content.ActualWidth,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)))
);
if (L_Content.ActualWidth > this.ActualWidth)
{
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth - (L_Content.ActualWidth / 2),
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(15)))
);
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth * 2,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(30)))
);
}
else
{
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth - L_Content.ActualWidth,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(15)))
);
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth + (this.ActualWidth - L_Content.ActualWidth),
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(30)))
);
}
animK.KeyFrames = collection;
animK.Duration = TimeSpan.FromSeconds(30);
animK2.KeyFrames = collection;
animK2.BeginTime = TimeSpan.FromSeconds(15);
animK2.Duration = TimeSpan.FromSeconds(30);
animK.AutoReverse = false;
animK2.AutoReverse = false;
animK.FillBehavior = FillBehavior.Stop;
animK.RepeatBehavior = RepeatBehavior.Forever;
animK2.FillBehavior = FillBehavior.Stop;
animK2.RepeatBehavior = RepeatBehavior.Forever;
Canvas.SetLeft(L_Content_Second, -L_Content_Second.ActualWidth); //This works at start, then it doesn't any more
L_Content_Second.Visibility = System.Windows.Visibility.Visible;
L_Content.BeginAnimation(Canvas.LeftProperty, animK, HandoffBehavior.SnapshotAndReplace);
L_Content_Second.BeginAnimation(Canvas.LeftProperty, animK2, HandoffBehavior.SnapshotAndReplace);
XAML
<UserControl x:Name="userControl" x:Class="Supreme.Components.ScrollLabel"
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" Height="18" Width="300" MouseDoubleClick="UserControl_MouseDoubleClick_1" Background="#00000000">
<Canvas x:Name="Container">
<Label x:Name="L_Content" Content="" HorizontalAlignment="Left" Padding="0" Height="18" Foreground="{Binding Foreground, ElementName=userControl}" VerticalAlignment="Top"/>
<Label x:Name="L_Content_Second" Content="" HorizontalAlignment="Left" Padding="0" Height="18" Foreground="{Binding Foreground, ElementName=userControl}" SizeChanged="L_Content_SizeChanged" VerticalAlignment="Top"/>
</Canvas>
</UserControl>

I tried to attempt the same via XAML
below is the sample for same, run it and see if this does what you are looking for.
<Canvas x:Name="Container">
<Canvas.Resources>
<l:PositionConverter xmlns:l="clr-namespace:CSharpWPF"
x:Key="PositionConverter" />
<Storyboard x:Key="marquee">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="L_Content"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:10">
<SplineDoubleKeyFrame KeyTime="0:0:0"
Value="{Binding ActualWidth, ElementName=L_Content, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:5"
Value="{Binding ActualWidth,ElementName=Container}" />
<SplineDoubleKeyFrame KeyTime="0:0:10"
Value="{Binding ActualWidth,ElementName=Container}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="L_Content_Second"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:10">
<SplineDoubleKeyFrame KeyTime="0:0:0"
Value="{Binding ActualWidth, ElementName=L_Content_Second, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:5"
Value="{Binding ActualWidth, ElementName=L_Content_Second, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:10"
Value="{Binding ActualWidth,ElementName=Container}" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
<TextBox x:Name="L_Content"
Foreground="Red"
Text="first" />
<TextBox x:Name="L_Content_Second"
Foreground="Green"
Text="second">
</TextBox>
<Button Content="Start" Canvas.Top="30">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
</Button.Triggers>
</Button>
<Canvas.Triggers>
<EventTrigger SourceName="L_Content"
RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
<EventTrigger SourceName="L_Content_Second"
RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
<EventTrigger RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
</Canvas.Triggers>
</Canvas>
I've used textbox in this example for easy value change, also for this I have created a converter
namespace CSharpWPF
{
public class PositionConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return -System.Convert.ToDouble(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Related

Is there a way to use Storyboard with ListView/GridView items?

Here is my question:
I have a GridView in my UWP app. Each item in the GridView, keeps a button. Like following:
<GridView ItemsSource="{x:Bind ExampleItems, Mode=OneWay}" x:Name="mDataGridView">
<GridView.ItemTemplate>
<DataTemplate x:Name="DataTemplate" x:DataType="local:ItemTemplate">
<StackPanel Height="100" Width="100" Background="OrangeRed" x:Name="rootPanel">
<Grid Width="155" Height="210" Background="Red" x:Name="myGrid"/>
<Button x:Name="mOpenDetailButton" Click="mOpenDetailButton_Click" Margin="0, 30, 20,0"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
I also have a pre-defined animation in a Storyboard like following
<Storyboard x:Name="OpenStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="grid" x:Name="mOpenAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.095"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="CloseStoryboard">
<DoubleAnimation Storyboard.TargetName="grid" Duration="00:00:00.1" x:Name="mCloseAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)" To="0.095"/>
</Storyboard>
OK, here comes the question, is it possible to use the animations defined in the Storyboards with the GridView items? What I want is:
When user clicks on the mOpenDetailButton button, OpenStoryboard will be played, and the ScaleY value of grid changes with it.
Any help would be appreciated, thank you.
You can put the Storyboard in the StackPanel inside DataTemplate and define a RenderTransform on the your myGrid as the target object of StackPanel. In this case, it simplifies the specification of target object and more convenient to get Storyboard in code-behind and play it. For example:
.xaml:
<GridView ItemsSource="{x:Bind ExampleItems, Mode=OneWay}" x:Name="mDataGridView">
<GridView.ItemTemplate>
<DataTemplate x:Name="DataTemplate" x:DataType="local:ItemTemplate">
<StackPanel Height="100" Width="100" Background="OrangeRed" x:Name="rootPanel">
<StackPanel.Resources>
<Storyboard x:Name="OpenStoryboard">
<DoubleAnimationUsingKeyFrames
Duration="0:0:0.2"
Storyboard.TargetProperty="ScaleY"
Storyboard.TargetName="transform" >
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.095"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</StackPanel.Resources>
<Grid Width="100" Height="50" Background="Red" x:Name="myGrid">
<Grid.RenderTransform>
<CompositeTransform
x:Name="transform"
ScaleX="1" ScaleY="0" />
</Grid.RenderTransform>
</Grid>
<Button x:Name="mOpenDetailButton" Content="Mybutton" Click="mOpenDetailButton_Click" Margin="20"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
.cs:
First, get its parent panle(StackPanel) via the button, then get its Storyboard animation and play it.
private void mOpenDetailButton_Click(object sender, RoutedEventArgs e)
{
UIElement mybutton = sender as UIElement;
StackPanel stackPanel = FindParent<StackPanel>(mybutton);
object storyvalue = null;
stackPanel?.Resources.TryGetValue("OpenStoryboard", out storyvalue);
Storyboard storyboard = value as Storyboard;
storyboard?.Begin();
}
public static T FindParent<T>(DependencyObject element)
where T : DependencyObject
{
while (element != null)
{
DependencyObject parent = VisualTreeHelper.GetParent(element);
T candidate = parent as T;
if (candidate != null)
{
return candidate;
}
element = parent;
}
return default(T);
}

How to count how many times storyboard animation looped?

I have code like this
<Storyboard x:Key="AdvMarquee" Completed="Storyboard_Completed">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="-25" To="0" BeginTime="0:00:00" Duration="0:00:01" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="0" To="25" BeginTime="0:00:03" Duration="0:00:01" />
</Storyboard>
<Style x:Key="AnimationImageStyle" TargetType="StackPanel">
<Setter Property="Canvas.Top" Value="200" />
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource AdvMarquee}"/>
</EventTrigger>
</Style.Triggers>
</Style>
And Applied this Animation style with this code
<Canvas x:Name="Advertise" Background="{x:Null}" Margin="10,0,0,0" >
<StackPanel Style="{StaticResource AnimationImageStyle}">
<Button Click="Advertise_Click" Style="{StaticResource AdvertisementBtnStyle}">
<TextBlock Name="AdvText" Text="This is Animated Text" Padding="10, 0, 10, 0"/>
</Button>
</StackPanel>
</Canvas>
I've tried to use Completed Event on Storyboard to calculate how many times Storyboard animation executed.
Before this, I tried to add RepeatBehavior="Forever" on Storyboard but it just loop forever and didn't run completed event.
and now, when I remove RepeatBehavior="Forever", it complete it's progress, count up, but it doesn't run again.
how can I solve this problem?
still have no idea cuz I'm really new to work with xaml wpfform.
My Storyboard_Completed is just like this.
int count = 0;
private void Storyboard_Completed( object sender, EventArgs e )
{
count++;
}
Put event handler for CurrentStateInvalidated for the last animation and you will have the possibility to get current iteration:
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="0" To="25" BeginTime="0:00:03" Duration="0:00:01" CurrentStateInvalidated="DoubleAnimation_CurrentStateInvalidated"/>
int cnt=0;
private void DoubleAnimation_CurrentStateInvalidated(object sender, EventArgs e)
{
var ac = sender as AnimationClock;
cnt = (ac.Parent as ClockGroup).CurrentIteration;
}
Storyboard_Completed you will not need.

How to add objects to Canvas after ellipse reaches middle of the Canvas?

I am animating the ellipse to move horizontally in wpf. Now I want to add few more ellipses on to the canvas when ellipse reaches certain point on canvas (let's say midpoint of the canvas). How can I achieve this?
XAML code-
<Canvas Background="AliceBlue" x:Name="canvas">
<Ellipse
Name="ellipse1"
Canvas.Left="50"
Fill="#FFFFFF00"
Height="75"
Width="100"
/>
</Canvas>
Code behind-
public partial class MainWindow : Window
{
private DoubleAnimation anim = new System.Windows.Media.Animation.DoubleAnimation(50, 400, TimeSpan.FromSeconds(10),
System.Windows.Media.Animation.FillBehavior.HoldEnd);
private AnimationClock clock;
public MainWindow()
{
InitializeComponent();
clock = anim.CreateClock();
this.ellipse1.ApplyAnimationClock(Canvas.LeftProperty, clock);
}
}
Initially I thought it was simple, I would just access Canvas.Left from code behind and when it reaches the value I want, I would add the ellipses. But I am struggling to implement this, I guess I would need some kind of watcher or event to achieve this. How should I implement it?
Create two storyboards. Each one does half the animation. When the first storyboard completes start the second storyboard and add the other ellipses.
XAML
<Window.Resources>
<Storyboard x:Key="StartingStoryboard"
Completed='Storyboard_Completed'>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
Storyboard.TargetName="ellipse1">
<EasingDoubleKeyFrame KeyTime="0"
Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:2"
Value="100" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="EndingStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
Storyboard.TargetName="ellipse1">
<EasingDoubleKeyFrame KeyTime="0"
Value="100" />
<EasingDoubleKeyFrame KeyTime="0:0:2"
Value="200" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource StartingStoryboard}" />
</EventTrigger>
</Window.Triggers>
<Canvas Background="AliceBlue"
x:Name="canvas1">
<Ellipse Name="ellipse1"
Fill="#FFFFFF00"
Height="75"
Width="100"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
Code
private void Storyboard_Completed(object sender, EventArgs e) {
var sb = FindResource("EndingStoryboard") as Storyboard;
sb.Begin();
var orangeEllipse = new Ellipse();
orangeEllipse.Fill = new SolidColorBrush(Colors.Orange);
orangeEllipse.Width = orangeEllipse.Height = 40;
canvas1.Children.Add(orangeEllipse);
}

animation and data bindings

I need an help with animations, i tried to set by code the value of an SplineDoubleKeyFrame by data binding, but it doesn't work, why?
Code xaml:
<StackPanel Margin="0,435,0,0">
<StackPanel.Resources>
<Storyboard x:Name="myStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="barra" Storyboard.TargetProperty="Width">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<SplineDoubleKeyFrame KeySpline="0,0 1,0" Value="{Binding linea1}" KeyTime="0:0:0.8" />
<SplineDoubleKeyFrame KeySpline="0.10, 0.21 0.00, 1.0" Value="{Binding linea1}" KeyTime="0:0:1.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</StackPanel.Resources>
<Rectangle Fill="White" HorizontalAlignment="Left" Height="72" Margin="12,0,0,0" Grid.Row="1" Stroke="#FF8E76FF" VerticalAlignment="Top" Width="444" StrokeThickness="5"/>
<Rectangle Visibility="Visible" x:Name="barra" Fill="#FF8E76FF" HorizontalAlignment="Left" Height="72" Margin="12,-72,0,0" Stroke="#FF8E76FF" StrokeThickness="5" VerticalAlignment="Top" Width="456"/>
</StackPanel>
c#:
linea1 = 440;
myStoryboard.Begin();
Thanks in advance! ;)
For DataBinding you need to use Property only .you cannot use varibale for binding(for ex: var linea1 = 440;)
To create property I create a class
public class StackpanelProperties
{
public int linea1 { get; set; }
}
And set datacontext to Stackpnael so that I can use this property for binding
this.InitializeComponent();
stack.DataContext = new StackpanelProperties() { linea1 = 440 };
xaml code
<StackPanel Name="stack" Margin="0,435,0,0">
<StackPanel.Triggers>
<EventTrigger>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Duration="0:0:0.8" EnableDependentAnimation="True" Storyboard.TargetName="barra" Storyboard.TargetProperty="Width">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<SplineDoubleKeyFrame KeySpline="0,0 1,0" Value="{Binding linea1}" KeyTime="0:0:0.8" />
<SplineDoubleKeyFrame KeySpline="0.10, 0.21 0.00, 1.0" Value="{Binding linea1}" KeyTime="0:0:1.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<Rectangle Fill="White" HorizontalAlignment="Left" Height="72" Margin="12,0,0,0" Grid.Row="1" Stroke="#FF8E76FF" VerticalAlignment="Top" Width="444" StrokeThickness="5"/>
<Rectangle Visibility="Visible" x:Name="barra" Fill="#FF8E76FF" HorizontalAlignment="Left" Height="72" Margin="12,-72,0,0" Stroke="#FF8E76FF" StrokeThickness="5" VerticalAlignment="Top" Width="456"/>
</StackPanel>
Note : EventTrigger is used for begin animation on stackpnael Loaded event.

Listbox items on canvas scaling on center

I'm trying to cobble together examples of listbox items on a (rendered via canvas) that expand in size when clicked. I'm close but the items move when zoomed, I would like them to zoom from the center and stay in place. Here is sample data collection.
public class Item
{
public double X { get; set; }
public double Y { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
public class ItemsFactory
{
private List<Item> items;
public IEnumerable<Item> Items
{
get
{
return items ?? (items = new List<Item>()
{
new Item { Name = "One", X = 100, Y = 100, Color="Red" },
new Item { Name = "Two", X = 88, Y = 210, Color="Green" },
new Item { Name = "Three", X = 200, Y = 295, Color="Blue" }
});
}
}
}
And below is my WPF. Items get bigger when clicked but they also move. I have tried RenderTransformOrigin in various places with no luck.
<Window x:Class="WPFCards.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCards"
Title="MainWindow" Height="395.268" Width="607.807" Background="Black" WindowStyle="None" WindowState="Maximized">
<Window.Resources>
<local:ItemsFactory x:Key="sampleItems" />
</Window.Resources>
<Grid DataContext="{StaticResource sampleItems}">
<ListBox ItemsSource="{Binding Items}"
Background="Black"
SelectionMode="Multiple" >
<ListBox.ItemTemplate>
<DataTemplate>
<Canvas>
<Canvas.RenderTransform>
<ScaleTransform x:Name="st" ScaleY="{Binding ScaleX, RelativeSource={RelativeSource Self}}" />
</Canvas.RenderTransform>
<Grid
Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}">
<Ellipse Fill="{Binding Color}" Width="50" Height="40" />
<ContentPresenter Content="{Binding Name}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Canvas>
<DataTemplate.Resources>
<CubicEase x:Key="ease" EasingMode="EaseOut"/>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
RenderTransform should be applied on ellipse container i.e. Grid and not on Canvas. Setting it on canvas is increasing the width and height of grid and hence affecting layout of ellipses in it.
Also, in case you want centric zoom, set RenderTransformOrigin="0.5,0.5" on Grid.
<Canvas>
<Grid Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<ScaleTransform x:Name="st"
ScaleY="{Binding ScaleX,
RelativeSource={RelativeSource Self}}"/>
</Grid.RenderTransform>
<Ellipse Fill="{Binding Color}" Width="50" Height="40" />
<ContentPresenter Content="{Binding Name}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Canvas>
A bit of guessing: I think you should set
RenderTransformOrigin="0.5,0.5"
of your Canvas

Categories