Пользовательский UniformGrid, вызывающий проблему масштабирования в ItemsControl
У меня проблемы с пользовательским элементом управления UniformGrid, который не масштабирует все его дочерние элементы при запуске приложения - мне нужно немного изменить размер окна, чтобы содержимое правильно масштабировалось.
UniformGrid используется в качестве ItemsPanel в ItemsControl. ItemsControl ItemsSource привязан к коллекции viewmodel, а UserControls выбираются на основе типа viewmodel. Вот фрагмент:
<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>
Моя проблема в том, что некоторые части UserControls не масштабируются при запуске приложения. Примером является пользовательский элемент управления RovIllustrationIndicator. Он содержит Viewbox с сеткой, содержащей элемент управления Image и элемент управления ItemsControl (я удалил несвязанные вещи):
<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>
При запуске приложения высота и ширина окна просмотра равны нулю. Но если я изменю размер окна только на один пиксель, окно просмотра масштабируется до правильного размера.
Дело в том, что, если я изменю верхнюю ItemsControl ItemsPanel на стандартную UniformGrid, а не на свой собственный WeightedUniformGrid, он отлично масштабируется при запуске. Поэтому что-то с WeightedUniformGrid в сочетании с UserControls вызывает сбой масштабирования при запуске. Но что?
Вот реализация WeightedUniformGrid:
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;
}
}