IsMouserOver Interference With IsSelected in WPF - c#

Hey guys I have an issue with triggers for IsMouserOver and IsSelected.
My goal is to create an animation that changes the BorderThickness of my ListViewItems in IsMouserOver. Using EnterActions and ExitActions yields the desired result, however, when I try to also take into account the IsSelected property in another trigger, every property but BorderThickness can be set.
When I remove the whole IsMouseOver trigger, BorderThickness will be set in IsSelected and displayed correctly.
<Style TargetType="{x:Type ListViewItem}" x:Key="SubMenuStyles">
<Setter Property="Height" Value="40"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="Orange"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="BorderThickness" From="0,0,0,0" To="10,0,0,0"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="BorderThickness" From="10,0,0,0" To="0,0,0,0"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="ListViewItem.IsSelected" Value="True">
<Setter Property="Background" Value="#233E4F"/>
<Setter Property="BorderThickness" Value="50,0,0,0"/>
<Setter Property="BorderBrush" Value="Orange"/>
</Trigger>
</Style.Triggers>
</Style>

Problem
The behavior you encountered before is shown in the picture below:
where the problem is that your orange border is being reset every time the mouse is over the selected item.
What I believe you want to achieve is to keep the fixed 50px border on a selected item, like shown below:
Solution
In order to achieve that, we need to find a way to execute the animation only for items that are not selected (i.e. where IsSelected="False").
This is where MultiTriggers come into play.
MultiTriggers are quite like "normal" Triggers with the important addition that they fire not when only one condition is fulfilled, but when all conditions are fulfilled.
e.g.
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
<!-- More conditions, if you want -->
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<!-- Will only be set when ALL conditions are fulfilled. -->
</MultiTrigger.Setters>
<MultiTrigger.EnterActions>
<!-- Will also only be executed when ALL conditions are fulfilled -->
</MultiTrigger.ExitActions>
</MultiTrigger>
Code
So, in your case, adjust your style to look like this:
<Window.Resources>
<Style x:Key="SubMenuStyles" TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="40" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="ListViewItem.IsSelected" Value="True">
<Setter Property="Background" Value="#233E4F" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="50,0,0,0" />
<Setter Property="BorderBrush" Value="Orange" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="BorderBrush" Value="Orange" />
</MultiTrigger.Setters>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness"
From="0,0,0,0"
To="10,0,0,0"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness"
From="10,0,0,0"
To="0,0,0,0"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListView Width="200"
Height="150"
Margin="30">
<ListViewItem Style="{StaticResource SubMenuStyles}">A ListView</ListViewItem>
<ListViewItem IsSelected="True" Style="{StaticResource SubMenuStyles}">with several</ListViewItem>
<ListViewItem Style="{StaticResource SubMenuStyles}">items</ListViewItem>
</ListView>
</Grid>

Related

C# implementation Button Animation shows weird behaviour [duplicate]

