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:
public class StackPanel3S
: Grid
{
public string SharedSizeGroup { get; set; }
#region DP Orientation
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);
}));
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
#endregion
private void OrientationChanged(
DependencyPropertyChangedEventArgs args
)
{
this.RowDefinitions.Clear();
this.ColumnDefinitions.Clear();
}
protected override Size MeasureOverride(Size constraint)
{
int count = this.InternalChildren.Count;
if (this.Orientation == System.Windows.Controls.Orientation.Vertical)
{
for (int i = this.RowDefinitions.Count; i < count; i++)
{
this.RowDefinitions.Add(
new RowDefinition()
{
Height = GridLength.Auto,
SharedSizeGroup = this.SharedSizeGroup + "__R" + i
});
}
for (int i = this.RowDefinitions.Count - 1; i >= count; i--)
{
this.RowDefinitions.RemoveAt(i);
}
for (int i = 0; i < count; i++)
{
UIElement child;
if ((child = this.InternalChildren[i]) != null)
{
Grid.SetRow(child, i);
}
}
}
else
{
for (int i = this.ColumnDefinitions.Count; i < count; i++)
{
this.ColumnDefinitions.Add(
new ColumnDefinition()
{
Width = GridLength.Auto,
SharedSizeGroup = this.SharedSizeGroup + "__C" + i
});
}
for (int i = this.ColumnDefinitions.Count - 1; i >= count; i--)
{
this.ColumnDefinitions.RemoveAt(i);
}
for (int i = 0; i < count; i++)
{
UIElement child;
if ((child = this.InternalChildren[i]) != null)
{
Grid.SetColumn(child, i);
}
}
}
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