Is there a workaround for throttled WPF DataTrigger events?
Coming from Why is this WPF animation only triggered once?, I eventually found out that WPF does indeed throttle its' binding events.
The code below demonstrates the behavior: clicking a button starts a task with a loop of 100 iterations that updates a property every 2ms. A control bound to this property counts how many times DataContextChanged
was fired.
On my machine, if I click the button 5 times, the output is something like this:
Iterations: 100, Count: 86
Iterations: 100, Count: 87
Iterations: 100, Count: 85
Iterations: 100, Count: 89
Iterations: 100, Count: 90
My original problem (I thought, see link above) was that an animation was only triggered once. I then found out, that it wasn't the animation, it was the DataTrigger
that wasn't responding. Then I studied the behavior of DataContextChanged
(see code) and now assume that basically WPF throttles all events.
I wanted to define the animation in XAML only, which means I have to depend on DataTrigger
firing "correctly" as in "always". It apparently doesn't. Must I - in order to achieve the animation I originally intended - implement my own event (that will not get throttled) and trigger the animation from code?
AnimationTestControl.xaml:
<UserControl x:Class="AnimationTests.AnimationTestControl"
x:Name="self"
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:local="clr-namespace:AnimationTests"
xmlns:helpers="clr-namespace:Iov.Common.Ui.Helpers;assembly=Iov.Common"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid DataContext="{Binding ElementName=self}">
<StackPanel Orientation="Horizontal">
<Button Content="Toggle Border" Click="ButtonToggle_Click" />
<Border x:Name="TestBorder" DataContext="{Binding ElementName=self, Path=TestBorderItem.MyState}"/>
</StackPanel>
</Grid>
</UserControl>
AnimationTestControl.xaml.cs:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace AnimationTests
{
public class TestItem : INotifyPropertyChanged
{
private int _myState;
public int MyState
{
get => _myState;
set { _myState = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class AnimationTestControl : UserControl
{
public AnimationTestControl()
{
InitializeComponent();
}
public TestItem TestBorderItem { get; } = new TestItem();
private void ButtonToggle_Click(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
var cnt = 0;
var max = 100;
void handler(object s, DependencyPropertyChangedEventArgs args) => cnt++;
TestBorder.DataContextChanged += handler;
for (int i = 0; i < max; i++)
{
TestBorderItem.MyState = i + 1;
Thread.Sleep(2);
}
Console.WriteLine($"{max}: {cnt}");
TestBorder.DataContextChanged -= handler;
});
}
}
}
from Recent Questions - Stack Overflow https://ift.tt/3vxuVav
https://ift.tt/eA8V8J
Comments
Post a Comment