我制作了一个自定义的WeightedUniformGrid类,该类在ItemsControl中用作ItemsPanel。每个网格元素都使用ItemsSource集合中对象中的Weight属性进行加权。
但是当我在视图模型中更改Weight属性时,它不会立即显示在View中。我必须更改用新值绘制的WeightedUniformGrid的窗口大小。
如何获取ItemsSource集合中的属性更改以使ItemsControl重绘?我在想也许在WeightedUniformGrid中添加一个DependencyProperty,它将同时影响AffectsArrange和AffectsMeasure。但是我不确定我可以绑定到什么。
我已经搜索了很长时间,并且有些问题很相似(例如this)。但是我一直无法适应他们的需求。
MainWindow.xaml:
<ItemsControl ItemsSource="{Binding Path=ElementGroupCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:WeightedUniformGrid Rows="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1"
RenderOptions.EdgeMode="Aliased">
<Viewbox Stretch="Uniform">
<TextBlock Text="{Binding Path=Number}" Foreground="Black" FontSize="20"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Viewbox>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
MainWindowVM.cs:
public class MainWindowVM
{
public MainWindowVM()
{
_elementGroupCollection = new ObservableCollection<ElementGroup>()
{
new ElementGroup(1, 60),
new ElementGroup(2, 150),
new ElementGroup(3, 90),
new ElementGroup(4, 80),
new ElementGroup(5, 60),
new ElementGroup(6, 160)
};
}
private ObservableCollection<ElementGroup> _elementGroupCollection;
public ObservableCollection<ElementGroup> ElementGroupCollection
{
get { return _elementGroupCollection; }
}
}
ElementGroup.cs:
public class ElementGroup : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ElementGroup(int number, double? weight)
{
Number = number;
Weight = (weight >= 0) ? weight : null;
}
public int Number { get; }
private double? _weight;
public double? Weight
{
get { return _weight; }
set { SetNotify(ref _weight, value); }
}
public void SetNotify<T>(ref T storage,
T value,
[CallerMemberName] string propertyName = null)
{
storage = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
WeightedUniformGrid.cs:
public class WeightedUniformGrid : UniformGrid
{
protected override Size MeasureOverride(Size constraint)
{
var size = base.MeasureOverride(constraint);
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 = (FrameworkElement)Children[i];
var dc = child.DataContext;
int rowNumber = (int)Math.Floor(i / elementsPerRow);
double? weight = dc.GetType().GetProperty("Weight")?.GetValue(dc, null) as double?;
if (weight == null) { weight = 100; }
double weightedElementWidth = unweightedElementWidth * (double)weight / 100;
child.Measure(new Size(weightedElementWidth, elementHeight));
}
return size;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var size = base.ArrangeOverride(arrangeSize);
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 = (FrameworkElement)Children[i];
var dc = child.DataContext;
int rowNumber = i / elementsPerRow;
double? weight = dc.GetType().GetProperty("Weight")?.GetValue(dc, null) as double?;
if (weight == null) { weight = 100; }
double weightedElementWidth = unweightedElementWidth * (double)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;
}
}
答案 0 :(得分:3)
您的WeightedUniformGrid不得直接访问其子元素的DataContext中的Weight属性。除此之外,这是一种不好的做法,因为当视图模型项的权重发生变化时,没有机制可以强制布局通过。
应该有一个绑定到Weight的附加属性。附加属性的FrameworkPropertyMetadataOptions
将强制通过布局。
public class WeightedUniformGrid : UniformGrid
{
public static readonly DependencyProperty WeightProperty =
DependencyProperty.RegisterAttached(
"Weight", typeof(double), typeof(WeightedUniformGrid),
new FrameworkPropertyMetadata(double.NaN,
FrameworkPropertyMetadataOptions.AffectsParentMeasure |
FrameworkPropertyMetadataOptions.AffectsParentArrange));
public static double GetWeight(UIElement element)
{
return (double)element.GetValue(WeightProperty);
}
public static void SetWeight(UIElement element, double value)
{
element.SetValue(WeightProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
var size = base.MeasureOverride(constraint);
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);
// get attached property value
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);
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 = i / elementsPerRow;
// get attached property value
double weight = GetWeight(child);
if (double.IsNaN(weight)) { weight = 100; }
double weightedElementWidth = unweightedElementWidth * (double)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;
}
}
以ItemContainerStyle绑定附加属性:
<ItemsControl ItemsSource="{Binding Path=ElementGroupCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:WeightedUniformGrid Rows="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="local:WeightedUniformGrid.Weight" Value="{Binding Weight}"/>
</Style>
</ItemsControl.ItemContainerStyle>
...
</ItemsControl>