DependencyProperty 忽略了一些 PropertyChanged 调用
DependencyProperty ignoring some of PropertyChanged calls
我的 DependencyProperty 有问题。假设你有一个更新一些 UI 元素的计时器,如果回调每 100 毫秒调用一次,这又会更新 UI 那么我没问题,但是,如果计时器设置为 ~10ms例如,某些呼叫将被忽略。我做了一个重现问题的小解决方案:
这是自定义 UI元素,具有依赖性 属性:
public class CustomLabel : Label
{
public float Range
{
get { return (float)GetValue(MaxRangeProperty); }
set { SetValue(MaxRangeProperty, value); }
}
public static readonly DependencyProperty MaxRangeProperty =
DependencyProperty.Register("Range", typeof(float), typeof(CustomLabel),
new PropertyMetadata(0f, RangePropertyChanged));
private static void RangePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = d as CustomLabel;
Debug.WriteLine("CustomLabel");
self.Content = self.Range;
}
}
这是一个触发计时器并更新 属性 的 ViewModel,后者又应在 CustomLabel 的 DependencyProperty 上调用回调。
public class ViewModel : INotifyPropertyChanged
{
Timer timer;
Thread t;
public ViewModel()
{
t = new Thread(() => timer = new Timer(new TimerCallback(CallBack), null, 0, 10));
t.Start();
Range = 100;
}
void CallBack(object state)
{
Range = (new Random()).Next(0, 1000);
}
private float _range;
public float Range
{
get { return _range; }
set
{
if (_range != value)
{
_range = value;
NotifyPropertyChanged();
Debug.WriteLine("ViewModel");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是我的 CustomLabel 和 ViewModel 所在的视图:
<Window x:Class="TimerTest.MainWindow"
xmlns:local="clr-namespace:TimerTest"
Title="MainWindow">
<Grid>
<local:CustomLabel x:Name="customLabel" Range="{Binding Range}"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
customLabel.DataContext = ViewModel;
}
public ViewModel ViewModel { get; set; }
}
因此,我在 DependencyProperty 的每一侧都做了一些 Debug.WriteLine()
语句,输出如下所示:
100ms 10ms
CustomLabel ViewModel
ViewModel CustomLabel
CustomLabel ViewModel
ViewModel ViewModel
CustomLabel CustomLabel
ViewModel ViewModel
CustomLabel ViewModel
ViewModel ViewModel
CustomLabel ViewModel
ViewModel CustomLabel
为什么会这样,我该怎么办?
谢谢你的时间。
NotifyPropertyChanged
事件由使用队列的 Dispatcher
处理。调度程序处理事件的速度低于将事件添加到队列的速度。
使用 DispatcherTimer
可能会让您更新得更快:
DispatcherTimer timer =
new DispatcherTimer(TimeSpan.FromMilliseconds(10),
DispatcherPriority.Normal,
delegate
{
MyCustomLabel.SetValue(MaxRangeProperty, viewModel.Range);
},
Dispatcher);
还有...
您使用的 System.Threading.Timer class 默认情况下不具有 10 毫秒的精度。它将使用操作系统定时器。
引用有关计时器分辨率的 Microsoft 文档:
The default timer resolution on Windows 7 is 15.6 milliseconds (ms)
可以通过调用 Windows API 来增加计时器分辨率,但这会导致电池耗尽。
我的 DependencyProperty 有问题。假设你有一个更新一些 UI 元素的计时器,如果回调每 100 毫秒调用一次,这又会更新 UI 那么我没问题,但是,如果计时器设置为 ~10ms例如,某些呼叫将被忽略。我做了一个重现问题的小解决方案:
这是自定义 UI元素,具有依赖性 属性:
public class CustomLabel : Label
{
public float Range
{
get { return (float)GetValue(MaxRangeProperty); }
set { SetValue(MaxRangeProperty, value); }
}
public static readonly DependencyProperty MaxRangeProperty =
DependencyProperty.Register("Range", typeof(float), typeof(CustomLabel),
new PropertyMetadata(0f, RangePropertyChanged));
private static void RangePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = d as CustomLabel;
Debug.WriteLine("CustomLabel");
self.Content = self.Range;
}
}
这是一个触发计时器并更新 属性 的 ViewModel,后者又应在 CustomLabel 的 DependencyProperty 上调用回调。
public class ViewModel : INotifyPropertyChanged
{
Timer timer;
Thread t;
public ViewModel()
{
t = new Thread(() => timer = new Timer(new TimerCallback(CallBack), null, 0, 10));
t.Start();
Range = 100;
}
void CallBack(object state)
{
Range = (new Random()).Next(0, 1000);
}
private float _range;
public float Range
{
get { return _range; }
set
{
if (_range != value)
{
_range = value;
NotifyPropertyChanged();
Debug.WriteLine("ViewModel");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是我的 CustomLabel 和 ViewModel 所在的视图:
<Window x:Class="TimerTest.MainWindow"
xmlns:local="clr-namespace:TimerTest"
Title="MainWindow">
<Grid>
<local:CustomLabel x:Name="customLabel" Range="{Binding Range}"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
customLabel.DataContext = ViewModel;
}
public ViewModel ViewModel { get; set; }
}
因此,我在 DependencyProperty 的每一侧都做了一些 Debug.WriteLine()
语句,输出如下所示:
100ms 10ms
CustomLabel ViewModel
ViewModel CustomLabel
CustomLabel ViewModel
ViewModel ViewModel
CustomLabel CustomLabel
ViewModel ViewModel
CustomLabel ViewModel
ViewModel ViewModel
CustomLabel ViewModel
ViewModel CustomLabel
为什么会这样,我该怎么办? 谢谢你的时间。
NotifyPropertyChanged
事件由使用队列的 Dispatcher
处理。调度程序处理事件的速度低于将事件添加到队列的速度。
使用 DispatcherTimer
可能会让您更新得更快:
DispatcherTimer timer =
new DispatcherTimer(TimeSpan.FromMilliseconds(10),
DispatcherPriority.Normal,
delegate
{
MyCustomLabel.SetValue(MaxRangeProperty, viewModel.Range);
},
Dispatcher);
还有...
您使用的 System.Threading.Timer class 默认情况下不具有 10 毫秒的精度。它将使用操作系统定时器。
引用有关计时器分辨率的 Microsoft 文档:
The default timer resolution on Windows 7 is 15.6 milliseconds (ms)
可以通过调用 Windows API 来增加计时器分辨率,但这会导致电池耗尽。