如何使WPF数据网格中的最后一列总是占用所有剩余空间?

ttp71kqs  于 2023-06-30  发布在  其他
关注(0)|答案(8)|浏览(187)

标准WPF 4数据网格。
假设我有一个200像素宽的数据网格,有两列。我想列总是整个空间,这意味着如果用户调整第一列的大小为50像素,最后一个将是150。
最初我为第一列设置宽度100像素,为最后一列设置 *(在XAML中)。
我认为问题在于删除虚拟的第三列,如下所述:
http://wpf.codeplex.com/Thread/View.aspx?ThreadId=58939
但实际上没有什么区别--尽管如此,在调整列的大小时,我在右边得到了一些额外的空间--有了虚拟列,它是一个虚拟列(默认为白色),没有它,它是空的空间(默认为灰色)。

问题:如何强制约束,即无论用户如何调整列的大小,

sum(columns width)==datagrid width

编辑

是的,我使用WPF 4。

变通办法

我将其中一个答案标记为解决方案,但实际上由于WPF设计,它不是解决方案。这就是WPF所能做的最好的事情,它不是很好-首先,列的CanUserResize选项意味着IsResizeable,并且该选项在打开时与Width设置为 * 相矛盾。所以如果没有一些真正聪明的技巧,你最终会得到:

  • 最后一列表面上可调整大小但实际上不可调整大小的数据网格,并且示出了右侧的小空间(即,虚拟列不可调整大小)--对于最后一列:CanUserResize=true,Width=*
  • datagrid的最后一列不能由用户调整大小,它相应地显示,最初右边没有空间显示,但当用户调整datagrid的任何元素的大小时,它可以显示--对于最后一列:CanUserResize=false,Width=*

到目前为止,我可以看到WPF datagrid的两个问题:

  • 误导性命名
  • 特征矛盾

我仍然全神贯注于如何真正解决这个问题。

2j4z5cfb

2j4z5cfb1#

将数据网格的宽度设置为“自动”。您允许列在网格本身内正确调整大小,但将宽度硬连接为200。

    • 更新**:基于@micas的评论,我可能误读了。如果是这种情况,请尝试使用100作为左列的宽度,使用100 * 作为右列的宽度(注意星号)。这将默认右列的宽度为100,但允许调整其大小以填充网格。
ie3xauqp

ie3xauqp2#

可以设置列宽以在代码上加星星。在构造函数中,添加:

Loaded += (s, e) => dataGrid1.Columns[3].Width =
new DataGridLength(1, DataGridLengthUnitType.Star);
qltillow

qltillow3#

我只是将其实现为一个附加行为。问题是,当您将DataGrid的最后一列设置为 * 时,它确实会调整大小以适应,但其他单元格的所有自动调整都会出错。为了解决这个问题,附加的行为对其他(非最后一个)单元格进行手动自动拟合。
这也适用于调整其他列的大小-一旦加载,您可以调整大小,最后一列将始终填充。请注意,此行为只在Loaded事件上起作用一次

// Behavior usage: <DataGrid DataGridExtensions.LastColumnFill="True"/>
public class DataGridExtensions
{
    public static readonly DependencyProperty LastColumnFillProperty = DependencyProperty.RegisterAttached("LastColumnFill", typeof(bool), typeof(DataGridExtensions), new PropertyMetadata(default(bool), OnLastColumnFillChanged));

    public static void SetLastColumnFill(DataGrid element, bool value)
    {
        element.SetValue(LastColumnFillProperty, value);
    }

    public static bool GetLastColumnFill(DataGrid element)
    {
        return (bool)element.GetValue(LastColumnFillProperty);
    }

    private static void OnLastColumnFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid == null) return;

        dataGrid.Loaded -= OnDataGridLoaded;
        dataGrid.Loaded += OnDataGridLoaded;
    }        

    private static void OnDataGridLoaded(object sender, RoutedEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        var lastColumn = dataGrid.Columns.LastOrDefault();
        if(lastColumn != null)
            lastColumn.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

        // Autofit all other columns
        foreach (var column in dataGrid.Columns)
        {
            if (column == lastColumn) break;

            double beforeWidth = column.ActualWidth;
            column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToCells);
            double sizeCellsWidth = column.ActualWidth;
            column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToHeader);
            double sizeHeaderWidth = column.ActualWidth;
            column.MinWidth = Math.Max(beforeWidth, Math.Max(sizeCellsWidth, sizeHeaderWidth));
        }
    }
}
webghufk

