如何在WPF控件模板中为ListView列应用MinWidth?

7

在一个类似问题的回答这里后,我能够在XAML页面上设置MinWidth。

我想要做的是在所有ListView中的所有GridViewColumn的控件模板中实现这一点。

这个可能吗?

更新:

我尝试了下面简单的示例代码,但它不起作用:

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style TargetType="{x:Type GridViewColumnHeader}" >
            <Setter Property="MinWidth" Value="200" />
        </Style>
    </Window.Resources>

    <Grid Width="500">
        <Border BorderBrush="Black" BorderThickness="2" Margin="20">
            <ListView SelectionMode="Single">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Header 1" Width="Auto">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="Hello There"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Header 2" Width="Auto" />
                    </GridView>
                </ListView.View>
            </ListView>
        </Border>
    </Grid>
</Window>

GridViewColumn甚至没有MinWidth属性。 无法在样式或控件模板中设置不存在的属性。 请发布您的XAML,说明如何在GridViewColumn上设置MinWidth。 - paparazzo
在链接中,展示了使用Thumb和处理DragDelta事件来完成MinWidth的方法。是否有一种在控件模板中实现这个功能的方式? - Elan
7个回答

10

如果您使用GridViewColumnHeader,您可以处理大小更改:

  <GridView>
     <GridViewColumn>
        <GridViewColumnHeader Content="HeaderContent" SizeChanged="HandleColumnHeaderSizeChanged"/> 
   ...

在代码中:

    private void HandleColumnHeaderSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
    {
        if (sizeChangedEventArgs.NewSize.Width <= 60) {
            sizeChangedEventArgs.Handled = true;
            ((GridViewColumnHeader) sender).Column.Width = 60;
        }
    }

这正是我所需要的!非常感谢。 - Ben Hayward
谢谢。最终我创建了一个附加属性,这样我就可以使用 gv:MinWdth="75"。但愿这个功能可以直接使用。 - Billy Jake O'Connor

4
 <ListView>
        <ListView.View>
            <GridView>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock MinWidth="100"/>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                 ...more columns...
            </GridView>
        </ListView.View>
    </ListView>

1
我实际上正在寻找这样的解决方案。当您需要将其应用于特定列时,它非常有效。我只需要在文本块上添加绑定即可。 - Apostrofix

4
<Window.Resources>
    <Style TargetType="{x:Type GridViewColumnHeader}" >
        <Setter Property="MinWidth" Value="400" />
    </Style>
</Window.Resources>

阅读 - GridViewColumnHeader - 而不是 GridViewColumn。我测试过了 - 它可以工作。 - paparazzo
1
我发布了一个示例,但对我来说无法工作。您能否看看是否有其他可能是我没有的东西? - Elan
请尝试将width = auto从各个列中去掉。我确实测试过这个方法,在我的GridView中它起作用了。 - paparazzo
3
你说的400宽度我认同,是我的疏忽。但是,无论如何,它仍然对我不起作用。你不需要对此变得紧张。如果代码也可以为我所用,我会接受这个答案。不幸的是,代码示例在我这里无法工作,我仍在寻求解决方法。 - Elan
我完全同意@Elan的观点 - 列在使用minWidth正确初始化后,但在调整大小时变得完全混乱。这不是解决方案。 - Ola Berntsson
显示剩余4条评论

4

我也遇到了这个问题。为了解决它,我必须做两件事:

  1. 修改ListView的标题的ControlTemplate。
  2. 处理ControlTemplate中Thumb的DragDelta事件。

ListView的标题是GridViewColumnHeader。 下面展示了GridViewColumnHeader的ControlTemplate的简化版本。正如我们所看到的,它使用Canvas中的Thumb来创建拖动/调整大小的效果。

PS:要获取完整的GridViewColumnHeader ControlTemplate,请参阅How to grab WPF 4.0 control default templates?

<ControlTemplate TargetType="GridViewColumnHeader">
<Grid SnapsToDevicePixels="True">
    <Border BorderThickness="0,1,0,1" Name="HeaderBorder" ...>
    <!-- omitted -->
    </Border>
    <Border BorderThickness="1,0,1,1" Name="HeaderHoverBorder" Margin="1,1,0,0" />
    <Border BorderThickness="1,1,1,0" Name="HeaderPressBorder" Margin="1,0,0,1" />
    <Canvas>
        <Thumb Name="PART_HeaderGripper">
        <!-- omitted -->
        </Thumb>
    </Canvas>
</Grid>
<ControlTemplate.Triggers>
<!-- omitted -->
</ControlTemplate.Triggers>

为了限制GridViewColumnHeader的大小,我们需要钩取Thumb的拖动事件(DragStarted、DragDelta、DragCompleted等)。
结果证明,只需要DragDelta事件,只要我们可以在DragDeltaEventHandler中知道MinSize即可。
下面是带注释的修改后的XAML。
<Grid Width="500">
    <Border BorderBrush="Black" BorderThickness="2" Margin="20">
        <ListView SelectionMode="Single">
            <ListView.View>
                <GridView>                        
                    <GridViewColumn Header="Header 1" Width="Auto">
                        <!-- Apply a style targeting GridViewColumnHeader with MinWidth = 80 and a ControlTemplate -->
                        <GridViewColumn.HeaderContainerStyle>
                            <Style TargetType="{x:Type GridViewColumnHeader}">
                                <Setter Property="MinWidth" Value="80" />
                                <Setter Property="Control.Template" Value="{DynamicResource myGridViewColumnHeaderControlTemplate}" />
                            </Style>
                        </GridViewColumn.HeaderContainerStyle>**
                    </GridViewColumn>
                    <GridViewColumn Header="Header 2" Width="Auto" />
                </GridView>
            </ListView.View>
        </ListView>
    </Border>
