In my application, I have a graph (Custom Wpf Panel) with Wpf controls as elements. The elements are custom controls derived from Wpf Control. DataTemplates associate the controls to their view models. The view models collection, "GraphElements" is binded to the itemsControl as shown below.
<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>
The elements in the graph can vary from 2 to 500. Under certain mode of the application, the elements display their value. To display the value, the ViewModel for the element fires the INotifyPropertyChanged.PropertyChanged("VariableValue")
Issue: When i have 100+ elements in the graph, each elements view model fires INotifyPropertyChanged.PropertyChanged to have the elements value displayed. This calls MeasureOverride 100+ times leading to memory and peformance issues.
How can I reduce the number of MeasureOverride calls?
XAML for Value display for the graph element:
<TextBlock
Text="{Binding Path=VariableValue, StringFormat={}({0})}" Width="60"
FontSize="11" Name="txtBlk">
</TextBlock>
The TextBlock above is collapsed if VariableValue is null
<DataTrigger Binding="{Binding Path=VariableValue}" Value="{x:Null}">
<Setter TargetName="txtBlk" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
开发者_JAVA百科UPDATE: The issue is reproducible in the sample at the link given below. Download, build, debug. Once App is opened, set a breakpoint in Window.xaml.cs MeasureOverride. Come back to the App and hit the "Click Me" button. The breakpoint is hit 11 times !.
http://sivainfotech.co.uk/measureoverrideissue.zip
Any ideas greatly appreciated.
There's no a precise answer, because your problem should be clarified better. Anyway, the PropertyChanged event has born just to trigger any change to the subscribers.
You don't specify what the elements are, how are hosted in the panel, and what kind of trigger they do to fire so many MeasureOverride. You may choose a different approach: unbind all the elements, then bind them again. Another solution is to avoid any binding that could lead to many triggers, and update the UI via a DispatcherTimer calling an InmvalidateVisual every tick.
Hope it helps.
EDIT: I made a trick on your sources. I must admit that is something I never used. However, feel free to decide whether to use it.
1) place an indicator within the MeasureOverride (avoid the breakpoints):
protected override Size MeasureOverride(Size availableSize)
{
System.Console.WriteLine("measure-override");
return base.MeasureOverride(availableSize);
}
2) within the MyViewModel class add this:
private bool _keepSync = true;
public bool KeepSync
{
get { return this._keepSync; }
set
{
if (this._keepSync != value)
{
this._keepSync = value;
this.Sync();
}
}
}
3) modify the MyProp-set (same as for Width):
set
{
if (value != prop)
{
this.prop = value;
if (this.KeepSync)
this.NotifyPropertyChanged("MyProp");
}
}
4) within the Window1 class add the following DependencyProperty (useful for the demo):
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) modify the button click handler as follows:
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);
}
}
Well, if you try to run the application, when you click the button, you'll notice several "measure-override" displayed in the output window of Visual Studio. That's the original behavior. (Note also the ckeckbox marked as true, but DO NOT unmark it!)
Now, shut down the app and restart. Now, UNMARK the checkbox, then click on the button. You'll notice much less "measure-override" calls.
Hope this helps.
Cheers
EDIT2: DAMN!...I forget a piece!...Sorry!
private void Sync()
{
if (this.KeepSync == false)
return;
this.NotifyPropertyChanged("MyProp");
this.NotifyPropertyChanged("Width");
}
Re-cheers
精彩评论