在WrapPanel中动态调整ItemWidth

问题描述

我正在使用WPF MVVM。我有以下代码

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <ListView ItemsSource="{Binding ItemCollection}" Height="160" Width="810">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Width="500" Height ="150" ItemWidth="100" ItemHeight="30"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding Checked}">
                    <TextBlock Text="{Binding Label}"/>
                </CheckBox>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ScrollViewer>

代码显示每个CheckBox的宽度为100。WrapPanel中的每一行由于其大小和CheckBox最多可包含5个ItemWidth es (500/100)。

我有多个CheckBox,它们的宽度不同。

  • 大多数复选框的宽度为
  • 一个复选框的宽度等于280
  • 将来我可能会有一个宽度> 300且的复选框

我不想将ItemWidth显式设置为280,因为大多数项目都较小。相反,我希望每个CheckBox占用100的整数倍,可以显示其所有内容。如果WrapPanel中的当前行没有足够的空间,则将其移至下一行。

对于上面的样本宽度,我希望如此。

  • 宽度
  • 宽度
  • 宽度> 300和

我该如何实现?

解决方法

ItemWidth明确定义了所有项目的宽度。

一个Double,代表WrapPanel中包含的所有项目的统一宽度。默认值为NaN。

不要设置ItemWidth。然后每个项目都占据其各自的大小。

WrapPanel的子元素可能具有明确设置的width属性。 ItemWidth指定WrapPanel为子元素保留的布局分区的大小。结果, ItemWidth优先于元素自身的宽度

现在,如果您没有明确地定义项目的宽度,则它们的大小将适合其内容,但不会与100的倍数对齐。 WrapPanel不支持将项目自动缩放为已定义大小的倍数。

如果要启用这种动态大小调整,则必须创建一个自定义包装面板,也可以编写自定义行为。我为您展示了后者的示例,因为它可重用且更灵活。我使用Microsoft.Xaml.Behaviors.Wpf NuGet程序包,该程序包包含该程序的基类。

public class AlignWidthBehavior : Behavior<FrameworkElement>
{
   public static readonly DependencyProperty AlignmentProperty = DependencyProperty.Register(
      nameof(Alignment),typeof(double),typeof(AlignWidthBehavior),new PropertyMetadata(double.NaN));

   public double Alignment
   {
      get => (double)GetValue(AlignmentProperty);
      set => SetValue(AlignmentProperty,value);
   }

   protected override void OnAttached()
   {
      base.OnAttached();
      AssociatedObject.LayoutUpdated += OnLayoutUpdated;
   }

   protected override void OnDetaching()
   {
      base.OnDetaching();
      AssociatedObject.LayoutUpdated -= OnLayoutUpdated;
   }

   private void OnLayoutUpdated(object sender,EventArgs e)
   {
      var size = AssociatedObject.ActualWidth;
      var alignment = Alignment;
      var isAligned = size % alignment < 10E-12;

      if (!double.IsNaN(alignment) && !isAligned)
         AssociatedObject.Width = Math.Ceiling(size / alignment) * alignment;
   }
}

当项目的布局更改时,将触发此行为。然后,它检查关联项目的宽度是否与Alignment属性给定的值对齐,如果没有,请对其进行调整。

<DataTemplate>
   <CheckBox IsChecked="{Binding Checked}">
      <b:Interaction.Behaviors>
         <local:AlignWidthBehavior Alignment="100"/>
      </b:Interaction.Behaviors>
      <TextBlock Text="{Binding Label}"/>
   </CheckBox>
</DataTemplate>

您必须如上所述将行为附加到XAML中,并将Alignment设置为所需的值,并且不要忘记从ItemWidth中删除WrapPanel属性。