在我的应用程序中,我有一个图形(自定义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调用次数?
<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
任何想法都非常感激。
答案 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");
}
重新欢呼声