I am currently working on a C# WPF project and I am trying to make my custom style buttons.
What I want to have to happen, is when the mouse hovers over the button, it slightly increases in size as an animation, then when the mouse leaves the button, the animation decreases the size of the button to the original size.
Below is my ControlTemplate that I've created for my button. No errors are thrown but nothing happens either.
<Application.Resources>
<Style x:Key="RoundCorner" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="ButtonGrid">
<Border x:Name="border" CornerRadius="8" BorderBrush="White" BorderThickness="2">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.FontWeight="Bold"></ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="Blue"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleX)"
To="0.95" Duration="0:0:0.05" />
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleY)"
To="0.95" Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleX)"
To="1.08" Duration="0:0:0.05" />
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(ScaleTransform.ScaleY)"
To="1.08" Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="ButtonGrid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
Thanks for any help you can provide
You have couple of problems with your code:
1) You are trying to animate Rectangle.RenderTransform properties and there is no Rectangle in your ControlTemplate. RenderTransform is a dependency property on UIElement. So, you should remove Rectangle
2) Also, There is no RenderTransform applied to your Grid.
3) After fixing above two items, if you try you get continuous animation (Button expanding/shrinking in size), to fix this set Background property Grid to Transparent, so that Grid participates in hit testing and respond to Mouse overs.
Update your style XAML to the following and it will work:
<Style x:Key="RoundCorner" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="ButtonGrid" Background="Transparent">
<Border
x:Name="border"
BorderBrush="White"
BorderThickness="2"
CornerRadius="8">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.FontWeight="Bold">
</ContentPresenter>
</Border>
<Grid.RenderTransform>
<ScaleTransform />
</Grid.RenderTransform>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Blue"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.05"
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.95"/>
<DoubleAnimation
Duration="0:0:0.05"
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.95"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.05"
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="1.08"/>
<DoubleAnimation
Duration="0:0:0.05"
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="1.08"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonGrid" Property="Opacity" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Another thing, instead of using Trigger.EnterActions and Trigger.ExitActions, you could use VisualStateManager to achieve the same result. Using VisualStateManager is much more easier than Trigger.EnterActions and ExitActions.
Below is the code with VisualStateManager used to do the animations:
<Style x:Key="RoundCorner" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="ButtonGrid" Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.05" To="MouseOver"/>
<VisualTransition GeneratedDuration="0:0:0.05" To="Normal"/>
</VisualStateGroup.Transitions>
<VisualStateGroup.States>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.95"/>
<DoubleAnimation
Storyboard.TargetName="ButtonGrid"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.95"/>
</Storyboard>
</VisualState>
</VisualStateGroup.States>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border
x:Name="border"
BorderBrush="White"
BorderThickness="2"
CornerRadius="8">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.FontWeight="Bold">
</ContentPresenter>
</Border>
<Grid.RenderTransform>
<ScaleTransform/>
</Grid.RenderTransform>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Blue"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonGrid" Property="Opacity" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

SelectedItem Style don't work for ListBox with DataTemplate?

