Trigger Visibility change before it is successfully applied - c#

I have the style for one of my user controls:
<Style x:Key="AddServerPanel" TargetType="{x:Type uc:AddServer}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:0.2" From="252,550,26,0" To="252,248,26,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:0.2" From="252,248,26,0" To="252,550,26,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
I have a user control hidden in the background appearing when I click on the button. I want to have a slide in and slide out animation. This code already works for sliding in but sliding out doesn't works properly as the control just disappears. I think this is because the visibility property is applied to the control and afterwards the trigger is called. How could I fix this behaviour to have a proper "slide out" effect on the control?
Edit
I changed my code and switched over to a DataTrigger.
However this is working but I just see the animation when the Binding property value is false. This is really curious.
Also it would be nice to know how the set the "From" property in the ThicknessAnimation to the current margin of the control.
<uc:AddServer x:Name="AddServerPanel" Height="300" Width="570" Margin="252,550,26,0">
<uc:AddServer.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AddServerPanelVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:2" From="252,550,26,0" To="252,248,26,0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AddServerPanelVisible}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:2" From="252,248,26,0" To="252,550,26,0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</uc:AddServer.Style>
</uc:AddServer>

If you are using animations to show/hide the control, chances are you are manipulating the opacity of said control. If that's the case, you can begin your Storyboard in code, and, when completed, set visibility flag to true. This will run animation first, then set the visibility.
HOWEVER. I should point out that because you are already hiding the control via opacity, there's no real need to change it's visibility at that point.
Another option would be to set a visibility flag (that doesn't bind to the control's visibility property), and when flag value changes, start appropriate Storyboard (e.g., if flag is true, show control, or begin show animation).
I also wanted to explain why what you currently have does what it does.
Whenever you set a trigger, you are telling the control to do something when a property equals such and such value. What happens is the control is first hiding, and when visibility is hidden, only then will it start the hiding animation. Only problem is...it's already hidden! That's where my solution comes in ;)
Edit
Realized your animating margin, not opacity. In either case, the control is always hidden before beginning the animation, which is the problem.
Solution
Here's an example:
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Fade in animation -->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Fade out animation -->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
IsVisible is a flag you'd have to define. The rest is pretty straightforward.

Related

How to dynamically choose TextBlock animation type in wpf?

I would like to create simple animation which type is based on some value. I need to change color of text in TextBlock control, but the target color is depending on bounded variable. I've already created 2 DataTriggers and depending on a value of my bounded variable, a proper animation should start. At the beginning everything seems to work properly (AnimationValue is equal to 0 on start), when the value changes to 1, animation runs, then value returns to 0. The problem is, when the value turns to 2 (Animation with another color also runs) and then again 0, the first animation is not going to run anymore but the second one still works in a proper way.
<Border
Grid.Column="0"
Background="Transparent">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="32"
Foreground="White"
Text="MyText">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AnimationValue}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="Gray"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="White"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AnimationValue}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="Firebrick"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="White"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
There is no problem with setting a correct value - i've checked it using a debugger and everytime correct value is set. 0 is always between 1 and 2. DataContext is also not a problem - a connection between View and ViewModel is not being broken. I've noticed that broken animation is always the first one in xaml file. Now the "Gray" animation stops working correctly, but if i change order in xaml file, firebrick animation will be the broken one. Thanks for any help.
It looks like running StoryBoards in triggers is a weird thing. I don't have a technical explanation as to how they behave but here is a SO question where I found an answer for you.
Here is some code based on the answer above that works:
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AnimationValue}" Value="0">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Animation1" />
<StopStoryboard BeginStoryboardName="Animation2" />
<BeginStoryboard x:Name="Animation0">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="White"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AnimationValue}" Value="1">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Animation0" />
<StopStoryboard BeginStoryboardName="Animation2" />
<BeginStoryboard x:Name="Animation1">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="Gray"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AnimationValue}" Value="2">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Animation0" />
<StopStoryboard BeginStoryboardName="Animation1" />
<BeginStoryboard x:Name="Animation2">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
To="Firebrick"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>

Fading out a WPF control on Visibility.Hidden

I'd like to use style to make a WPF control (in this case a 3rd party one from Telerik, but could be anything) fade out as it's Visibility property is changed to Hidden. This approach I'm trying to use works well for fading in content after it's set to Visibility.Visible. But this does NOT work the opposite way:
<Style x:Key="FadeoutStyle" TargetType="telerik:RadDiagramShape">
<Style.Triggers>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
I'm pretty sure of why this doesn't work: as soon as the control is set to Hidden, WPF stops drawing it. So my animation might even be playing, but it's not showing because Visibility.Hidden immediately got applied.
Question 1: Is my assumption correct? Question 2: What is a good pattern to work around this? I can imagine a solution where a custom property initiates the fadeout anim and uses a callback once done that applies the actual Visibility.Hidden setting. Is there a cleaner solution?
Visibility.Hidden issues aside, ultimately it turned out that I was using the trigger incorrectly. My working version (using a different property) looks like this:
<Trigger Property="IsEnabled" Value="False">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>

'Foreground' property does not point to a DependencyObject in path '(Foreground).(0)'

