I have a property called ResultChanged that bound a DataGrid item, and when the value is true the style applied on the DataGridCell will colorize the cell, so I need to blink the Cell of the DataGrid settings for 5 times the value of ResultChanged to true and false, this is my xaml design:
<DataGridTextColumn Header="{DynamicResource hour}" Binding="{Binding Result}">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource MaterialDesignDataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding ResultChanged}" Value="True">
<Setter Property="Background" Value="Orange" />
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
<Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
I tried to implement the logic in this way:
int i = 0;
bool blinkOn = false;
while (true)
{
if(!blinkOn)
{
mtc.ResultChanged = true;
blinkOn = false;
}
else
{
mtc.ResultChanged = false;
blinkOn = true;
}
i++;
System.Threading.Thread.Sleep(1000);
//Stop blinking after 5 times
if (i == 5)
break;
}
the problem is that the Cell will always colorized and I don't see any blinking, any idea?
Can you try this style for your DataGridCell ?
<DataGridTextColumn Header="{DynamicResource hour}" Binding="{Binding Result}">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource MaterialDesignDataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding ResultChanged}" Value="True" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="Blink"
AutoReverse="True"
RepeatBehavior="5x">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01"
Value="Orange" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
BeginTime="00:00:00"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01"
Value="Black" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
The above style will AutoReverse itself. If you want to have the Orange color stay after the animation, you can set AutoReverse to False. But this will not get the same Easing action on reverse.
If you prefer to have the Easing action on reverse, you can introduce another action (StoryBoard) to do that after the intial StoryBoard completes at 00:00:10. Like,
<DataGridTextColumn Header="{DynamicResource hour}" Binding="{Binding Result}">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource MaterialDesignDataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding ResultChanged}" Value="True" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<Storyboard x:Name="Blink"
AutoReverse="True"
RepeatBehavior="5x">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01"
Value="Orange" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
BeginTime="00:00:00"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01"
Value="Black" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard AutoReverse="False">
<ColorAnimationUsingKeyFrames BeginTime="00:00:10" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Orange" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
You can play around the AutoReverse and BeginTime to your animation preference.
Related
I am designing a ListView and I want to change its border color when it is focused, IsFocused property dosen't seem to work for ListView. Is there any property similar to IsFocused for Listview?
<Style x:Key="{x:Type ListView}" TargetType="ListView">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="{StaticResource EnabledListViewBorder}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Border x:Name="Border"
Background="Transparent"
Padding="{TemplateBinding Padding}"
BorderBrush="{StaticResource EnabledListViewBorder}"
BorderThickness="1">
<ScrollViewer Style="{DynamicResource ListViewColumnHeaderScrollViewer}">
<ItemsPresenter />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DiabledListViewBorder}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DiabledListViewBorder}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="GotFocus">
<BeginStoryboard>
<Storyboard Duration="0:0:0:1" AutoReverse="False">
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" From="Red" To="Red"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<BeginStoryboard>
<Storyboard AutoReverse="False" Duration="0:0:0:1">
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" From="Green" To="Green"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
I have updated the question with my Sourcecode with the solution provided but the Border Color isn't changing. What am i missing here.
You can use Style.Triggers to handle GotFocus and LostFocus events.
<ListView.Style>
<Style TargetType="ListView">
<Style.Triggers>
<EventTrigger RoutedEvent="GotFocus">
<BeginStoryboard>
<Storyboard Duration="0:0:0:1" AutoReverse="False">
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" From="Red" To="Red"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<BeginStoryboard>
<Storyboard AutoReverse="False" Duration="0:0:0:1">
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" From="Green" To="Green" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
Update:
If you have rewritten the ControlTemplate, then delete Style.Triggers part and move EventTriggers from it to the ControlTemplate.Triggers. Additionally you have to set Storyboard.TargetName="Border".
<EventTrigger RoutedEvent="LostFocus">
<BeginStoryboard>
<Storyboard AutoReverse="False" Duration="0:0:0:1" Storyboard.TargetName="Border" >
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" From="Green" To="Green" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
For ListBox IsFocused not working because focus goes to ItemsPresenter that contains ListBoxItems. So focus go to ItemsPresenter that is inside the ListBox. I got workaround that you subscribe event MouseDown and inside it do Focus for ListView, then the trigger starts working.
Let me know if it works for you
I am attempting to animate the background colour of a label on the data change of a property. The label is changing colour up to the value of 3. however if the property returns to a value of 1, the animation stays on the previous colour. My XAML is below.
<Style x:Key="Colors" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Red"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Yellow"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="3">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Green"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Label XAML
<Label x:Name="vClock" Content="{Binding Path=Clock,Mode=OneWay}" FontSize="25" Style="{StaticResource Colors}">
</Label>
I think you need to remove the other active storyboard first, but it's is better to proceed declaring your storyboard in the resources:
<Storyboard x:key="Value 1">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Red"/>
</Storyboard>
<Storyboard x:key="Value 2">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Yellow"/>
</Storyboard>
<Storyboard x:key="Value 3">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Green"/>
</Storyboard>
then look at the datatrigger
<Style x:Key="Colors" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ColorNo}" Value="1">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Value2" />
<StopStoryboard BeginStoryboardName="Value3" />
<BeginStoryboard Storyboard="{StaticResource Value1}"
x:Name="Value1" />
</DataTrigger.EnterActions>
</DataTrigger>
...
</Style.Triggers>
</Style>
Do the same for all the triggers
I want to use an image to show state and I want one of those images to be a loading spinner. I'm rotating the image when its a spinner using a trigger. However, this approach seems to throw the error "Cannot animate '(RenderTransform).(RotateTransform.Angle)' on an immutable object instance"
<ItemsControl ItemsSource="{Binding StatefulViewModels}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Expander IsEnabled="{Binding IsCompleted}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<Image Margin="5" Width="18">
<Image.RenderTransform>
<RotateTransform Angle="0" CenterX="9" CenterY="9"/>
</Image.RenderTransform>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedWithErrors}">
<Setter Property="Source" Value="../Images/Failed.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedSuccessfully}">
<Setter Property="Source" Value="../Images/Success.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Unexecuted}">
<Setter Property="Source" Value="../Images/Waiting.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Running}">
<Setter Property="Source" Value="../Images/Running.png"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:2"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Margin="5" Text="{Binding Path=Title}" FontSize="16"/>
</StackPanel>
</Expander.Header>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ResultTemplateSelector}"/>
</Expander>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've read an MSDN article explaining about datatriggers and storyboards but I can't think of a good alternative to this approach.
Whats the correct way to achieve this/fix my approach?
Try to set the RenderTransform property of the Image using a Style setter instead of setting it using a local value:
<Image Margin="5" Width="18">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="0" CenterX="9" CenterY="9"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedWithErrors}">
<Setter Property="Source" Value="../Images/Failed.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.CompletedSuccessfully}">
<Setter Property="Source" Value="../Images/Success.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Unexecuted}">
<Setter Property="Source" Value="../Images/Waiting.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentState}" Value="{x:Static enums:State.Running}">
<Setter Property="Source" Value="/Images/Running.png"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:2"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
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>
I have the following xaml
<DataGrid Name ="TheGrid" Margin="0,21,0,0" DataContext ="{Binding StreamItems}" ItemsSource="{Binding}" CanUserReorderColumns="True" CanUserResizeColumns="True"
CanUserResizeRows="False" CanUserSortColumns="True" AutoGenerateColumns="False" RowHeight="21">
<DataGrid.Resources>
<Style TargetType="DataGridCell" x:Key="FlashStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentState}" Value="Idle" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="BlinkIdle" >
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<DiscreteColorKeyFrame Value="DarkOrchid" KeyTime="0:0:0" />
<DiscreteColorKeyFrame Value="DarkOrchid" KeyTime="0:0:5" />
<LinearColorKeyFrame Value="Transparent" KeyTime="0:0:8" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentState}" Value="Streaming" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="BlinkStreaming">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<DiscreteColorKeyFrame Value="DarkGreen" KeyTime="0:0:0" />
<DiscreteColorKeyFrame Value="DarkGreen" KeyTime="0:0:5" />
<LinearColorKeyFrame Value="Transparent" KeyTime="0:0:8" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
The problem i'm having, is when i scroll, or the user sorts columns, the animation is triggers. how do i tell the animation to trigger only once, when the data changes?