Changing Property in ThicknessAnimation - wpf

I'm using a label in my project to show some news in moving style by thicknessanimation, but I can't change the content of label after the animation completes. What can I do?
First when the window loads I activate a thicknessanimation with the code below
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard TargetName="MyLabel">
<ThicknessAnimation Storyboard.TargetProperty="Margin" SpeedRatio="0.8" RepeatBehavior="Forever"
FillBehavior="HoldEnd" From="0,0,0,0" To="100,0,0,0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
Then I Set a Style on the label
<Style TargetType="Label">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=MyLabel, Path=Margin}" Value="100,0,0,0" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapes" />
</MultiDataTrigger>
</Style.Triggers>
I tried to change the content with IsVisibleChanged but it didn't work.

Based on your code snippet the first issue is your MultiDataTrigger. You have the condition and the setter backwards try something like this and see if it works
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Property="Visibility" Value="Collapsed" />
</MultiDataTrigger.Conditions>
<Setter Binding="{Binding ElementName=Label1, Path=Margin}" Value="0,0,0,0"/>
The Condition tag is for the event or property you're looking for to trigger the Setter tag which will set the properties of the element you want to change.
Also a MultiDataTrigger is probably not the tag you need to change the margin this code should be a bit simpler
<Style TargetType="Label">
<Style.Triggers>
<Trigger Property="Visibility" Value="Collapsed">
<Setter Property="Margin" TargetName="MyLabel" Value="0,0,0,0"/>
</Trigger>
</Style.Triggers>

Related

WPF Highlight background when dragged over

I have successfully implemented drag and drop in my application. An am now working on improving the user experience.
My aim is when a drag starts to highlight my possible targets, and then if they are dragged over change to a different colour.
I came up with this which almost works, however it seems to sometimes miss the DragLeave Event. The style is applied to any of my controls that are being used as a drop target (multiple types)
<Style x:Key="HighlightDrop">
<Setter Property="Control.Background" Value="Orange" /> <!-- usually set to transparent, just set to orange here to make it obvious-->
<Style.Triggers>
<EventTrigger RoutedEvent="UIElement.DragEnter">
<BeginStoryboard x:Name="Highlight">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="Background.Color"
Duration="0:0:0">
<DiscreteColorKeyFrame Value="LightGreen" KeyTime="0:0:0" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.DragLeave">
<StopStoryboard BeginStoryboardName="Highlight" />
</EventTrigger>
<DataTrigger Binding="{Binding AmDragging, RelativeSource={RelativeSource AncestorType=Window}}" Value="True" >
<Setter Property="Control.Background" Value="LightBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
I then tried a different approach, but this didn't work wither as it appears that the IsMouseOver event doesn't work during dragging.
<Style x:Key="HighlightDrop">
<Setter Property="Control.Background" Value="Orange" />
<!-- usually set to transparent, just set to orange here to make it obvious-->
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AmDragging, RelativeSource={RelativeSource AncestorType=Window}}" Value="True" />
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Control}}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Control.Background" Value="LightGreen" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Control}}" Value="True" > <!-- Just used to test that the binding for IsMouseOver is working -->
<Setter Property="Control.Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding AmDragging, RelativeSource={RelativeSource AncestorType=Window}}" Value="True" >
<Setter Property="Control.Background" Value="LightBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
What am I missing here, it feels like I am re-inventing the wheel when the job should be pretty basic to acheive.
drag and drop works is quite hard to understand and annoying thing.
drag event sometimes is consumed by another control.
especially, if your xaml page has content control. content control has PreviewDragEnter, PreviewDragLeave, PreviewDragOver. you should call following source code when its event fired. e.Handled = true.
In my case, I was about to move ListBox Item ordering with drag and drop interaction. ListBox Item contains TextBox, ComboBox wich is IsEditable=true, and ContentControl. those controls were consuming drag leave event when mouse is over it while drag leaving. so I called e.Handled = true. on PreviewDragEnter, PreviewDragLeave, PreviewDragOver event of those controls.

Mutual Toggle buttons - Using two toggle buttons to represent tri-state using triggers

