I'm having problems with a custom UniformGrid control that's not scaling all its children at application startup - I have to resize the window just a bit for the contents to be scaled correctly.
The UniformGrid is used as ItemsPanel in an ItemsControl. The ItemsControl ItemsSource is bound to a viewmodel collection, and UserControls are selected based on viewmodel type. Here's a snippet:
<DataTemplate DataType="{x:Type indicators:RollPitchIndicatorVM}">
<indicators:RollPitchIndicator />
</DataTemplate>
<DataTemplate DataType="{x:Type indicators:RovIllustrationIndicatorVM}">
<indicators:RovIllustrationIndicator />
</DataTemplate>
<DataTemplate DataType="{x:Type indicators:RulerIndicatorVM}">
<indicators:RulerIndicator />
</DataTemplate>
My problem is that some parts of the UserControls aren't scaled at application startup. An example is the RovIllustrationIndicator UserControl. It contains a Viewbox with a Grid that's containing an Image control and an ItemsControl control (I've stripped away unrelated stuff):
<Grid HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Viewbox x:Name="RovImage" Grid.Column="1">
<Grid>
<Image x:Name="BackgroundImage" Source="{Binding Path=BackgroundImage}" Stretch="None" />
<!-- ItemsControl displaying images and polygons overlaying the Image control -->
</Grid>
</Viewbox>
</Grid>
At application startup, the Viewbox height and width are both zero. But if I resize the window just a single pixel, the Viewbox is scaled to correct size.
The thing is, if I change top ItemsControl ItemsPanel to standard UniformGrid rather than my custom WeightedUniformGrid, it scales fine at startup. So something with the WeightedUniformGrid in combination with the UserControls is causing scaling to fail at startup. But what?
Here's the WeightedUniformGrid implementation:
public class WeightedUniformGrid : UniformGrid
{
public static readonly DependencyProperty WeightProperty =
DependencyProperty.RegisterAttached(
"Weight",
typeof(double),
typeof(WeightedUniformGrid),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.AffectsParentArrange |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public static double GetWeight(UIElement element)
{
return (double)element.GetValue(WeightProperty);
}
public static void SetWeight(UIElement element, double value)
{
element.SetValue(WeightProperty, value);
}
public static readonly DependencyProperty SortDirectionProperty =
DependencyProperty.RegisterAttached(
nameof(SortDirection),
typeof(Orientation),
typeof(WeightedUniformGrid),
new FrameworkPropertyMetadata(
Orientation.Horizontal,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsMeasure));
public Orientation SortDirection
{
get { return (Orientation)GetValue(SortDirectionProperty); }
set { SetValue(SortDirectionProperty, value); }
}
protected override Size MeasureOverride(Size constraint)
{
var size = base.MeasureOverride(constraint);
if (Columns > 0)
{
double elementsPerColumn = Math.Ceiling((double)Children.Count / Columns);
double elementWidth = size.Width / Columns;
double unweightedElementHeight = size.Height / elementsPerColumn;
for (int i = 0; i < Children.Count; ++i)
{
var child = Children[i];
int columnNumber = (int)Math.Floor(i / elementsPerColumn);
double weight = GetWeight(child);
if (double.IsNaN(weight)) { weight = 100; }
double weightedElementHeight = unweightedElementHeight * weight / 100;
child.Measure(new Size(elementWidth, weightedElementHeight));
}
}
else if (Rows > 0)
{
double elementsPerRow = Math.Ceiling((double)Children.Count / Rows);
double elementHeight = size.Height / Rows;
double unweightedElementWidth = size.Width / elementsPerRow;
for (int i = 0; i < Children.Count; ++i)
{
var child = Children[i];
int rowNumber = (int)Math.Floor(i / elementsPerRow);
double weight = GetWeight(child);
if (double.IsNaN(weight)) { weight = 100; }
double weightedElementWidth = unweightedElementWidth * weight / 100;
child.Measure(new Size(weightedElementWidth, elementHeight));
}
}
return size;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var size = base.ArrangeOverride(arrangeSize);
if (Columns > 0)
{
int elementsPerColumn = (int)Math.Ceiling((double)Children.Count / Columns);
double elementWidth = size.Width / Columns;
double unweightedElementHeight = size.Height / elementsPerColumn;
double[] accumulatedHeightPerColumn = new double[Columns];
for (int i = 0; i < Children.Count; ++i)
{
var child = Children[i];
int columnNumber;
if (SortDirection == Orientation.Vertical) // Vertical orientation
{
columnNumber = i / elementsPerColumn;
}
else // Horizontal orientation
{
columnNumber = i % Columns;
}
double weight = GetWeight(child);
if (double.IsNaN(weight)) { weight = 100; }
double weightedElementHeight = unweightedElementHeight * weight / 100;
child.Arrange(new Rect(new Point(columnNumber * elementWidth, accumulatedHeightPerColumn[columnNumber]),
new Point((columnNumber + 1) * elementWidth, accumulatedHeightPerColumn[columnNumber] + weightedElementHeight)));
accumulatedHeightPerColumn[columnNumber] += weightedElementHeight;
}
}
else if (Rows > 0)
{
int elementsPerRow = (int)Math.Ceiling((double)Children.Count / Rows);
double elementHeight = size.Height / Rows;
double unweightedElementWidth = size.Width / elementsPerRow;
double[] accumulatedWidthPerRow = new double[Rows];
for (int i = 0; i < Children.Count; ++i)
{
var child = Children[i];
int rowNumber;
if (SortDirection == Orientation.Vertical) // Vertical orientation
{
rowNumber = i % Rows;
}
else // Horizontal orientation
{
rowNumber = i / elementsPerRow;
}
double weight = GetWeight(child);
if (double.IsNaN(weight)) { weight = 100; }
double weightedElementWidth = unweightedElementWidth * weight / 100;
child.Arrange(new Rect(new Point(accumulatedWidthPerRow[rowNumber], rowNumber * elementHeight),
new Point(accumulatedWidthPerRow[rowNumber] + weightedElementWidth, (rowNumber + 1) * elementHeight)));
accumulatedWidthPerRow[rowNumber] += weightedElementWidth;
}
}
return size;
}
}
