WPF DataTrigger does not fire if value remains the same

I have a WPF data trigger that is set to fire when the value is true.

I want this trigger to fire every time this value is set to true, even if it was true before. Unfortunately, it seems that only fire if the value has changed from true to false or vice versa. My underlying data model fires the PropertyChanged event from INotifyPropertyChanged, even if the value is true twice in a row, but Trigger doesn't seem to pick it up.

In any case, to trigger the trigger regardless of whether the bound value has changed?

Some of you asked for the code, that’s all. It is interesting to note that converters will be called each time. The problem is more specific to running the animation.

Update If I change my code to reset the value to false, and then again to true, it will start the animation. Obviously, this is not perfect and does not make the code enjoyable to read. I hope there is a better way to do this.

Any help is greatly appreciated.

WPF Code

<Grid> <Grid.Resources> <Storyboard x:Key="AnimateCellBlue"> <ColorAnimation Storyboard.TargetProperty="Background.Color" From="Transparent" To="Blue" Duration="0:0:0.1" AutoReverse="True" RepeatBehavior="1x" /> </Storyboard> </Grid.Resources> <TextBox Name="txtBox" Text="{Binding DataContext.DisplayText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"> <TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <DataTrigger Binding="{Binding DataContext.IsTrue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard Name="BidSizeUpStoryB" Storyboard="{StaticResource AnimateCellBlue}" /> </DataTrigger.EnterActions> </DataTrigger> </Style.Triggers> </Style> </TextBox.Style> </TextBox> </Grid> 

Code for: -

 public partial class MainWindow : Window { private DataItem _dataItem; private DispatcherTimer _dispatcherTimer; public MainWindow() { InitializeComponent(); _dataItem = new DataItem(); _dataItem.DisplayText = "Testing"; _dataItem.IsTrue = true; this.DataContext = _dataItem; _dispatcherTimer = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, TimerCallbackHandler, Dispatcher); } private void TimerCallbackHandler(object s, EventArgs args) { Console.WriteLine("In Timer"); _dataItem.IsTrue = true; _dataItem.DisplayText = "Timer " + DateTime.Now.Ticks; } } 

DataItem: -

 public class DataItem : INotifyPropertyChanged { private bool _isTrue; private string _displayText; public bool IsTrue { get { return _isTrue; } set { _isTrue = value; NotifyPropertyChanged("IsTrue"); } } public string DisplayText { get { return _displayText; } set { _displayText = value; NotifyPropertyChanged("DisplayText"); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion private void NotifyPropertyChanged(string info) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info)); } } 
+6
source share
2 answers

The trigger will light regardless of the set value. Whenever a PropertyChanged event is raised for a property associated with a trigger, the trigger will be called. Here's the sample I checked for -

 <TextBox> <TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <DataTrigger Binding="{Binding DataContext.IsTrue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Converter={StaticResource MyConverter}}" Value="True"> <Setter Property="Background" Value="Red"/> </DataTrigger> </Style.Triggers> </Style> </TextBox.Style> </TextBox> 

I set a breakpoint on my converter and got it when I raise the PropertyChanged event for the IsTrue property. There will be some other problem in your code. Can you show a bit of your code where you ran into this problem?

+1
source

Update: It seems that my first answer depended on my ValueConverters. So, I did a bit more research and discovered ConditionalBehavior and PropertyChangedTrigger

Here I tested.

 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="WpfControlLibrary1.UserControl1" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"/> </UserControl.Triggers> <Grid> <i:Interaction.Triggers> <!--Use multiple PropertyChangedTriggers to handle multiple conditions--> <!--True State--> <ei:PropertyChangedTrigger Binding="{Binding DataContext.IsTrue, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" > <i:Interaction.Behaviors> <!--This is just a humble demonstration of Conditional Behavior, it has so much potential that you can replicate complex if conditions--> <ei:ConditionBehavior> <ei:ConditionalExpression ForwardChaining="And"> <ei:ComparisonCondition Operator="Equal" LeftOperand="{Binding DataContext.IsTrue, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" RightOperand="True" /> </ei:ConditionalExpression> </ei:ConditionBehavior> </i:Interaction.Behaviors> <!--I'm not sure why, but I needed to apply the default state first so that the true state plays it storyboard.--> <!--If you don't do this, this behaves like a data trigger.--> <ei:GoToStateAction StateName="DefaultState"/> <ei:GoToStateAction StateName="TrueState"/> </ei:PropertyChangedTrigger> <!--False state--> <ei:PropertyChangedTrigger Binding="{Binding DataContext.IsTrue, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" > <i:Interaction.Behaviors> <ei:ConditionBehavior> <ei:ConditionalExpression ForwardChaining="And"> <ei:ComparisonCondition Operator="Equal" LeftOperand="{Binding DataContext.IsTrue, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" RightOperand="False" /> </ei:ConditionalExpression> </ei:ConditionBehavior> </i:Interaction.Behaviors> <ei:GoToStateAction StateName="DefaultState"/> </ei:PropertyChangedTrigger> </i:Interaction.Triggers> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="VisualStateGroup"> <VisualState x:Name="DefaultState"/> <VisualState x:Name="TrueState"> <Storyboard Storyboard.TargetName="txtBox" > <ColorAnimation Storyboard.TargetProperty="Background.Color" From="Transparent" To="Blue" Duration="0:0:0.1" AutoReverse="True" RepeatBehavior="1x" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <TextBox x:Name="txtBox" Text="{Binding DataContext.DisplayText, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"/> </Grid> </UserControl> 

You can use Visual State groups for this. Moreover, the property of behavior and value can be tied to a database!

I quickly did this in a mixture and did not test. this seems like my solution.

 <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="WpfControlLibrary1.UserControl1" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <i:Interaction.Behaviors> <ei:DataStateBehavior Binding="{Binding DataContext.IsTrue, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" Value="True" TrueState="TrueState"/> </i:Interaction.Behaviors> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="VisualStateGroup"> <VisualState x:Name="DefaultState"/> <VisualState x:Name="TrueState"> <Storyboard Storyboard.TargetName="txtBox" > <ColorAnimation Storyboard.TargetProperty="Background.Color" From="Transparent" To="Blue" Duration="0:0:0.1" AutoReverse="True" RepeatBehavior="1x" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <TextBox x:Name="txtBox" Text="{Binding DataContext.DisplayText, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"/> </Grid> </UserControl> 
0
source

Source: https://habr.com/ru/post/899072/


All Articles