I need two toggle buttons to reset each other. Similar to radio buttons but with tri-state. i.e.
Button 1 is checked
Button 2 is checked
None is checked
I tried the following:
<ToggleButton x:Name="Button1" IsEnabled="{Binding IsConnected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource DetailedSettingsButtonStyle}" >
<Setter Property="IsChecked" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=IsChecked}" Value="True">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ToggleButton x:Name="Button2"
IsEnabled="{Binding IsConnected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource DetailedSettingsButtonStyle}" >
<Setter Property="IsChecked" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button1, Path=IsChecked}" Value="True">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
The result is when Button 1 is checked and I click button 2, button 1 gets unchecked as expected, but button 2 stays unchecked, until I press it again.
So in order to toggle between button 1 and 2, I have to click twice on button 2.
In other words, considering the three states I am trying to create, I have to always pass through the state of non-is-selected everytime, and no way to directly move between the other two states.
Any idea what am I doing wrong?
You should be able to do this using Storyboards:
<ToggleButton x:Name="Button1" IsEnabled="{Binding IsConnected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource DetailedSettingsButtonStyle}">
<Setter Property="IsChecked" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=Button2}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ToggleButton x:Name="Button2" IsEnabled="{Binding IsConnected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource DetailedSettingsButtonStyle}">
<Setter Property="IsChecked" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=Button1}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
You can add the data-trigger for other case as well (Value=False, Set IsChecked=True) for both.
<DataTrigger Binding="{Binding ElementName=Button1, Path=IsChecked}" Value="False">
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
But you will lose tri-state behavior. Better to create converters and bind them to a view-model property.

Strange conflict of XAML IsMouseOver and IsEnabled causing button to appear active when disabled

In my application, I have a custom button style and template that has a couple states based on XAML triggers:
<Style x:Key="UniversalButton" TargetType="{x:Type Button}">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="#FFD7D7D7" />
<Setter Property="BorderBrush" Value="#FF999999" />
<Setter Property="Foreground" Value="#FF666666" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Style x:Key="ButtonBorder" TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3" />
<Setter Property="BorderThickness" Value="0,0,0,2" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
<Style x:Key="ButtonContent" TargetType="{x:Type ContentPresenter}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Margin" Value="8,7,8,8" />
<Setter Property="RecognizesAccessKey" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Setter Property="Opacity" Value="0.8" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsEnabled" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.8" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.6" />
</Trigger>
</ControlTemplate.Triggers>
<Border Style="{DynamicResource ButtonBorder}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="{TemplateBinding Padding}"
Opacity="{TemplateBinding Opacity}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter Style="{DynamicResource ButtonContent}"
TextBlock.FontWeight="{TemplateBinding FontWeight}"
TextBlock.Foreground="{TemplateBinding Foreground}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The specific instance of the button in question has a command with a CanExecute based on the selections of an ItemsControl in the view. When I toggle my selections, including those that are meant to disable the button, things work fine. The button becomes semi-transparent as I have called for in the XAML trigger for IsEnabled.
The problem starts when I mouse over the button, activating the storyboards above. After this happens, changing the selections to a state in which the button is disabled leaves the button fully opaque, regardless of the state of IsEnabled/CanExecute.
Through extensive debugging, I've determined that the issue is not caused by the CanExecute as previously thought, and that it must be linked to the IsMouseOver trigger. I've exhausted all of my XAML knowledge in trying to figure this conflict out and I'd be very grateful for help.
Thanks in advance!
I believe that the issue is caused by Dependency Property value precedence in WPF. In this case the animation (Timeline) has the default FillBehavior of FillBehavior.HoldEnd. After the animation finishes all the attempts to change the value (the Opacity property) should have a higher precedence. From the link you can see only system coercing the value can override the animation-based value (even locally setting such as by assignment directly in codebehind cannot override this).
That's why your template Trigger (whose precedence is 7) cannot override the current animated value. There are some solutions to this but I think you should use all storyboard in your triggers (instead of using a setter). If you don't want any animation, just set Duration to zero:
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To=".8" Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<!-- similar for ExitActions -->
</Trigger>
<!-- -->
<Trigger Property="IsEnabled" Value="False">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To=".6" Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<!-- similar for ExitActions -->
</Trigger>
Using Storyboard this way is also good for maintenance. That's when you want to add some animation to the property value change, then you already have the Storyboard there and you can just add more options or animations...
PS: Don't be afraid of long XAML code. It's the nature and a characteristic of XAML code. However as a trade-off, we have more controls over the behaviors and effects.
I just have the same problem.
After several tests, I found the ExitActions of IsEnabled trigger, though finished its execution, would still somehow "stays" there and blocks the upcoming animation.
I haven't figured out the exact reason behind it, yet emprically, removing that storyboard before beginning another seems to solve the problem.
Codes like:
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Trigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="someStoryboard" />
<BeginStoryboard>
<!-- animation here -->
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Trigger.ExitActions>
<BeginStoryboard x:Name="someStoryboard">
</Trigger>

