DependencyProperty忽略一些PropertyChanged调用

时间:2015-09-01 10:39:14

标签: c# wpf binding dependency-properties

我的DependencyProperty存在问题。假设您有一个更新某些UI元素的计时器,如果每100ms调用一次回调,这反过来更新UI然后我没有问题,但是,如果计时器设置为~10ms,例如,一些调用将获得忽略。我做了一个小解决方案来重现问题:

这是一个带有依赖属性的自定义UIElement:

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上的CallBack。

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所在的View和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

为什么会发生这种情况,我该怎么办呢? 谢谢你的时间。

1 个答案:

答案 0 :(得分:1)

NotifyPropertyChanged事件由使用队列的Dispatcher处理。调度程序正在以比它们添加到队列中更慢的速率处理事件。

使用DispatcherTimer可能会让您更快地更新:

DispatcherTimer timer =
    new DispatcherTimer(TimeSpan.FromMilliseconds(10),
                    DispatcherPriority.Normal,
                    delegate
                    {
                        MyCustomLabel.SetValue(MaxRangeProperty, viewModel.Range);
                    },
                    Dispatcher);

也...

默认情况下,您使用的System.Threading.Timer类的精度不能达到10毫秒。它将使用操作系统计时器。

引用有关计时器分辨率的Microsoft文档:

  

Windows 7上的默认计时器分辨率为15.6毫秒(ms)

可以通过调用Windows API来提高计时器分辨率,但这可能会导致电池耗尽。