Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A WPF StackPanel-Surrogate with Shared-sizing Scope Ability

0.00/5 (No votes)
14 Sep 2014 2  
Here is a simple trick for simulating the shared-sizing feature of the WPF Grid even in a StackPanel fashion.

Introduction

Basically, you can have several panels, each one in a separate visual fragment, and “synchronize” their children height (or width, when horizontally-oriented).
A short video explains better than thousands of words.

Background

This article explains pretty well the feature available for the Grid control. The goal is having a StackPanel-like layout control, which offers similar "synchronization" features.

Using the Code

The solution is pretty simple. Since the Grid already offers such a feature, the trick is leveraging it instead a “real” StackPanel. Otherwise, the mechanism for managing the shared-size scopes is rather complex. As for “complex”, I mean that you should keep all the scrolling and virtualization features which is part of a StackPanel, and that’s rather complex.

The resulting StackPanel-surrogate code is very simple:

    /// <summary>
    /// Represent a StackPanel surrogate whose children width/height can be
    /// shared with other homogeneous panel's children
    /// </summary>
    public class StackPanel3S
        : Grid
    {
        /// <summary>
        /// Gets or sets a value that identifies the panel as a member 
        /// of a defined group that shares sizing properties.
        /// </summary>
        public string SharedSizeGroup { get; set; }

        #region DP Orientation

        /// <summary>
        /// Identifies the StackPanelEx.Orientation dependency property.
        /// </summary>
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
            "Orientation",
            typeof(Orientation),
            typeof(StackPanel3S),
            new FrameworkPropertyMetadata(
                Orientation.Vertical,
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                (obj, args) =>
                {
                    var ctl = (StackPanel3S)obj;
                    ctl.OrientationChanged(args);
                }));

        /// <summary>
        /// Gets or sets a value that indicates the dimension by which child elements are stacked.
        /// </summary>
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        #endregion

        private void OrientationChanged(
            DependencyPropertyChangedEventArgs args
            )
        {
            //flush any current row/column definition
            this.RowDefinitions.Clear();
            this.ColumnDefinitions.Clear();
        }

        protected override Size MeasureOverride(Size constraint)
        {
            //retrieve the number of children
            int count = this.InternalChildren.Count;

            if (this.Orientation == System.Windows.Controls.Orientation.Vertical)
            {
                //add the missing row-defintions
                for (int i = this.RowDefinitions.Count; i < count; i++)
                {
                    this.RowDefinitions.Add(
                        new RowDefinition()
                        {
                            Height = GridLength.Auto,
                            SharedSizeGroup = this.SharedSizeGroup + "__R" + i
                        });
                }

                //remove the unnecessary row-definitions
                for (int i = this.RowDefinitions.Count - 1; i >= count; i--)
                {
                    this.RowDefinitions.RemoveAt(i);
                }

                //passing a progressive index to each child
                for (int i = 0; i < count; i++)
                {
                    UIElement child;
                    if ((child = this.InternalChildren[i]) != null)
                    {
                        Grid.SetRow(child, i);
                    }
                }
            }
            else
            {
                //add the missing column-defintions
                for (int i = this.ColumnDefinitions.Count; i < count; i++)
                {
                    this.ColumnDefinitions.Add(
                        new ColumnDefinition()
                        {
                            Width = GridLength.Auto,
                            SharedSizeGroup = this.SharedSizeGroup + "__C" + i
                        });
                }

                //remove the unnecessary column-definitions
                for (int i = this.ColumnDefinitions.Count - 1; i >= count; i--)
                {
                    this.ColumnDefinitions.RemoveAt(i);
                }

                //passing a progressive index to each child
                for (int i = 0; i < count; i++)
                {
                    UIElement child;
                    if ((child = this.InternalChildren[i]) != null)
                    {
                        Grid.SetColumn(child, i);
                    }
                }
            }

            //yield the default measuring pass
            return base.MeasureOverride(constraint);
        }
    }

In order to test (and demonstrate) the functionality, the code comes with a small application, which is the one seen in the video.

The app shows three ways to use the StackPanel3S, each one targets a different way to host several children elements:

  • Direct declaration from within the XAML document (leftmost column)
  • Direct declaration from the underlying C# code (center column)
  • Indirect creation via MVVM (rightmost column)

Above each column, there are three sliders for rotating the shapes hosted as "children elements". The rotation angle leads a rectangle to take more or less space (height, in this case), thus the stacked elements are arranged differently.

The left-top checkbox allows the user to enable/disable the size sharing. When the function is disabled, each column arranges their children independently. However, when is enabled, the actual height of every "row" is dependent to all set.

History

  • Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here