Dot Net For All

Understanding MeasureOverride and ArrangeOverride

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 which can in turn help us to create custom panel in WPF.


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.

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.

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 king 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 below code listing.

Please note that the coordinate system of WPF works ass 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
                {
                    //The x location will always keep increasing, there is not need to take care of it
                    location.X = GetXLocationAfterMiddleChild(childNumber);

                    //If the UIElements are odd in number
                    if (this.InternalChildren.Count % 2 != 0)
                    { 
                        //We need to get the Y location of the child before middle location, to have the same 
                        //Y location for the child after middle child                      
                        int relativeChildBeforeMiddle = middleChild - (childNumber - middleChild);
                        location.Y = GetYLocationAfterMiddleChild(relativeChildBeforeMiddle);
                    }
                    else
                    {
                       ///TODO: Do the design for the even number of children
                    }

                    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>
        <Button BorderBrush="Black" Background="Red" Content="1" Width="40"></Button>

        <Button BorderBrush="Black" Background="Red" Content="2" Width="40"></Button>
        <Button BorderBrush="Black" Background="Red" Content="3" Width="40"></Button>

        <Button BorderBrush="Black" Background="Red" Content="4" Width="40"></Button>

        <Button BorderBrush="Black" Background="Red" Content="5" Width="40"></Button>

        <Button BorderBrush="Black" Background="Red" Content="6" Width="40"></Button>
        <Button BorderBrush="Black" Background="Red" Content="7" Width="40"></Button>

        <Button BorderBrush="Black" Background="Red" Content="8" Width="40"></Button>

    </local:DiagnolPanel>

Conclusion:

If we try to understand the layout process without proper example. it could be bit confusing and 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 thought about the article.

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview