I have an application where I need to perform a fairly expensive calculation based on the actual width and height of a control. I have bound ActualWidth and ActualHeight of this control to properties in my code. I find that Actualheight is being changed in small steps. I assume this is because of the Layout Engine.
A stripped down version of the xAML is
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sup="clr-namespace:RowAnimation.Support"
xmlns:vm="clr-namespace:RowAnimation.ViewModels"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:RowAnimation.Views"
x:Name="RowerAnimation"
x:Class="RowAnimation.Views.RowerAnimationControl"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance {x:Type vm:RowerAnimationControlVM}}"
d:DesignHeight="497" d:DesignWidth="741">
<Grid>
<DockPanel >
<Grid Margin="5" x:Name="canvas1" ClipToBounds="True" VerticalAlignment="Top">
<sup:DataPiping.DataPipes>
<sup:DataPipeCollection>
<sup:DataPipe Source="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}"
Target="{Binding ViewportWidth, Mode=OneWayToSource}"/>
<sup:DataPipe Source="{Binding ActualHeight, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}"
Target="{Binding ViewportHeight, Mode=OneWayToSource}"/>
</sup:DataPipeCollection>
</sup:DataPiping.DataPipes>
<Path x:Name="gridLines" Stroke="#FF6083E2" Data="{Binding GridPath, Mode=OneWay}"/>
</Grid>
</DockPanel>
</Grid>
</UserControl>
The GridPath is set in my ViewModel when the ViewportHeight or ViewportWidth property in the ViewModel is changed.
When I set a breakpoint in my ViewModel I find that ViewportWidth is changed once but the ViewportHeight is changed multiple times in small increments. When I comment out the method that calculates the GridPath then the VieportHeight break point hits only once. So it looks like the setting of GridPath triggers a new Layout calculation.
In my actual implementation I also have a Grid docked to the bottom of the control, which is why I have the Dockpanel in the XAML.
Since I am using MVVM I use DataPiping from this Stackoverflow link to get the ActualHeight and ActualWidth in my ViewModel.
Does anybody have an idea what is going on and how I might avoid it? As it is the setting of the GridPath (and I actually have more PathGeometries in my actual implementation) can make resizing my window take more than 8 seconds.
In addition:
Hm, It seems that I don't understand Geometries enough. Here is the code that I use to populate the grid:
private void PopulateGrid()
{
PathGeometry path = new PathGeometry();
Rect rect = new Rect(converter.NormalizePoint(xMin, yMin), converter.NormalizePoint(xMax, yMax));
RectangleGeometry border = new RectangleGeometry(rect);
path.AddGeometry(border);
for (double y = 0; y < yMax; y += 30) {
LineGeometry line = new LineGeometry(converter.NormalizePoint(xMin, y), converter.NormalizePoint(xMax, y));
path.AddGeometry(line);
}
GridPath = path;
}
When I comment out the first 4 lines then the ActualHeight is not set and I see no Grid in my user control. The rectangle causes the Layout engine to work but just the LineGeometry doesn't.
Note that xMin, yMin, xMax and xMin are in world coordinates and the NormalizePoint converts them to screen coordinates, based on the current ViewportWidth and ViewportHeight.