I have a ListBox with a DataTemplate and I need to change the background color of the selected item. I have found some example and tried all of them but probably I missed some point.
I tried to change the SolidColorBrush resource and add the trigger for IsSelected in the ListBox Style and for ListBoxItem Style but none of those work.
<Window.Resources>
<Style x:Key="ListBoxGreenStyle" TargetType="{x:Type ListBox}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="LightGreen" />
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ListBoxDragDrop" TargetType="{x:Type ListBoxItem}">
<Setter Property="AllowDrop" Value="true"/>
<EventSetter Event="PreviewMouseMove" Handler="ListBox_PreviewMouseMove"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBox_PreviewMouseLeftButtonDown"/>
<EventSetter Event="Drop" Handler="ListBox_Drop"/>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="LightGreen" />
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<ListBox x:Name="lstSlides" Grid.Row="1" Grid.Column="0" Grid.RowSpan="1" Margin="5" Drop="lstSlides_Drop" DragEnter="lstSlides_DragEnter" AllowDrop="True" ItemContainerStyle="{DynamicResource ListBoxDragDrop}" SelectionMode="Extended" SelectionChanged="lstSlides_SelectionChanged" Style="{DynamicResource ListBoxGreenStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Image}" Height="32" Width="32" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Label Grid.Column="1" Content="{Binding ItemCount}" HorizontalAlignment="Center"/>
<Label Grid.Column="2" Content="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What is the correct Style where to add the triggers or to change the SolidColorBrush Resource?
You should change your Style "ListBoxDragDrop" adding the default ItemContainerStyle. Then you can change the SolidColorBrush as you like.
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="LightGreen"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="Green"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="LightGreen"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="Green"/>
<Style x:Key="ListBoxDragDrop" TargetType="{x:Type ListBoxItem}">
<Setter Property="AllowDrop" Value="true"/>
<EventSetter Event="PreviewMouseMove" Handler="ListBox_PreviewMouseMove"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBox_PreviewMouseLeftButtonDown"/>
<EventSetter Event="Drop" Handler="ListBox_Drop"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The simplest way is to get rid of the Style you declare for your ListBoxItem and use instead a DataTrigger in the DataTemplate you use inside your ListBox:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" Value="True">
<Setter TargetName="grid" Property="Background" Value="Red"></Setter>
</DataTrigger>
</DataTemplate.Triggers>
(Don't forget to add x:Name="grid" to your Grid.)
The RelativeSource binding lets WPF look up the visual tree until it finds a ListBoxItem (and it will, because you are templating the Content of each ListBoxItem).
This will sets the Background property of the content of the ListBoxItem to Red. It won't change the Background property of the ListBoxItem itself!
If that's what you want, the more complicated but most powerful option is to completely overwrite the Template property of ListBoxItem using a Style:
<Style x:Key="{x:Type ListBoxItem}" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" Padding="2" SnapsToDevicePixels="true">
<Border.Background>
<SolidColorBrush Color="Transparent" />
</Border.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Green" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This way, you can redefine each property in all states (Selected, Unselected, Focused etc).
This is only a starting point inspired by this MSDN page.
Sadly, this is not possible to override only part of a ControlTemplate. If you rewrite it, you need to rewrite every part of it, even the parts you don't wish to change.

Strange ColorAnimation and MultiTrigger behavior on ListView

I have a listview. I want to implement this behavior:
The initial color of non-clicked item is Gray
When mouse is over non-clicked item item, the item changes from Gray to Black. When mouse moves out, the item changes its color back to Gray
When item is clicked it changes to Red
When mouse is over the clicked item, the item remains Red anyway.
Below is how I try to implement it:
<Style TargetType="{x:Type ListViewItem}" x:Key="PackageListViewItemStyle">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Padding" Value="1,1" />
<Setter Property="HorizontalContentAlignment"
Value="{Binding HorizontalContentAlignment,
RelativeSource={RelativeSource FindAncestor,
AncestorLevel=1,
AncestorType={x:Type ItemsControl}}}" />
<Setter Property="VerticalContentAlignment"
Value="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource FindAncestor,
AncestorLevel=1,
AncestorType={x:Type ItemsControl}}}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Gray" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="{StaticResource PackageListItemPrimaryForegroundColorBrush}" />
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2"
SnapsToDevicePixels="True"
Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
StrokeDashArray="1 2"
StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
ContentTemplate="{TemplateBinding ContentTemplate}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="Background" Value="Red" />
<Setter TargetName="Bd" Property="BorderThickness" Value="2" />
</Trigger>
<MultiTrigger>
<!-- mouse hovers -->
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard Name="glowsb">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Gray" To="Black"
Duration="0:0:0.9"
AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard Name="glowsbback">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Black"
To="Gray"
Duration="0:0:0.9"
AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="Bd"
Property="TextElement.Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see the animation is used only for mouseOver/mouseLeave event.
Result: animation works as expected but when I click an item it becomes Gray instead of Red.
I figured that here Gray comes from ExitAction Animation "To" property.
If I change it like this:
<MultiTrigger.ExitActions>
<BeginStoryboard Name="glowsbback">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Black" To="Green" Duration="0:0:0.9" AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
then the clicked item becomes Green.
Ok, let's get rid of the "To" property:
<MultiTrigger.ExitActions>
<BeginStoryboard Name="glowsbback">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Black" Duration="0:0:0.9" AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
Cool, the clicked item becomes Red as expected, but it goes to Red animated, whereas I put it in Setter without animation:
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="Background" Value="Red" />
<Setter TargetName="Bd" Property="BorderThickness" Value="2" />
</Trigger>
What did I miss?
I dont't know if this is the most elegant way to solve your problem.
In WPF Triggers are processed in the order they are declared.
See also In WPF, does the order of Triggers matter?
Therefore you can put your "IsSelected"-Trigger right after your MultiTrigger and stop that animation by using StopStoryboard
Result:
...
<ControlTemplate.Triggers>
<MultiTrigger>
<!-- mouse hovers -->
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard Name="glowsb">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Gray" To="Black"
Duration="0:0:0.9"
AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard Name="glowsbback">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd"
From="Black"
Duration="0:0:0.9"
AutoReverse="False"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsSelected" Value="true">
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="glowsbback" />
</Trigger.EnterActions>
<Setter TargetName="Bd" Property="Background" Value="Red" />
<Setter TargetName="Bd" Property="BorderThickness" Value="2" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="Bd"
Property="TextElement.Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
...

