如何在WPF中根据窗口大小调整特定控件的大小?

3

我有一个 ListView 控件,我想要调整最后一列的大小以与 Window 的大小同步。因此,如果 Window 的宽度增加了 100 个单位,我希望列的宽度也增加 100 个单位。

我应该在 Window 上使用 Resize 事件,并手动调整列标头的大小吗?类似于:

columnHeader.Width = windowSize.X - 400;
2个回答

7
这里有一个解决方案,它使用数据绑定和一些转换器技巧来完成工作。首先,让我们描述一个简单的带有一些数据和3列的ListView。
<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Width="140" Header="Date" />
            <GridViewColumn Width="140" Header="Day" 
                            DisplayMemberBinding="{Binding DayOfWeek}" />
            <GridViewColumn Width="140" Header="Year" 
                            DisplayMemberBinding="{Binding Year}"/>
        </GridView>
    </ListView.View>

    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
</ListView>

这将带我们到你所在的地方。现在,为了让最后一列根据父元素的宽度增长和缩小,我们需要构建一个转换器并连接它。首先,让我们调整GridView的最后一列,使其宽度动态变化。
<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Year}">
    <GridViewColumn.Width>
        <MultiBinding Converter="{StaticResource lastColumnMaximizerConverter}">
            <Binding Path="ActualWidth" 
                     RelativeSource="{RelativeSource AncestorType=ListView}"/>
            <Binding Path="View.Columns" 
                     RelativeSource="{RelativeSource AncestorType=ListView}"/>
        </MultiBinding>
    </GridViewColumn.Width>
</GridViewColumn>

我们所做的是创建了一个MultiBinding对象,连接了一个IMultiValueConverter,并描述了我们想要发送到IMultiValueConverter实现中的几个参数。第一个参数是父ListView的ActualWidth。第二个参数是父ListView上的View.Columns集合。我们现在拥有计算视图中最后一列的最终宽度所需的一切。
现在我们需要创建一个IMultiValueConverter实现。我碰巧有一个在这里。
public class WidthCalculationMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, 
                          object parameter, CultureInfo culture)
    {
        // do some sort of calculation
        double totalWindowWidth;
        double otherColumnsTotalWidth = 0;
        double.TryParse(values[0].ToString(), out totalWindowWidth);
        var arrayOfColumns = values[1] as IList<GridViewColumn>;

        for (int i = 0; i < arrayOfColumns.Count - 1; i++)
        {
            otherColumnsTotalWidth += arrayOfColumns[i].Width;
        }

        return (totalWindowWidth - otherColumnsTotalWidth) < 0 ? 
                     0 : (totalWindowWidth - otherColumnsTotalWidth);
    }

    public object[] ConvertBack(object value, Type[] targetTypes,
                                object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

嘘...顺便说一下,这并不是最安全的代码。你需要改进它!这只是一个演示,你知道演示代码的性质! :)

最后,我们需要在XAML中实例化我们的转换器实例。

<Grid.Resources>
    <Converters:WidthCalculationMultiConverter 
                  x:Key="lastColumnMaximizerConverter"/>
</Grid.Resources>

现在我们有一个转换器,它会根据ListView中的列宽(不包括最后一列)来确定最后一列的宽度,并且使用绑定将所需参数发送到该转换器中进行处理,得到正确答案并应用到最后一列的宽度上。
如果您已经正确地组合了它们,那么现在您应该拥有一个ListView,其中最后一列将始终拉伸到父ListView的最大宽度。
希望这能让您开始并且帮助您理解如何在不编写任何代码的情况下使用WPF提供的更多功能。

顺便问一下,当以编程方式设置列宽时,是否会像用户手动调整大小一样,相应的单元格也会被调整大小? - Joan Venge
我不确定我是否理解你的问题,但如果你调整列的大小,那么该列中的所有单元格都会随着列宽度的变化而增长。你不需要同时调整所有单元格的大小。 - Dave White
谢谢,这正是我想问的。我之所以问这个问题,是因为当你隐藏列标题时,它并没有隐藏单元格,所以我认为也许缩放只适用于列标题。 - Joan Venge

2
我不能完全为这个答案负责,因为我在这里得到了它。
基本上这是思路:
用GridView替换listView中的“View”。然后,您可以为第一列指定任何宽度。在这种情况下是100、50、50。 然后我们在最后一列上添加一个绑定,该绑定以父ListView控件作为输入。但我们允许一个转换器来替我们完成脏工作。
<ListView>
    <ListView.View>
      <GridView>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="100"/>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="50"/>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="50"/>
        <GridViewColumn Header="4" DisplayMemberBinding="{Binding}">
        <GridViewColumn.Width>
          <MultiBinding Converter="{StaticResource starWidthConverter}">
            <Binding Path="ActualWidth"  RelativeSource="{RelativeSource AncestorType=ListView}"/>
            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListView}"/>
          </MultiBinding>
        </GridViewColumn.Width>
      </GridViewColumn>
     </GridView>
    </ListView.View>
    <ListViewItem>item1</ListViewItem>
    <ListViewItem>item2</ListViewItem>
    <ListViewItem>item3</ListViewItem>
    <ListViewItem>item4</ListViewItem>
  </ListView>

因此,在转换器的“转换”函数中,我们这样做:
ListView listview = value[1] as ListView;
double width = listview.ActualWidth;
GridView gv = listview.View as GridView;
for(int i = 0;i < gv.Columns.Count-1;i++)
{
  if(!Double.IsNaN(gv.Columns[i].Width))
    width -= gv.Columns[i].Width;
}
return width - 5;// this is to take care of margin/padding

获取listview的宽度,并计算新的大小。


顺便问一下,当以编程方式设置列宽时,是否会像用户手动调整大小一样,相应的单元格也会被调整大小? - Joan Venge
好的,我添加了这个,但它似乎没有任何作用,宽度似乎被设置为自动,但我已经添加了转换器和绑定,视图已经像在戴夫的帖子中一样设置为GridViewColumn。 - Joan Venge
抱歉,我错误地假设我提供的链接中的代码实际上是可用的。现在它应该可以工作了。 - Liz
谢谢Liz,我会尝试一下并向您确认的 :O - Joan Venge
@Liz - 很好的观点。我的答案总是关注实际宽度,但当我看到你的第一个只有listView参数的答案时,我想“哦..更简单..有趣...”但现在你改变了它,这很有道理。如果只有listview作为参数,它是行不通的。 - Dave White
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接