标准WPF 4数据网格。
让我说我有200像素宽的数据网格和2列。我希望列总是占用整个空间,这意味着如果用户将第一列的大小调整为50像素,则最后一列将为150.
最初我为第1列设置宽度为100像素,为最后一列设置*(在XAML中)。
我认为问题在于移除虚拟的第3列,如下所述:
http://wpf.codeplex.com/Thread/View.aspx?ThreadId=58939
但实际上没有区别 - 仍然,在调整列的大小时,我在右边获得了一些额外的空间 - 使用虚拟列,它是一个虚拟列(默认为白色),没有它,它是空的空间(默认为灰色)。
问题:如何强制执行约束,无论用户如何调整列的大小,
sum(columns width)==datagrid width
是的,我使用WPF 4。
解决方法
我将其中一个答案标记为解决方案,但实际上由于WPF设计,它不是解决方案。它只是WPF最多可以做的事情,它并不是很好 - 首先,列的选项CanUserResize实际上是IsResizeable,当打开时这个选项与Width设置为*相矛盾。因此,如果没有一些非常聪明的技巧,你最终会得到:
datagrid,其最后一列在表面上可以调整大小,但事实上它不是,并且右边的空间很小(即虚拟列不可调整大小) - 对于最后一列:CanUserResize = true,Width = * < / p>
datagrid,用户无法调整最后一列的大小,并且相应地显示,最初没有显示右边的空格,但是当用户调整datagrid的任何元素时,可以显示它 - 对于最后一列:CanUserResize = false ,宽度= *
到目前为止,我可以看到WPF datagrid的两个问题:
我仍然很清楚如何真正解决这个问题。
答案 0 :(得分:21)
将数据网格的宽度设置为“自动”。您允许列在网格本身内正确调整大小,但您已将宽度硬连接到200.
更新:根据@ micas的评论,我可能会误读。如果是这种情况,请尝试使用100表示左列的宽度,使用100 *表示右列(注意星号)。这会将右列的宽度默认为100,但允许它调整大小以填充网格。
答案 1 :(得分:5)
您可以在代码上设置列宽为星号。 在构造函数中,添加:
Loaded += (s, e) => dataGrid1.Columns[3].Width =
new DataGridLength(1, DataGridLengthUnitType.Star);
答案 2 :(得分:4)
我刚刚将此作为附加行为实现。问题是当你将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));
}
}
}
答案 3 :(得分:0)
预先警告:这是一个黑客......
我注册了&#34; AutoGeneratedColumns&#34; &#34; OnLastColumnFillChanged&#34;中的事件Dr. ABT的类的方法并将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"
<强>更新强>: 我说里程会有所不同!我当然做到了。
整个&#34; autofit列&#34; bit导致我的一些DataGrid中的列具有可变数量的列,只能与列标题一样宽。我删除了那部分,现在它似乎正在处理应用程序中的所有DataGrids。
现在我有:
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);
}
答案 4 :(得分:0)
这是一个非常简单的答案,所有这些都在后面的代码中执行。 :-) 所有列将自动调整大小;最后一列将填充所有剩余空间。
// 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);
答案 5 :(得分:0)
我可能会迟到一些,但是您可以尝试从this question进行验证。我扩展了原始网格并为最后一列的拉伸添加了方法:
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
,以指示是否应自动拉伸最后一列。
答案 6 :(得分:0)
根据 pennyrave 给 DR.ABT's answer 的更新,我进行了进一步的更新以使其更好地工作。它仍然是一个 hack,但是当我不断更新 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 顶部的 window 标记中添加类似 xmlns:Util="clr-namespace:MyProject.Util"
的内容,然后您可以在 DataGrid 标记中使用 Util:DataGridExtensions.LastColumnFill="True"
。