I have a WPF canvas project where I drag and drop objects on the canvas from a toolbox. Based upon certain data, some of those objects should flash or blink. I get an unhandled exception :Cannot animate '(Foreground).(0)' on an immutable object instance.. Following is my code. Somebody suggested using (Foreground).(SolidColorBrush.Color) and I changed that in my markup but it doesn't seem to fix it.
<!-- DataTemplate for DesignerCanvas look and feel -->
<DataTemplate DataType="{x:Type viewModels:SingleValueControlViewModel}">
<Grid>
<Label Name="label" Content="{Binding TagValue}" IsHitTestVisible="False" Height="{Binding ItemHeight}" Width="{Binding ItemWidth}"
Background="{Binding BackColor}"
Foreground="{Binding ForeColor}"
BorderBrush="{Binding BorderColor}"
BorderThickness="{Binding StyleProperties.BorderWidth}"
FontFamily="{Binding StyleProperties.Font.FontFamily}"
FontSize = "{Binding StyleProperties.Font.Size}"
FontStyle="{Binding StyleProperties.Font.Style}"
HorizontalContentAlignment="{Binding TextAlign}" >
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding StyleProperties.FlashEnable}" Value="true">
<Setter Property="Label.Background" Value="Black"></Setter>
<Setter Property="Label.Foreground" Value="Red"></Setter>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
Duration="00:00:00:01"
From="Black" To="Red">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Grid>
</DataTemplate>
/* Final answer after examining user's code
*/
Add this to your DiagramControl.xaml
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Opacity" Value="1"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To=" 0.1" Duration="00:00:0.3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
/* Fresh update as user still can't run his animation and reports error said in comment
*/
You must be binding your Background, Foreground properties, and remember Brush objects are immutable. There is a workaround as described in following msdn link :
immutable instance animation error
debugging animations
/* New answer posted after user updated his question with present XAML code */
I have used your code as it is like below with one addition of TargetType="Label" and it is working perfectly. I used my own StyleProperties.FlashEnable binding for your DataTrigger to work.
This is one side. Another side : You are doing all this dynamically as you are dragging items to Canvas. For this you need to apply your style/triggers in code.
<Grid>
<Label Content="Hi ! I am added dynamically" TextBlock.FontSize="45">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding StyleProperties.FlashEnable, Mode=OneWay}" Value="true">
<Setter Property="Label.Background" Value="Black"></Setter>
<Setter Property="Label.Foreground" Value="Red"></Setter>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
Duration="00:00:00:01"
From="Black" To="Red">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Grid>
/* Old answer posted before user updated his question */
To show your object flashing, you must be changing their Foreground property. And this Foreground color must be coming from some variable. For binding you have to use a dependency property, or your class containing your property/variable must implement INotifyPropertyChanged, so that your property raise PropertyChanged event.
You should also provide some initial value to Foreground if you are using Animation.
You also might be using DynamicResource instead of StaticResource.
More can be said if you post some XAML.
This works for me:
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding FlashEnable}" Value="True">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="Red"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1" From="Black" To="Red">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
You can also explicitly set a SolidColorBrush in the style setter of the Foreground property:
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding FlashEnable}" Value="True">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground">
<Setter.Value>
<SolidColorBrush Color="Red"/>
</Setter.Value>
</Setter>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation
Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1" From="Black" To="Red">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>

WPF Animation Triggers Regardless of Property Value

I am trying to make a smooth fade in/out animation for an inline alert box in a WPF form. The problem is that the animation seems to run even when the property is not set to the value expected in the trigger. I've put a breakpoint in the converter method and it never returns anything except Visibility.Hidden, however the trigger on Visibility.Visible is firing the animation of the Height property (may or may not be firing the one on opacity too but I couldn't tell since the Grid is Visibility.Hidden).
Here is the style:
<Style TargetType="Grid" x:Key="AlertStyle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:02"
BeginTime="00:00:00"
To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:01"
BeginTime="00:00:00"
To="60.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
The Grid in question is here:
<Grid DockPanel.Dock="Top"
Visibility="{Binding Path=Message, Converter={StaticResource NullToVis}}"
Style="{StaticResource AlertStyle}">
<ContentPresenter Content="{Binding Path=Message, Mode=OneWay}"/>
</Grid>
The Value Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value == null) ? Visibility.Hidden : Visibility.Visible;
}
When I add a second trigger to the <Style.Triggers> element neither animation ever runs.
The second trigger:
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:01"
BeginTime="00:00:00"
To="0.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:02"
BeginTime="00:00:00"
To="0.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
I've never tried to do anything related to Animation in WPF before and I've found similar styles peppered all over the internet but haven't been able to modify any successfully to get them to do what I'm trying to accomplish.
Try this:
<Style TargetType="Grid" x:Key="AlertStyle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard Name="Storyboard">
<Storyboard BeginTime="00:00:00">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:02" To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:01" To="60.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="Storyboard" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
If that doesn't work, then you can try using a DataTrigger with a custom Boolean value... you can't go too far wrong with that.

WPF TextBlock ColorAnimation

I am totally new with WPF animations, and for the moment I face big problems with this.
I have a TextBlock for which, as long as a property to my view model has a specific value, I want to run a ColorAnimation to its background color. When the value to my property changes I want the background color for my TextBlock to return back to the original one (probably Transparent). Here is what I discovered so far, but it still not working as I described.
<TextBlock Text="{Binding DatabaseTasks.Count, StringFormat= 'Count: {0}'}" VerticalAlignment="Center" Background="Transparent">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsIdle}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
<ColorAnimation RepeatBehavior="Forever"
FillBehavior="Stop"
From="Red"
To="Transparent"
By="Blue"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Can someone fix this?
Thank you in advance!
EDIT:
The binding works just fine and I tested it using a simple DataTrigger that just changes the background color. My problem is that animation does not stop and return to the orginal color. I am sure I miss something.
Adding the RemoveStoryboard tag should do it.
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsIdle}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="tt" >
<Storyboard Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
<ColorAnimation RepeatBehavior="Forever"
FillBehavior="Stop"
From="Red"
To="Transparent"
By="Blue"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="tt"></RemoveStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>

Categories