How can I detect if textbox has text?

I have a textbox which has an animation but i want it to stop that animation if the textbox has text and start again if the textbox doesn't have text? But i'm not sure if this is possible as the animation is linked to another textbox?
TextBoxStyle1 is the animation.
TextBoxStyle2 is where the text input will be.
Here is my code;
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<local:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />
<Storyboard x:Key="StoryboardBorder">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0" Value="#FFABADB3"/>
<EasingColorKeyFrame KeyTime="0:0:0.6" Value="#FF00BCD4"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<FontFamily x:Key="MainFont">Arial</FontFamily>
<SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
<SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FFC1C1C1"/>
<SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF00BCD4"/>
<Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FontFamily" Value="{StaticResource MainFont}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ControlTemplate.Resources>
<Storyboard x:Key="StoryboardTextAnimation">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="TextBox">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-23.333"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="TextBox">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.76"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="TextBox">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.76"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="TextBox">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-25.597"/>
</DoubleAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBox">
<EasingColorKeyFrame KeyTime="0" Value="#FF8B8B8B"/>
<EasingColorKeyFrame KeyTime="0:0:0.2" Value="#FF00BCD4"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="StoryboardTextAnimation_Copy1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="TextBox">
<SplineDoubleKeyFrame KeyTime="0" Value="-23.333"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="TextBox">
<SplineDoubleKeyFrame KeyTime="0" Value="0.76"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="TextBox">
<SplineDoubleKeyFrame KeyTime="0" Value="0.76"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="TextBox">
<SplineDoubleKeyFrame KeyTime="0" Value="-25.597"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBox">
<SplineColorKeyFrame KeyTime="0" Value="#FF00BCD4"/>
<SplineColorKeyFrame KeyTime="0:0:0.2" Value="#FFC1C1C1"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border x:Name="border" SnapsToDevicePixels="True" BorderThickness="0,0,0,2" BorderBrush="Transparent">
<ScrollViewer x:Name="TextBox" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" RenderTransformOrigin="0.5,0.5" Content="Floating Label Text" Background="White" Margin="0,3.75,0,3.25">
<ScrollViewer.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</ScrollViewer.RenderTransform>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource StoryboardBorder}" />
<BeginStoryboard Storyboard="{StaticResource StoryboardTextAnimation}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource StoryboardTextAnimation_Copy1}" />
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
<SolidColorBrush x:Key="brushWatermarkForeground" Color="LightGray" />
<SolidColorBrush x:Key="TextBox.MouseOver.Border2" Color="#FF7EB4EA"/>
<Style x:Key="TextBoxStyleNew" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ControlTemplate.Resources>
<Storyboard x:Key="StoryboardAnimateText"/>
</ControlTemplate.Resources>
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True" BorderThickness="0,0,0,2" Margin="0,-5,0,0">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border2}"/>
<Setter Property="BorderThickness" TargetName="border" Value="0"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
<Setter Property="BorderThickness" TargetName="border" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
<Style x:Key="TextBlockStyle1" TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="TextTrimming" Value="None"/>
</Style>
<SolidColorBrush x:Key="TextBox.MouseOver.Border3" Color="#FF00BCD4"/>
<SolidColorBrush x:Key="TextBox.Focus.Border2" Color="#FF00BCD4"/>
<Style x:Key="TextBoxStyle2" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Name="grid1" Focusable="True">
<Grid x:Name="TextBoxes" Margin="23.75,0,-23.75,0">
<TextBlock Margin="250.449,182.112,374.044,0" Text="Hint Text" Foreground="{StaticResource brushWatermarkForeground}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" Height="19.725" VerticalAlignment="Top" Style="{DynamicResource TextBlockStyle1}" />
<TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="#FFC1C1C1" Margin="250.449,182.112,352.952,0" Height="25.689" VerticalAlignment="Top" BorderThickness="0,0,0,2" Style="{DynamicResource TextBoxStyle2}" />
<TextBox x:Name="textBox1" Text="Floating Label Text" Height="25.689" VerticalAlignment="Top" Margin="250.449,182.112,352.952,0" Style="{DynamicResource TextBoxStyle1}" BorderThickness="1" Foreground="#FFC1C1C1" Background="White" BorderBrush="#FFC1C1C1"/>
</Grid>
</Grid>
Not sure why the down votes, as solving this would have been a good exercise for anyone looking to hone their WPF style/template skills.
There are two important things to remember when creating styles/templates, especially when triggers get involved.
The order of the triggers matters.
If you find yourself scratching your head, you need to look at it from another angle and simplify.
At the point where you got, it would have been a good time to go ahead and make a custom control, in fact the example I provide below is going to be just that.
For what you are attempting to do, you have 3 possible outcomes to test for. If we take a few steps back and look, we can see exactly what we need to do, I'll break it down.
IsFocused
Color the border and label (This happens no matter what)
!IsFocused & Text.Length = 0
ENTER: Lower and resize the label.
EXIT: Raise and resize the label.
IsFocused & Text.Length = 0
ENTER: Display the hint.
EXIT: Hide the hint.
That's it, those are the only three triggers that we need, one Trigger and two MultiTriggers. You could get creative and make it only one trigger really, but maintainability and readability would be horrendous.
Let's start with the code for the custom control:
public class AnimatedTextBox : TextBox
{
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof (string), typeof (AnimatedTextBox),
new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty HintProperty =
DependencyProperty.Register("Hint", typeof (string), typeof (AnimatedTextBox),
new FrameworkPropertyMetadata(null));
static AnimatedTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (AnimatedTextBox),
new FrameworkPropertyMetadata(typeof (AnimatedTextBox)));
}
public string Label
{
get { return (string) GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public string Hint
{
get { return (string) GetValue(HintProperty); }
set { SetValue(HintProperty, value); }
}
}
I've just made Label and Hint strings to keep things simple, you could make them objects and expand the possibilities some.
And now the style:
<Color x:Key="Color.Control.Border.Focus">#FF00BCD4</Color>
<SolidColorBrush x:Key="SolidColorBrush.Control.Border" Color="#FFABADB3" />
<SolidColorBrush x:Key="SolidColorBrush.Hint" Color="LightGray" />
<Style TargetType="{x:Type local:AnimatedTextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="BorderBrush" Value="{StaticResource SolidColorBrush.Control.Border}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AnimatedTextBox}">
<ControlTemplate.Resources />
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0,0,0,2"
SnapsToDevicePixels="True">
<StackPanel>
<TextBlock x:Name="LabelTextBlock"
Focusable="False"
Foreground="{TemplateBinding BorderBrush}"
RenderTransformOrigin="0.5,0.5"
Text="{TemplateBinding Label}">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX=".75" ScaleY=".75" />
<TranslateTransform X="0" Y="0" />
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
<Grid>
<ScrollViewer x:Name="PART_ContentHost"
Focusable="false"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
<TextBlock x:Name="HintTextBlock"
Margin="5 0 0 0"
Focusable="False"
Foreground="{StaticResource SolidColorBrush.Hint}"
IsHitTestVisible="False"
Opacity="0"
Text="{TemplateBinding Hint}" />
</Grid>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.6"
Storyboard.TargetProperty="BorderBrush.Color"
To="{StaticResource Color.Control.Border.Focus}" />
<ColorAnimation Duration="0:0:0.6"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="Foreground.Color"
To="{StaticResource Color.Control.Border.Focus}" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.6" Storyboard.TargetProperty="BorderBrush.Color" />
<ColorAnimation Duration="0:0:0.6"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="Foreground.Color" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Text.Length, RelativeSource={RelativeSource Self}}" Value="0" />
<Condition Binding="{Binding Path=IsFocused, RelativeSource={RelativeSource Self}}" Value="false" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
To="1" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
To="1" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
To="15" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="LabelTextBlock"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Text.Length, RelativeSource={RelativeSource Self}}" Value="0" />
<Condition Binding="{Binding Path=IsFocused, RelativeSource={RelativeSource Self}}" Value="true" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="HintTextBlock"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="HintTextBlock"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style is 95% what you had, I just combined it into one control. The only thing I really changed, was I set up the template to be defaulted in the 'IsFocused' and 'HasText' state. The idea is that our control always strives to be in that state, so it's easier to trip it up and set it to the other state. There are a few more ifs and buts the other way around, so coding for the least-common denominator plays to our benefit.
The usage would be as follows:
<Grid>
<StackPanel VerticalAlignment="Center">
<local:AnimatedTextBox Width="300"
Margin=" 0 0 0 15"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Hint="Hint 1"
Label="Label 1" />
<local:AnimatedTextBox Width="300"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Hint="Hint 2"
Label="Label 2" />
</StackPanel>
</Grid>
It may not be perfect, but hopefully that gets you on the right track.