</Grid>

在myGridViewColumnHeaderControlTemplate中添加一些XAML来:
  1. 将GridViewColumnHeader的MinWidth绑定到Canvas的MinWidth。
  2. 连接Thumb的DragDelta事件。
<ControlTemplate x:Key="TemplateGridViewColumnHeader" TargetType="GridViewColumnHeader">
    <!-- omitted -->
    <Canvas MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinWidth, Mode=OneTime}">
        <Thumb x:Name="PART_HeaderGripper" DragDelta="myGridViewColumnHeader_DragDelta">
最后是myGridViewColumnHeader_DragDelta函数:
    private void myGridViewColumnHeader_DragDelta(object sender, DragDeltaEventArgs e)
    {
        DependencyObject parent = sender as DependencyObject;

        try
        {
            do
            {
                parent = VisualTreeHelper.GetParent(parent as DependencyObject);
            } while (parent.GetType() != typeof(Canvas));

            Canvas canvas = parent as Canvas;
            if (canvas.ActualWidth + e.HorizontalChange < canvas.MinWidth)
            {
                e.Handled = true;
            }
        }
        catch
        {
        }
    }

这是我发现的唯一可行的方法。希望有更简单的方法。

1
在我看来,这是一种过度设计。 - Billy Jake O'Connor

3
更新对Billy Jake O'Connor的解决方案,他给出了最简单、易于实现和正确工作的解决方案。 对于不希望所有列共享相同最小宽度的人来说,在下一个代码更新中,您可以直接在列属性中指定每个列的特定最小宽度,从而为每个列设置不同的最小宽度。
public static class GridColumn {
    public static readonly DependencyProperty MinWidthProperty =
        DependencyProperty.RegisterAttached("MinWidth", typeof(double), typeof(GridColumn), new PropertyMetadata(75d, (s, e) => {
            if(s is GridViewColumn gridColumn ) {
                SetMinWidth(gridColumn);
                ((System.ComponentModel.INotifyPropertyChanged)gridColumn).PropertyChanged += (cs, ce) => {
                    if(ce.PropertyName == nameof(GridViewColumn.ActualWidth)) {
                        SetMinWidth(gridColumn);
                    }
                };
            }
        }));

    private static void SetMinWidth(GridViewColumn column) {
        double minWidth = (double)column.GetValue(MinWidthProperty);

        if(column.Width < minWidth)
            column.Width = minWidth;
    }

    public static double GetMinWidth(DependencyObject obj) => (double)obj.GetValue(MinWidthProperty);

    public static void SetMinWidth(DependencyObject obj, double value) => obj.SetValue(MinWidthProperty, value);
}

以下是可能的 XAML 示例("local" 是您的命名空间名称,请根据实际情况进行修改):

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn local:GridColumn.MinWidth="25" />
            <GridViewColumn local:GridColumn.MinWidth="100" />
            <GridViewColumn local:GridColumn.MinWidth="200" />
        </GridView>
    </ListView.View>
</ListView>

2
我想要对所有列应用最小宽度,所以我写了这个代码:

  public static class GridViewConstraints
  {
    public static readonly DependencyProperty MinColumnWidthProperty =
        DependencyProperty.RegisterAttached("MinColumnWidth", typeof(double), typeof(GridViewConstraints), new PropertyMetadata(75d, (s,e) =>
        {
            if(s is ListView listView)
            {
                listView.Loaded += (lvs, lve) =>
                {
                    if(listView.View is GridView view)
                    {
                        foreach (var column in view.Columns)
                        {
                            SetMinWidth(listView, column);

                            ((System.ComponentModel.INotifyPropertyChanged)column).PropertyChanged += (cs, ce) =>
                            {
                                if (ce.PropertyName == nameof(GridViewColumn.ActualWidth))
                                    SetMinWidth(listView, column);
                            };
                        }
                    }
                };
            }
        }));

    private static void SetMinWidth(ListView listView, GridViewColumn column)
    {
        double minWidth = (double)listView.GetValue(MinColumnWidthProperty);

        if (column.Width < minWidth)
            column.Width = minWidth;
    }

    public static double GetMinColumnWidth(DependencyObject obj) => (double)obj.GetValue(MinColumnWidthProperty);

    public static void SetMinColumnWidth(DependencyObject obj, double value) => obj.SetValue(MinColumnWidthProperty, value);
}

只需将其拖放到您的列表视图中:

<ListView b:GridViewConstraints.MinColumnWidth="255" />

如果您希望将此设置为默认值,您可以创建一个ListView样式,在setter中设置该值。希望这可以帮到您。 - Billy Jake O'Connor
1
我尝试了其他答案,包括样式设置器,但都没有正常工作。这个解决方案完美地解决了问题,非常棒。 - Ola Berntsson

0

你可以尝试这个方法,对于每一列,如果你想为所有列设置不同的最小宽度和最大宽度自动

 <ListView.View>
    <GridView >
        <GridViewColumn Header="FILE NAME" DisplayMemberBinding="{Binding fileName}" Width="auto" >
            <GridViewColumn.HeaderContainerStyle>
                <Style TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="MinWidth" Value="200" />

                </Style>
            </GridViewColumn.HeaderContainerStyle>
        </GridViewColumn>
                <GridViewColumn Header="ERROR DETAILS" DisplayMemberBinding="{Binding errorMessage}" Width="auto">
            <GridViewColumn.HeaderContainerStyle>
                <Style TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="MinWidth" Value="396" />

                </Style>
            </GridViewColumn.HeaderContainerStyle>
        </GridViewColumn>

    </GridView>
</ListView.View>

它不起作用,内容溢出并且不可见。 - Orace

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