Animate background colour on exitaction

I'm trying to animate the background colour on a grid to change, once an event happens, but I can't get it working, I can get it to change colour immediately (via data triggers), but as soon as I try to introduce an animation into it, then I can't get it working (the animation doesn't seem to come into effect).
This is the current XAML I'm using (though I've tried various variations and cannot get it to animate):
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="False">
<Setter Property="Background" Value="LightYellow" />
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:02" To="White" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<!--
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
-->
Where Viewed is a dependency property (bool) on my Control. Any hints in the right direction would be appreciated. I've also tried setting it as an EventTrigger on a raised event which happens when the bool switches to true.
Thanks to Clemens helps, figured out what I needed to do:
<SolidColorBrush x:Key="GridColourBrush" Color="LightYellow" />
<Style x:Key="GridStyle" TargetType="Grid">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=me, Path=Viewed}" Value="False">
<Setter Property="Background" Value="{StaticResource GridColourBrush}" />
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:02" To="White" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- snipped stuff -->
<Grid MinWidth="525" x:Name="ContainerGrid" Style="{StaticResource GridStyle}" Background="{StaticResource GridColourBrush}" />
So setting the background to be flat white by default, then if the DP bool is false, change the background to the static solid colour brush, which I can then animate via the exit actions.
What i meant was simply that instead of
<Grid Background="LightYellow">
</Grid>
you would have to write
<Grid>
<Grid.Background>
<SolidColorBrush Color="LightYellow" />
</Grid.Background>
</Grid>
No need to have an extra resource.

'Setter' object cannot be added to 'EventTrigger'?

I get the following error on the WPF code bellow:
'Setter' object cannot be added to 'EventTrigger'. The given object must be an instance of TriggerAction or a derived type.
<Style x:Key="LinkLabel" TargetType="{x:Type Label}">
<Setter Property="FontFamily" Value="Tahoma"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter" >
<Setter Property="Cursor" Value="Hand"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseDown" >
<Setter Property="Foreground" Value="Red"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseUp" >
<Setter Property="Foreground" Value="DarkBlue"/>
</EventTrigger>
</Style.Triggers>
</Style>
Can anyone explain and point me in the right direction ?
MadSeb
Option 1 - Use BeginStoryboard like ChrisF said
Option 2 - Don't use EventTrigger - for example, for a label that changes background color when you mouse over it use:
<Style TargetType="Label">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true" >
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Unfortunately there is no property IsMouseDown you can use.
About your example:
You don't have to set the cursor on MouseEnter, just set the Cursor property and it will only affect the mouse cursor when the mouse is over the control.
If you want to create an hyperlink control don't use a label, eitehr use a Button with a custom template (this will give you the Click event and IsPressed property) or, even better - use the Hyperlink class like this:
<TextBlock><Hyperlink>This is a link</Hyperlink></TextBlock>
This will have all the styling you wanted.
You must use a TriggerAction to change the values as in this example from the EventTriggers MSDN page:
<Style TargetType="Rectangle">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
<Setter Property="Margin" Value="20" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="300" Duration="0:0:1.5"
AccelerationRatio="0.10" DecelerationRatio="0.25"
Storyboard.TargetProperty="(Canvas.Width)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1.5"
AccelerationRatio="0.10" DecelerationRatio="0.25"
Storyboard.TargetProperty="(Canvas.Width)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>

Resources