Validation.HasError and custom ToolTip

I'm setting extra ToolTip for a field with errors in ResourceDictionary
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
I also defined special style for this tooltip, which simulate the one on red triangle in right upper corner
<Style TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Placement" Value="Right"/>
<Setter Property="HorizontalOffset" Value="5"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border Name="Border"
Background="Red"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<ContentPresenter Margin="5 2 5 3"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Opened">
<BeginStoryboard>
<Storyboard TargetProperty="HorizontalOffset">
<DoubleAnimation From="0" To="5" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Everything works, but now ALL ToolTips have this style.
Is it possible to achieve that ToolTip style take his part only if Validation.HasError occurs? I can as x:Key, but how to apply that to Style.Triggers part?
Because I have this ToolTip defined also on other controls I wouldn't like to copy all this code multiple times, but if only that is a solution I will do so :(
After another couple of hours of Googling I try the solution provided here
https://social.msdn.microsoft.com/Forums/vstudio/en-US/10d2ecbf-9e6e-4414-b57e-79dd02e0944e/changing-style-of-tooltip-in-textbox
and it works!
A created ToolTip style
<ToolTip x:Key="ErrorToolTip"
Placement="Right"
Background="Red"
Foreground="White"
BorderThickness="0"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<ToolTip.Content>
<Binding Path="(Validation.Errors)[0].ErrorContent"/>
</ToolTip.Content>
<ToolTip.Triggers>
<EventTrigger RoutedEvent="ToolTip.Opened">
<BeginStoryboard>
<Storyboard TargetProperty="HorizontalOffset">
<DoubleAnimation From="0" To="5" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
and changed Style.Triggers to
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{StaticResource ErrorToolTip}" />
</Trigger>
</Style.Triggers>
Need to do some more styling, but it's OK for now.
You can set the property Validation.ErrorTemplate on the TextBox style:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate"
Value="{StaticResource ValidationTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError"
Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
However, this way you can't work with a ToolTip style, but you have to work with a template and a x:Key.
<ControlTemplate x:Key="ValidationTemplate">
<Border Name="Border"
Background="Red"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<ContentPresenter Margin="5 2 5 3"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Opened">
<BeginStoryboard>
<Storyboard TargetProperty="HorizontalOffset">
<DoubleAnimation From="0" To="5" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>

Categories