webghufk4#

预先警告:这是一个黑客....
我注册到Dr. ABT类的“OnLastColumnFillChanged”方法中的“AutoGeneratedColumns”事件,并将Loaded方法复制到其中,它可以工作。我还没有完全测试过,所以YMMV。
我的零钱:

private static void OnLastColumnFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid == null) return;

        dataGrid.Loaded -= OnDataGridLoaded;
        dataGrid.Loaded += OnDataGridLoaded;

        dataGrid.AutoGeneratedColumns -= OnDataGrid_AutoGeneratedColumns;
        dataGrid.AutoGeneratedColumns += OnDataGrid_AutoGeneratedColumns;
    }

    private static void OnDataGrid_AutoGeneratedColumns(object sender, EventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        var lastColumn = dataGrid.Columns.LastOrDefault();
        if (lastColumn != null)
            lastColumn.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

        // Autofit all other columns
        foreach (var column in dataGrid.Columns)
        {
            if (column == lastColumn) break;

            double beforeWidth = column.ActualWidth;
            column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToCells);
            double sizeCellsWidth = column.ActualWidth;
            column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToHeader);
            double sizeHeaderWidth = column.ActualWidth;
            column.MinWidth = Math.Max(beforeWidth, Math.Max(sizeCellsWidth, sizeHeaderWidth));
        }
    }

对了,别忘了将名称空间添加到XAML声明中!:)
上一页:

xmlns:ext="clr-namespace:TestProject.Extensions"

然后在DataGrid声明中:

ext:DataGridExtensions.LastColumnFill="True"

更新:我说里程会有变化!我的的确如此。

整个“autofit columns”位导致我的DataGrid中的一些列的列数可变,只有列标题那么宽。我删除了这一部分,现在它似乎可以处理应用程序中的所有DataGrid。
现在我有:

private static void OnDataGrid_AutoGeneratedColumns(object sender, EventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        UpdateColumnWidths(dataGrid);

    }

    private static void OnDataGridLoaded(object sender, RoutedEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        UpdateColumnWidths(dataGrid);
    }

    private static void UpdateColumnWidths(DataGrid dataGrid)
    {
        var lastColumn = dataGrid.Columns.LastOrDefault();
        if (lastColumn == null) return;

        lastColumn.Width = new DataGridLength(1.0d, DataGridLengthUnitType.Star);

    }
yhqotfr8

yhqotfr85#

这里有一个非常简单的答案,所有这些都在代码后面执行。:-)所有列将自动调整大小;最后一列将填充所有剩余空间。

// build your datasource, e.g. perhaps you have a:
List<Person> people = ...

// create your grid and set the datasource
var dataGrid = new DataGrid();
dataGrid.ItemsSource = people;

// set AutoGenerateColumns to false and manually define your columns
// this is the price for using my solution :-)
dataGrid.AutoGenerateColumns = false;

// example of creating the columns manually.
// there are obviously more clever ways to do this 
var col0 = new DataGridTextColumn();
col0.Binding = new Binding("LastName");
var col1 = new DataGridTextColumn();
col1.Binding = new Binding("FirstName");
var col2 = new DataGridTextColumn();
col2.Binding = new Binding("MiddleName");
dataGrid.Columns.Add(col0);
dataGrid.Columns.Add(col1);
dataGrid.Columns.Add(col2);

// Set the width to * for the last column
col2.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
gxwragnw

gxwragnw6#

我可能会晚一点,但你可以试试我的代码从这个问题。我扩展了原始网格并添加了最后一列拉伸的方法:

private void StretchLastColumnToTheBorder()
{
    if (ViewPortWidth.HasValue)
    {
        var widthSum = 0d;
        for (int i = 0; i < Columns.Count; i++)
        {
            if (i == Columns.Count - 1 && ViewPortWidth > widthSum + Columns[i].MinWidth)
            {
                var newWidth = Math.Floor(ViewPortWidth.Value - widthSum);
                Columns[i].Width = new DataGridLength(newWidth, DataGridLengthUnitType.Pixel);
                return;
            }
            widthSum += Columns[i].ActualWidth;
        }
    }
}

