Binding to an ancestor property inside an animation in WPF - c#

I have a WPF page with a TabControl in it, and I wanted to design the tabs to have a nice underline when selected and on mouse hover.
I've managed to do that but each tab has a different length, and it seems that I can't bind a property of the TabItem inside of its animation.
This is what i have done so far:
<TabControl Width="{Binding ActualWidth,
ElementName=cnvsMain}" Height="{Binding
ActualHeight, ElementName=cnvsMain}"
BorderThickness="0" >
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<StackPanel>
<Border Name="Border"
BorderThickness="1,1,1,0"
BorderBrush="Gainsboro"
CornerRadius="6,6,0,0"
Margin="2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Border>
<Label Name="TabUnderline" Background="LimeGreen"
Margin=" 5, 0" Height="5" Width="0"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TabUnderline"
Storyboard.TargetProperty="Width"
From="0" To="100"
Duration="0:0:0.1"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TabUnderline"
Storyboard.TargetProperty="Width"
From="100" To="0" Duration="0:0:0.1"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Rooms">
<StackPanel>
<Label Content="TODO: List all rooms" />
<Label Content="TODO: Create room button" />
</StackPanel>
</TabItem>
<TabItem Header="Statistics" />
<TabItem Header="Details" />
</TabControl>
It works great but there is one simple flaw:
The under line will always be 100 pixels (Or is it units?) wide, and each tab has its unique and different width. I've tried to replace the hard-coded 100 to {Binding ActualWidth} or using a RelativeSource to get the width of the TabItem, but I keep getting an exception everytime this page is being loaded because of a binding-error.
How can I bind a TabItem's property to a value inside its own animation? Is it even possible?

Related

Adding animations to DataTemplate-Generated controls

I have some problem with adding animations to DataTemplate-Generated controls. I want to animate Border height.
XAML code:
<StackPanel x:Name="stackpanel">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Height="42">
<!--There some controls-->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
There C# code(but it works for only last one item):
StoryBoard maxhasb = new Storyboard();
StoryBoard minhasb = new Storyboard();
var maximizeHeightAnimation= new DoubleAnimation(42, 72, duration, FillBehavior.HoldEnd);
var minimizeHeightAnimation= new DoubleAnimation(72, 42, duration, FillBehavior.HoldEnd);
....
ItemsControl itemsControl = (ItemsControl)stackpanel.Children[0];
foreach (var item in itemsControl.Items)
{
ContentPresenter contentPresenter = (ContentPresenter)itemsControl.ItemContainerGenerator.ContainerFromItem(item);
Border border = (Border)itemsControl.ContentTemplate.LoadContent();
Storyboard.SetTarget(maximizeHeightAnimation, border);
Storyboard.SetTargetProperty(maximizeHeightAnimation, new PropertyPath(HeightProperty));
Storyboard.SetTarget(minimizeHeightAnimation, border);
Storyboard.SetTargetProperty(minimizeHeightAnimation, new PropertyPath(HeightProperty));
maxhasb.Children.Add(maximizeHeightAnimation);
minhasb.Children.Add(minimizeHeightAnimation);
}
Is there anything that I'm doing wrong?
It's quite difficult to work out what you intend doing with this. I guess the idea is to increase the height of an item and then decrease it a bit as it's inserted.
Maybe that isn't exactly what you intend.
You might find the sample for the "Toast" in this article interesting:
https://social.technet.microsoft.com/wiki/contents/articles/31416.wpf-mvvm-friendly-user-notification.aspx#Toast
You probably just want to use a layouttransform to animate ScaleY up and then down without any bounce. But maybe bounce will give you the effect you want or you're not bothered about the specific effect.
From ToastList:
<ListBox ItemsSource="{Binding Messages}" BorderBrush="Transparent" Background="LightGray">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1.2" FillBehavior="Stop" />
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="0" Duration="0:0:.2" FillBehavior="Stop">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="2" Bounciness="6"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<DataTrigger Binding="{Binding IsGoing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:1.2" />
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" To="0" Duration="0:0:1.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="Yellow"
CornerRadius="3"
BorderThickness="1"
Margin="4" Padding="4">
<Border.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="4" Color="Purple" />
</Border.BitmapEffect>
<TextBlock Text="{Binding Msg}" FontWeight="SemiBold" TextWrapping="Wrap" MaxWidth="200"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF XAML Animate/blinking a button on mouse over

I have a custom message box with two button 'Yes' and 'No'. Button 'Yes' is green and button 'No' is red.
I apply the same style for the two buttons through a xaml file defined separately as below:
MsgBoxButtonStyle.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button"
x:Key="MsgBoxButtonStyle">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="TextBlock.TextAlignment"
Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="Border" CornerRadius="0"
BorderBrush="#000" BorderThickness="1,1,1,1"
Background="{TemplateBinding Background}">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
These two buttons are place in my WPF window as below:
<Window x:Class="My.XAML.Controls.Windows.WpfMessageBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/My.XAML;component/Styles/MsgBoxButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Button Name="btnYes" Content="Yes"
Click="Button_Click" Foreground="Black"
Style="{StaticResource MsgBoxButtonStyle}"
Background="#b6dbd6"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
<Button Name="btnNo" Content="No"
Click="Button_Click" Foreground="Black"
Style="{StaticResource MsgBoxButtonStyle}"
Background="#dbb6b6"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center" />
</Window>
Now, I would like to perform a nice effect when mouse is positioned over the buttons, some kind of blinking or whatever using storyboard and dependant on the color of the button.
Also I would like to put this storyboard within my existing style file MsgBoxButtonStyle.xaml and no put each storyboard in the window for each button, i want to share it.
How can I do this? some one can provide me a nice effect for buttons?
Just add triggers to the style. Here's an example to start you off.
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.250" To="0"
Storyboard.TargetProperty="BorderThickness" />
<DoubleAnimation Duration="0:0:0.550" To="120"
Storyboard.TargetProperty="Height" />
<DoubleAnimation Duration="0:0:0.550" To="120"
Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.250" To="0"
Storyboard.TargetProperty="BorderThickness" />
<DoubleAnimation Duration="0:0:0.550" To="100"
Storyboard.TargetProperty="Height" />
<DoubleAnimation Duration="0:0:0.550" To="100"
Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>

