When setting Storyboard.TargetName on custom control in trigger, throws exception - wpf

I have custom button control FancyButton (created for reuse obviously) like this below and some code behind (mainly dependency properties):
<Button x:Class="Views.Controls.FancyButton"
...... >
<Button.ContentTemplate>
<DataTemplate>
....
</DataTemplate>
</Button.ContentTemplate>
</Button>
Below, a usage of this control, trigger should start to switch popup property on mouse up but instead of it I'm facing exception that says
'Popup' name cannot be found in the name scope of
'Views.Controls.FancyButton'.
<controls:FancyButton x:Name="ChooseButton" ServerConnection="{Binding CurrentServer}">
<Button.Triggers>
<EventTrigger SourceName="ChooseButton" RoutedEvent="UIElement.PreviewMouseLeftButtonUp">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup" Storyboard.TargetProperty="(IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</controls:FancyButton >
<Popup Placement="Bottom" Name="Popup">
..........
</Popup>
Previous solution - when used Button control directly - was working as expected but I had a XAML duplication.
What does it change and how to make it work?

Solved!
Instead of using Storyboard.TargetName attached property I used Storyboard.Target and proper binding as below:
<Button.Triggers>
<EventTrigger SourceName="ChooseButton" RoutedEvent="UIElement.PreviewMouseLeftButtonUp">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.Target="{Binding ElementName=popup1}" Storyboard.TargetProperty="(IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Cheers

Related

Add Color Animation to Mah:Tile

I'm Using MahApps.Metro and I'm using a Tile Element which Is loaded with namespace 'MahCtrl'( xmlns:MahCtrl="http://metro.mahapps.com/winfx/xaml/controls). I want to apply ColorAnimation to the Tile on MouseEnter and MouseLeave.
Here is the xaml snippet I am Currently working on.
<UserControl xmlns:MahCtrl="http://metro.mahapps.com/winfx/xaml/controls">
<MahCtrl:Tile Cursor="Hand" Background="Transparent" Height="200" Width="210"HorizontalContentAlignment="Center">
<MahCtrl:Tile.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.200"
Storyboard.TargetProperty="(MahCtrl:Tile.Background).Color"
To="#fffccc" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.250"
Storyboard.TargetProperty="(MahCtrl:Tile.Background).Color"
To="#ffffff" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</MahCtrl:Tile.Triggers>
</MahCtrl:Tile>
</UserControl>
In the debug section, When I Enter mouse into the Tile the following exception occurs
System.InvalidOperationException: 'Cannot resolve all property
references in the property path '(0).Color'. Verify that applicable
objects support the properties.'
I have tried Using (Background).Color and a lot of other combinations to StoryBoard.TargetProperty But this method works when MahCtrl:Tile element is wrapped with a stack panel and applying event triggers MouseEnter and MouseLeave triggers with target (StackPanel.Background).Color to the StackPanel. How can I target background property of MahCtrl:Tile
It would be a great help if someone can refer to a documentation regarding this topic
Thank you very much,

DoubleAnimation for textblock height

I have a textblock which should only show 2 lines of the text, while it is unselected. As soon as it gets selected, I want it to expand smoothly.
I started with something like:
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Second"
Storyboard.TargetProperty="(TextBlock.MaxHeight)"
To="50.0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
But the issue here is, that I don't know how big the text is.
You should be able to use From='0' instead, which would start the animation with a value of 0 and end with whatever the value of MaxHeight is. However, that raises a different problem, as MaxHeight defaults to infinity, which would make the animation far too fast. Adding an ObjectAnimationUsingKeyframes at the start that sets MaxHeight to ActualHeight might work to resolve this. Something like this:
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyframes
Storyboard.TargetName='Second'
Storyboard.TargetProperty='(TextBlock.MaxHeight)'>
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{Binding TargetName=Second, Path=ActualHeight}" />
</ObjectAnimationUsingKeyframes>
<DoubleAnimation
Storyboard.TargetName="Second"
Storyboard.TargetProperty="(TextBlock.MaxHeight)"
From="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
You can use DoubleAnimation to implement this. I have implemented this in a sample application.
<Window.Resources>
<Storyboard x:Key="OnGotFocus">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBox" Storyboard.TargetProperty="(FrameworkElement.Height)">
<EasingDoubleKeyFrame KeyTime="0:0:2">
<EasingDoubleKeyFrame.Value>
<System:Double>NaN</System:Double>
</EasingDoubleKeyFrame.Value>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnLostFocus">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBox" Storyboard.TargetProperty="(FrameworkElement.Height)">
<EasingDoubleKeyFrame KeyTime="0">
<EasingDoubleKeyFrame.Value>
<System:Double>NaN</System:Double>
</EasingDoubleKeyFrame.Value>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="30" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="UIElement.GotFocus" SourceName="textBox">
<BeginStoryboard Storyboard="{StaticResource OnGotFocus}" />
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.LostFocus" SourceName="textBox">
<BeginStoryboard x:Name="OnLostFocus_BeginStoryboard" Storyboard="{StaticResource OnLostFocus}" />
</EventTrigger>
</Window.Triggers>
Your code for textbox should be :
<TextBox x:Name="textBox"
Height="30"
HorizontalAlignment="Right"
Text="Hello World" />
This will animate the textbox to a specified height as soon as it gets focussed. I have added an animation to collapse it as well when it loses focus.
Hope this helps you.

One Storyboard for multiple triggers

Context: I have two Storyboards in my UserControl. One of them is for sliding the UserControl in and one is for sliding it out.
All I'm doing for the slide in/out is setting the margin to a negative value for sliding out and to zero for sliding in.
Now, I want the sbShowLeftMenu storyboard to be executed when the UserControl's visibility is set to Visible. Also, I want to be able to manually slide the UserControl in/out using the Buttons BtnHide & BtnShow.
Now if the UserControl becomes visible the sbShowLeftMenu is activated and the UserControls gets moved in. Switching the visibility between Collapsed and Visible this behaviour continues, as I want it to.
Now if I hit the BtnHide to move the UserControl out of viewport everything works fine until I start switching the Visibility of the UserControl again. Now the Storyboard doesn't work anymore. I can still move the UserControls in/out with the Buttons but the 'Visible' Trigger does not start the Storyboard.
Here are gif examples of the behavior:
With the click on 'Database Search' I set the Visibility of the UserControl to Visible (because its bound to the 'IsExpanded' property of the ExpanderControl) and it works just fine:
Here I demonstrate what happens after I manually click the 'BtnHide':
This is the code for the UserControl:
<UserControl.Resources>
<Style x:Key="TextBlockStyle">
<Setter Property="TextBlock.FontSize" Value="10"></Setter>
<Setter Property="TextBlock.Margin" Value="1"></Setter>
<Setter Property="TextBlock.VerticalAlignment" Value="Center"></Setter>
</Style>
<Storyboard x:Key="sbShowLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="-650,0,0,0" To="0,0,0,0" DecelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
<Storyboard x:Key="sbHideLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="0,0,0,0" To="-650,0,0,0" AccelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid Background="Red">
<StackPanel Panel.ZIndex="2" Name="pnlLeftMenu" Orientation="Horizontal" HorizontalAlignment="Left" Margin="-650,0,0,0" Height="500">
<!-- Content -->
<Border>The Content is in here</Border>
<Grid>
<Button x:Name="BtnShow" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 0,0 L 0.5,0.5 L 0,1"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbShowLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button x:Name="BtnHide" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" Visibility="Collapsed" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 1,1 L 0.5,0.5 L 1,0"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbHideLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsVisible"
Value="True"
my:TriggerTracing.TriggerName="BoldWhenMouseIsOver"
my:TriggerTracing.TraceEnabled="True">
<Trigger.EnterActions>
<BeginStoryboard Name="sbShowLeftMenu" Storyboard="{StaticResource sbShowLeftMenu}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Name="xy" Storyboard="{StaticResource sbHideLeftMenu}"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
You seem to have some complex logic which is interfering with the operations of the story boards.
I would recommend that you not try do this in triggers but instead in code behind. Create a state machine operation and then open/close/make visible/make invisible depending on the state.
The following code is what I use for similar logic of moving a panel based on boolean state via storyboards. You can expand it to handle visibility as well with other states.
private bool moveRight = true; // Start out on the left side, then move right.
public void MoveRight()
{
try
{
if (moveRight)
{
(Resources["MoveToOpen"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowClose"] as Storyboard)?.Begin(this, false);
}
else
{
(Resources["MoveToClose"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowOpen"] as Storyboard)?.Begin(this, false);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
moveRight = !moveRight;
}

Can't assign RoutedEvent to Binding.TargetUpdated

I have a textblock which should react for changing its text (it must display text for few seconds, and then dissapear).
<TextBlock Text="{Binding Path=OperationMessage, NotifyOnValidationError=True}" x:Name="label_OperationMessage" Visibility="Collapsed" HorizontalAlignment="Right" Margin="3,3,3,3" >
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2" From="1.0" To="0.0" BeginTime="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
When I launch my app I get error :
Failed to assign to property 'System.Windows.EventTrigger.RoutedEvent'.
on the line
<EventTrigger RoutedEvent="Binding.TargetUpdated">
What is wrong with this code?
The only event supported by Silverlight for use in the Triggers property is the Loaded event. Anything else will result in this exception.
To acheive this sort of Xaml event logic you can download the Blend SDK which contains all manner of useful features like this. In your case you are looking for the PropertyChangedTrigger.

How do I bind to a color in a WPF ColorAnimation?

I would like to do something that is seemingly quite simple, but I cannot figure out how to do it. I have a ColorAnimation that is triggered when the MouseEnter event occurs. It simply changes the background color of a Border from one color to another color.
Unfortunately, I can't figure out how to put anything but hardcoded colors into this ColorAnimation. So it looks currently like this:
<Style x:Key="MouseOverStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.5" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="Red" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
However, I'd like do something either like this:
<SolidColorBrush x:Key="MyEventColor" Color="{Binding EventColor}" />
<Style x:Key="MouseOverStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.5" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="{StaticResource MyEventColor}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
Or like this:
<Style x:Key="MouseOverStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.5" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="{Binding EventColor}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
When I try to do either of those, an exception gets thrown. For the first, it throws an exception telling me essentially that the "Color" property can't take a SolidColorBrush value...which makes sense...but it certainly doesn't help me out because the ColorAnimation won't let me animate the "(Border.Background).(SolidColorBrush)" property...it will only let me animate the "(Border.Background).(SolidColorBrush.Color)" property....
The exception on the second example basically tells me that it "Cannot freeze this Storyboard timeline tree for use across threads" ...so it sounds like the ColorAnimation is trying to do this binding in some other thread than the UI thread or something? Whatever it's trying to do...it isn't working.
How the heck can I do such a simple task?
For the first one, you could use {StaticResource MyColor} with MyColor defined as such:
<Color x:Key="MyColor">#FF00FF00</Color>
However, this doesn't solve your problem: you can't bind to animation properties since those properties need to be frozen (unchangeable) for the animation to work. Either try to remove your dependence on a binding, or recreate the storyboard with the correct color from code behind when the color changes.

Resources