其中ViewPortWidth是:

public double? ViewPortWidth 
{ 
    get 
    {
        return FindChild<DataGridColumnHeadersPresenter>(this, "PART_ColumnHeadersPresenter")?.ActualWidth;
    } 
}

因此,您必须找到DataGridColumnHeadersPresenter类型的可视子元素(答案来自here),它具有视口的宽度并计算最后一列的宽度。要自动执行此操作,可以在LayoutUpdated事件上触发此方法。此外,您可以添加DependencyProperty,指示是否应执行最后一列的自动拉伸。

f8rj6qna

f8rj6qna7#

基于pennyraveDR.ABT's answer的更新,我做了进一步的更新,以使其更好地工作。这仍然是一个技巧,但当我不断更新DataGrid的ItemsSource属性时,它似乎比他们的任何一个答案都更有效。如果我尝试在任何地方使用星星或自动宽度,WPF坚持所有列都只有20像素宽,所以我根据它们设置的自动值对它们进行硬编码。
我向AutoGeneratedColumns事件添加了一个调用,使其延迟一点。如果没有这个延迟,所有列都坚持它们只有20个像素宽。他们有时仍然这样做,但我已经检查了,它似乎工作,(但列呈现错误,然后在一毫秒后纠正。)
理想情况下,我们应该在WPF计算出自动大小之后,在呈现DataGrid之前应用列大小,但是我找不到任何方法让我的代码在那里运行。不是太早就是太晚。

public class DataGridExtensions
{
    public static readonly DependencyProperty LastColumnFillProperty = DependencyProperty.RegisterAttached("LastColumnFill", typeof(bool), typeof(DataGridExtensions), new PropertyMetadata(default(bool), OnLastColumnFillChanged));

    public static void SetLastColumnFill(DataGrid element, bool value)
    {
        element.SetValue(LastColumnFillProperty, value);
    }

    public static bool GetLastColumnFill(DataGrid element)
    {
        return (bool)element.GetValue(LastColumnFillProperty);
    }

    private static void OnLastColumnFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid == null) return;

        dataGrid.Loaded -= OnDataGridLoaded;
        dataGrid.Loaded += OnDataGridLoaded;

        dataGrid.AutoGeneratedColumns -= OnDataGrid_AutoGeneratedColumns;
        dataGrid.AutoGeneratedColumns += OnDataGrid_AutoGeneratedColumns;
    }

    private static void OnDataGrid_AutoGeneratedColumns(object sender, EventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => afterInvoke(dataGrid)));
    }

    private static void afterInvoke(DataGrid dataGrid)
    {
        bool nonMin = false;
        foreach (var col in dataGrid.Columns)
        {
            if (col.ActualWidth != col.MinWidth)
            {
                nonMin = true;
            }
        }
        if(nonMin)
        {
            OnDataGridLoaded(dataGrid, null);
        }
    }

    public static void OnDataGridLoaded(object sender, RoutedEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null) return;

        // set size of columns
        double sizeSoFar = 0;
        for(int i =0; i < dataGrid.Columns.Count; i++)
        {
            var column = dataGrid.Columns[i];

            //if last column
            if (i == dataGrid.Columns.Count-1)
            {
                sizeSoFar = dataGrid.ActualWidth - sizeSoFar - 2;//2 pixels of padding
                if(column.ActualWidth != sizeSoFar)
                {
                    column.MinWidth = sizeSoFar;
                    column.Width = new DataGridLength(sizeSoFar);
                }
            }
            else //not last column
            {
                double beforeWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToCells);
                double sizeCellsWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.SizeToHeader);
                double sizeHeaderWidth = column.ActualWidth;
                column.MinWidth = Math.Max(beforeWidth, Math.Max(sizeCellsWidth, sizeHeaderWidth));

                sizeSoFar += column.MinWidth; //2 pixels of padding and 1 of border
            }
        }
    }
}

记住在xaml顶部的窗口标记中添加类似xmlns:Util="clr-namespace:MyProject.Util"的内容,然后就可以在DataGrid标记中使用Util:DataGridExtensions.LastColumnFill="True"了。

pkln4tw6

pkln4tw68#

对于最后一列,设置Width="*"并覆盖MaxWidth,例如覆盖为MaxWidth="2000"

相关问题