多个PropertyChanged通知后多次调用WPF MeasureOverride

时间:2011-08-05 15:46:31

标签: .net wpf performance .net-3.5 measureoverride

在我的应用程序中,我有一个图形(自定义Wpf面板),其中Wpf控件作为元素。元素是从Wpf Control派生的自定义控件。 DataTemplates将控件与其视图模型相关联。 视图模型集合“GraphElements”绑定到itemsControl,如下所示。

<ItemsControl x:Name="PART_GraphItemsControl" Grid.Column="1"
              VerticalAlignment="Stretch" 
              HorizontalAlignment="Left"
              ItemsSource="{Binding Path=GraphElements}"
              VirtualizingStackPanel.IsVirtualizing="True"
              VirtualizingStackPanel.VirtualizationMode="Recycling">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <local:GraphDesignerPanel HorizontalAlignment="Stretch"
                                      VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

图表中的元素可以在2到500之间变化。在应用程序的特定模式下,元素显示其值。要显示该值,元素的ViewModel将触发INotifyPropertyChanged.PropertyChanged("VariableValue")

问题: 当我在图中有100多个元素时,每个元素视图模型都会触发INotifyPropertyChanged.PropertyChanged以显示元素值。这会导致MeasureOverride超过100次,导致内存和性能问题。

如何减少MeasureOverride调用次数?

图表元素的值显示XAML:

 <TextBlock  
    Text="{Binding Path=VariableValue, StringFormat={}({0})}" Width="60"
    FontSize="11" Name="txtBlk">
</TextBlock>

如果VariableValue为null,则上面的TextBlock会折叠

<DataTrigger Binding="{Binding Path=VariableValue}" Value="{x:Null}">
    <Setter TargetName="txtBlk"  Property="Visibility" Value="Collapsed"/>
</DataTrigger>

更新:此问题可在以下链接的示例中重现。下载,构建,调试。打开App后,在Window.xaml.cs MeasureOverride中设置断点。回到应用程序并点击“Click Me”按钮。断点被击中11次!。

http://sivainfotech.co.uk/measureoverrideissue.zip

任何想法都非常感激。

1 个答案:

答案 0 :(得分:2)

没有确切的答案,因为你的问题应该更好地澄清。无论如何,PropertyChanged事件的出现只是为了触发对订阅者的任何更改。

您没有指定元素是什么,如何在面板中托管,以及它们用于触发如此多的MeasureOverride的触发器类型。您可以选择不同的方法:取消绑定所有元素,然后再绑定它们。另一种解决方案是避免任何可能导致许多触发器的绑定,并通过每次调用InmvalidateVisual的DispatcherTimer更新UI。

希望它有所帮助。

编辑:我在你的消息来源上制作了一个技巧。我必须承认这是我从未使用过的东西。但是,请随时决定是否使用它。

1)在MeasureOverride中放置一个指标(避开断点):

    protected override Size MeasureOverride(Size availableSize)
    {
        System.Console.WriteLine("measure-override");
        return base.MeasureOverride(availableSize);
    }

2)在MyViewModel类中添加:

    private bool _keepSync = true;
    public bool KeepSync
    {
        get { return this._keepSync; }
        set
        {
            if (this._keepSync != value)
            {
                this._keepSync = value;
                this.Sync();
            }
        }
    }

3)修改MyProp集(与Width相同):

        set
        {
            if (value != prop)
            {
                this.prop = value;
                if (this.KeepSync)
                    this.NotifyPropertyChanged("MyProp");
            }
        }

4)在Window1类中添加以下DependencyProperty(对演示有用):

    public static readonly DependencyProperty KeepSyncProperty = DependencyProperty.Register(
        "KeepSync",
        typeof(bool),
        typeof(Window1),
        new PropertyMetadata(
            true,
            (obj, args) =>
            {
                var ctl = (Window1)obj;
                ctl.KeepSyncChanged(args);
            }));


    public bool KeepSync
    {
        get { return (bool)GetValue(KeepSyncProperty); }
        set { SetValue(KeepSyncProperty, value); }
    }


    private void KeepSyncChanged(DependencyPropertyChangedEventArgs args)
    {
        foreach (var vm in this.ViewModelCollection)
            vm.KeepSync = (bool)args.NewValue;
    }

5)修改按钮点击处理程序,如下所示:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        for (var i = 0; i < ViewModelCollection.Count; i++)
        {
            ViewModelCollection[i].Width += 10;
            ViewModelCollection[i].MyProp = string.Format("Item #{0}: {1}", i, ViewModelCollection[i].Width);
        }
    }

好吧,如果您尝试运行该应用程序,当您单击该按钮时,您会注意到Visual Studio的输出窗口中显示了几个“measure-override”。这是最初的行为。 (另请注意标记为true的ckeckbox,但不要取消标记!)

现在,关闭应用程序并重新启动。现在,UNMARK复选框,然后单击按钮。你会发现更少的“措施覆盖”调用。

希望这有帮助。

干杯

EDIT2:DAMN!...我忘了一块!...抱歉!

private void Sync()
{
  if (this.KeepSync == false)
    return;

  this.NotifyPropertyChanged("MyProp");
  this.NotifyPropertyChanged("Width");
}

重新欢呼声