MeasureOverride and ArrangeOverride
To build an effective and appealing UI in WPF, we should be aware of the layout process which takes places while creating the controls and in turn to understand the layout process, we should be aware of the MeasureOverride()
and ArrangeOverride()
methods of the FrameworkElement
class.
CodeProject
MeasureOverride()
and arrangeOverride()
are the two methods which take part in the layout process of the WPF element tree. These methods basically use the two other methods provided by the UIElement
class which are Measure()
and Arrange()
methods. To better understand this whole process of layout in WPF, first we need to know the class hierarchy of the controls in WPF.
The below figure should be able to make lots of thing clear about the class level hierarchy of the elements in WPF.
UIElement
class from the above hierarchy contains two methods which are used in the layout process which I will discuss shortly. This class also contains the RenderTransform
property which is used to do the transformations.
FrameworkElement
class contains all the properties which help us to give the shape to an element. These properties are like Height
, Width
, All types of alignments and Margin. A FrameworkElement
is best suited when you also want to provide custom sizing and positioning of elements, data binding, and styles.
Panel
class contains a UIElementCollection
property which contains all the elements of that class.
The Layout Process
The layout process is executed when the element is rendered for the first time. The layout system in WPF is a conversation between the layout container and its children.
This conversation is basically a two-step process which we will learn further.
- Step 1: Measure
- Step 2: Arrange
Step 1 (Measure): Following are the steps which take place in the measure process.
- Each element calculates the desired size and determines how big it wants to be.
- This is achieved by the element by calling the
Measure()
method on each child and access each child’s desired size property. If the child element has more child elements, in that case the child element also determines all its child’s desired size before returning its own desired size to the parent control.
- The parent can access the desired size from the previous step and calculate its own desired size.
- This process walks down the visual tree and starts at the top of level, that means the topmost parent calls the
measure()
method on each direct child and goes till the end.
- After this step, each element knows how big it wants to be and thus size is stored in the desired size property of each element.
Step 2 (Arrange): Following are the steps which take place in the arrange process.
- Each element arranges its child by calling
Arrange()
method on each direct child.
- The
Arrange()
method takes the final size (which we have calculated in Measure Step) and location, i.e., the element knows the position where it has to render and the size which it has to render.
After these two steps, the rendering occurs and element appears on the screen.
Layout Process Working
We can better understand the layout process when we should ourselves be able to create something which utilizes all the steps mentioned earlier.
And to better demonstrate the working of the MeasureOverride()
and ArrangeOverride()
methods, I have created my own panel which arranges all the controls in a ‘V’ shaped layout as shown in the figure below. Though I am not sure where we should be able to use this kind of panel but to show the demonstration, I have created it.
NOTE: Please note that this panel only works if we have odd number of children(UIElements). I am leaving to reader to implement it for even number of UIElements
To create my own panel which should arrange elements in this particular shape, we should first of all create a custom class derived from Panel
class of WPF framework as shown in the below code listing.
Please note that the coordinate system of WPF works as shown below. To understand the below code, we should know this thing.
Now in the below code listing, you can see that I have implemented the overridden methods MeasureOverride()
and ArrangeOverride()
.
public class DiagnolPanel:Panel
{
protected override Size MeasureOverride(Size availableSize)
{
var mySize = new Size();
foreach (UIElement child in this.InternalChildren)
{
child.Measure(availableSize);
mySize.Width += child.DesiredSize.Width;
mySize.Height += child.DesiredSize.Height;
}
return mySize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var location = new Point();
int childNumber = 0;
int middleChild = GetTheMiddleChild(this.InternalChildren.Count);
foreach (UIElement child in this.InternalChildren)
{
if (childNumber < middleChild)
{
child.Arrange(new Rect(location, child.DesiredSize));
location.X += child.DesiredSize.Width;
location.Y += child.DesiredSize.Height;
}
else
{
location.X = GetXLocationAfterMiddleChild(childNumber);
if (this.InternalChildren.Count % 2 != 0)
{
int relativeChildBeforeMiddle = middleChild - (childNumber - middleChild);
location.Y = GetYLocationAfterMiddleChild(relativeChildBeforeMiddle);
}
else
{
}
child.Arrange(new Rect(location, child.DesiredSize));
}
childNumber++;
}
return finalSize;
}
private double GetXLocationAfterMiddleChild(int childNUmber)
{
double xLocation = 0;
for (int i = 0; i < childNUmber; i++)
{
xLocation += this.InternalChildren[i].DesiredSize.Width;
}
return xLocation;
}
private double GetYLocationAfterMiddleChild(int relativeChildNumber)
{
UIElement correspondingChild = this.InternalChildren[relativeChildNumber - 2];
Point pointCoordinates =
correspondingChild.TransformToAncestor((Visual)this.Parent).Transform(new Point(0, 0));
return pointCoordinates.Y;
}
private int GetTheMiddleChild(int count)
{
int middleChild;
if (count % 2 == 0)
{
middleChild = count / 2;
}
else
{
middleChild = (count / 2) + 1;
}
return middleChild;
}
}
}
And use this panel
class in my XAML to arrange the UIElements
as shown below:
<local:DiagnolPanel>
<Button BorderBrush="Black" Background="Red" Content="0" Width="40"></Button>
Conclusion
If we try to understand the layout process without a proper example, it could be a bit confusing and it can be easily misunderstood.
I hope I have tried to explain the working of the layout process in WPF in a simple and better way. I have attached the sample code here - XAMLLayoutBasic for your reference. Please let me know your thoughts about the article.
The post Understanding MeasureOverride and ArrangeOverride appeared first on Dot Net For All.