Toggle opacity of control

I have button, which should toggle Opacity of element. Currently it works only at half - on click make control visible( set opacity to 1). I've tried use ToogleButton instead of simple Button to use properties IsChechecked (I think it is the same as IsClicked) but what I found wasn't good for me, cause for this properties I can't set TargetName for animation.
<ToggleButton BorderThickness="0" Background="Transparent" Grid.Column="0" Padding="0" Grid.Row="0" Focusable="False" Width="Auto" PreviewMouseUp="Share_Click">
<ToggleButton.Content>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,6,0" Grid.Column="0" Text="Filter" Foreground="#FFC0B6D1" FontFamily="Segoe UI Semibold" FontSize="14" />
<Image Grid.Column="1" Margin="0,4,0,0" VerticalAlignment="Center" Height="6" Width="12" Name="FiltersToggleArrow" Style="{StaticResource ToogleFilters}"/>
</Grid>
</ToggleButton.Content>
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="UIElement.PreviewMouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0.00" Storyboard.TargetName="FiltersPanel" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:00.2" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
The MSDN documentation says:
You can set this property to the name of any element within the scope of where the setter collection (the collection that this setter is part of) is applied. This is typically a named element that is within the template that contains this setter.
To get around this, you can instead apply your Storyboard inside the Style of your Panel, not the ToggleButton. In this case, you can use a DataTrigger to bind to the IsChecked property and act accordingly.
Here's an example I did with a ToggleButton and a Rectangle.
<ToggleButton Content="Switch" Name="chk"/>
<Rectangle Fill="Red" Name="rect"
Width="50" Height="50">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=chk}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="1"
To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
From my experience with triggers, they can be a royal pain in the backside and are tricky to get right. That being said, this example should be enough to fit your requirements.

Wpf button with both result datatrigger storyboard and mouseover background

I'm looking to implement this functionality: A button, bound to a command, that can signal back wether the command was successful in form of a red or green flash. At the same time, I want the button to look like other buttons.
This is the code I have now:
<Button IsEnabled="{Binding EmptyingAllowed}" Content="Töm lista" Foreground="#555555" FontWeight="SemiBold" Height="20" Width="65" Command="{Binding EmptyListCommand}" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="#BBBBBB" Padding="1" BorderThickness="0,0,1,1" Background="#DDDDDD">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ClearedOk}" Value="Ok">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation AccelerationRatio="0.2" DecelerationRatio="0.01" AutoReverse="True" Duration="0:0:1" To="LawnGreen" Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ClearedOk}" Value="Error">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation AccelerationRatio="0.2" DecelerationRatio="0.01" AutoReverse="True" Duration="0:0:1" To="Red" Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
When the command is executed, ClearedOk is set to "Ok" or "Error" and then to "Empty", this causes the flash I'm looking for, unfortunately, the usual mouseOver-effect is lost for some reason, and if I add a mouseOver-trigger, it always takes precedence over the flashing effect, meaning the flash is only visible of the button isn't mouseOver:ed.
Is there a solution to this?
Try to make datatemplate and model with properties of state and then binding in template to this properties.
<DataTemplate x:Key="DataTemplate">
<Grid >
<Ellipse Stroke="White"
StrokeThickness="5"
Fill="{Binding State, Converter={StaticResource CheckToColorConverter}}"/>
</Grid>
</DataTemplate>

WPF how to animate picture inside listboxitem using data trigger?

I have a listbox with templated listboxitem which is binded to an object.
one of the listboxitem controls is an image and it's source is also binded to a property inside the datacontext, So the pictur inside the Image control is different according to that property.
I want to animate the image but only specific one (when the binded property value is some specific value only).
I guess there are other ways to do that, maybe using code behind. But I would realy like use it in the xaml code, so I thought using animation inside DataTrigger, it makes sense to me because the starting/ending the animation depends on the DataContext:
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
But I cannot make it work any way.
I tried to put that code inside a style of the listBox and then apply that style to the image, it didnt work, I also tried to put inside the listBox.ItemTamplate, and other places but nothing helped.
Here is the code. maybe it can help to understand (I pasted only relevan code here):
<UserControl ...>
<UserControl.Resources>
<local:SomePropertyToImageConverter x:Key="somePropertyToImageConverter"/>
<DoubleAnimation x:Key="SomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="45" Duration="0:0:2" RepeatBehavior="Forever"/>
<DoubleAnimation x:Key="StopSomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="0" Duration="0:0:2"/>
</UserControl.Resources>
<Grid>
<ListBox Name="myListBox"
ItemsSource="{Binding Path=myDataContext}" IsEnabled="True" Focusable="True" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
Animation cannot change property values for a property path that does not exist. The "Image" you are trying to manipulate does not have "Rotate transform" you should add it and it should work:
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}">
<Image.RenderTransform >
<RotateTransform />
</Image.RenderTransform>
</Image>

Categories

Resources