BasedOn DataTriggers not Activating - wpf

I created a base style for my navigation menu. I want to change certain elements such as label content and icons based on a particular style while keeping the hover/click effects standard for all navigation items.
Here's my base XAML:
<Style x:Key="BaseNavigationStyle">
<Setter Property="Control.Cursor" Value="Hand"/>
<Setter Property="Control.Padding" Value="0,6,0,6"/>
<Setter Property="Control.Background" Value="{StaticResource NavigationItemBackgroundColorBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource NavigationItemBackgroundNormal}"/>
</DataTrigger.ExitActions>
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource NavigationItemBackgroundHover}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Here's my XAML for a Navigation Item:
<Style x:Key="ValidationNavigationStyle" BasedOn="{StaticResource BaseNavigationStyle}">
<Setter Property="Navigation:NavigationUserControl.Image">
<Setter.Value>
<BitmapImage UriSource="Assets/Navigation/validation.png"/>
</Setter.Value>
</Setter>
<Setter Property="Navigation:NavigationUserControl.Label" Value="Validation"/>
</Style>
Any ideas on why my all of the Setters (Cursor, Padding, Background) ARE being set but the MouseOver trigger is not working?
Side Note: If I cut/paste the trigger into the ValidationNavigationStyle, it will work as expected. I have a hunch that it has something to do with the RelativeSource binding but I can't quite figure out what it is.

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.

WPF Xaml Storyboard stops after dynamically changing Accent (Mahapps)

I have a WPF app which is using Mahapps for styling. The user has the ability to change the apps accent and or theme during run time which is done through the ThemeManager (a class provided by Mahapps). I have a Border which I have styled:
<Border Margin="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush3}"/>
<Setter Property="BorderThickness" Value="2 2 2 2"/>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush4}"/>
<Setter Property="CornerRadius" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsPopulated}" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush3}"/>
<Setter Property="BorderThickness" Value="2 2 2 2"/>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush4}"/>
<Setter Property="CornerRadius" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPopulated}" Value="False">
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="CornerRadius" Value="2"/>
<DataTrigger.EnterActions>
<BeginStoryboard Name="BeginStoryboard">
<Storyboard>
<ColorAnimation AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="BorderBrush.Color"
Duration="00:00:01"
From="Transparent"
To="IndianRed"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BeginStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid/>
</Border>
The styling and datatriggers work as expected. The border has a glowing red border brush which fades in and out - this is to grab the users attention. However, when the accent is changed at run time through a settings menu I have created, this seems to remove the animation and the border brush colour becomes the selected accent colour. The border thickness remains at 3 so I can see the setters for the data trigger stay the same, but the storyboard seems to be removed.
Any ideas as to why this is happening?

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.

Changing Property in ThicknessAnimation

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>

'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