始终获得 dataGrid 所需的最大列宽 / space
Always get the max needed column width / space of a dataGrid
我正在尝试为 DataGrid 中的列设置动画。这也很完美。但是,我总是需要一个列的最大宽度,以便从中计算与 DataGrid 宽度的比率。
这适用于第一次通话。但是一旦列被动画化,"DesiredValue" 总是等于列的值。
((DataGridTemplateColumn)column).Width.DesiredValue
有没有人有解决方案或方法来始终使一列或所有列的宽度达到最大要求?
谢谢
问候语
多米尼克
它没有经过测试,但我认为应该可以满足您的需求?
int GetMaximumColumnWidth(DataGrid Grid, int ColumnIndex)
{
int maximum = 0;
foreach(DataRow row in Grid.Rows)
{
string text = row.ItemArray[ColumnIndex];
Size textSize = TextRenderer.MeasureText(text, Grid.Font);
if(textSize.Width > maximum)
{
maximum = textSize.Width;
}
}
return maximum;
}
这只是遍历列的所有值,测量文本和 returns 最大宽度。
编辑:
抱歉,我看到您正在寻找 wpf 中的解决方案。 TextRenderer 是 WinForms。但是也有一些方法可以测量 wpf 中的文本,如下所述:
WPF equivalent to TextRenderer
也许有点帮助...
解决方案:
诀窍是将列的宽度设置为自动,以获得所需的 space(设置为自动后,不要忘记更新布局!)。我在 .NetFramework 的 Framework.dll 中找到了该提示(在 DataGridColumnHeader.cs 中找到)。如果您双击 "gripper",该列将获得您需要的宽度。
private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e)
{
DataGridColumnHeader header = this.HeaderToResize(sender);
if ((header != null) && (header.Column != null))
{
header.Column.Width = DataGridLength.Auto;
e.Handled = true;
}
}
这是我调整列宽的方法(获得自定义 DataGrid 控件):
妙法:
public void AdjustColumns()
{
double availableSpace = this.ActualWidth;
double starSpace = 0.0;
double starFactor = 0.0;
Dictionary<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimations = new Dictionary<HTDataGridTemplateColumn, DataGridLengthAnimation>();
Storyboard storyboard = new Storyboard();
foreach (DataGridColumn column in this.Columns.AsParallel())
{
if (column.Visibility == Visibility.Visible && column.GetType() == typeof(HTDataGridTemplateColumn) && ((HTDataGridTemplateColumn)column).ResizeMode != HTDataGridTemplateColumn.ResizeModeOptions.None)
{
DataGridLengthAnimation animation = new DataGridLengthAnimation
{
From = column.ActualWidth,
DataGridLengthUnitType = DataGridLengthUnitType.Pixel,
Duration = new Duration(TimeSpan.FromMilliseconds(250)),
FillBehavior = FillBehavior.Stop
};
column.Width = DataGridLength.Auto;
columnAnimations.Add((HTDataGridTemplateColumn)column, animation);
Storyboard.SetTarget(animation, column);
Storyboard.SetTargetProperty(animation, new PropertyPath(DataGridColumn.WidthProperty));
storyboard.Children.Add(animation);
}
}
this.UpdateLayout();
foreach (KeyValuePair<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimation in columnAnimations)
{
if (columnAnimation.Key.ResizeMode == HTDataGridTemplateColumn.ResizeModeOptions.Fit)
{
availableSpace -= columnAnimation.Key.Width.DesiredValue;
columnAnimation.Value.To = columnAnimation.Key.Width.DesiredValue;
columnAnimation.Value.Completed += (sender, args) =>
{
columnAnimation.Key.Width = new DataGridLength(columnAnimation.Key.Width.DesiredValue, DataGridLengthUnitType.Pixel);
};
}
else
starSpace += columnAnimation.Key.Width.DesiredValue;
}
if (starSpace > 0.0)
starFactor = availableSpace/starSpace;
foreach (KeyValuePair<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimation in columnAnimations.Where(a => a.Key.ResizeMode == HTDataGridTemplateColumn.ResizeModeOptions.Stretch))
{
columnAnimation.Value.To = columnAnimation.Key.Width.DesiredValue * starFactor;
columnAnimation.Value.Completed += (sender, args) =>
{
columnAnimation.Key.Width = new DataGridLength(columnAnimation.Key.Width.DesiredValue * starFactor, DataGridLengthUnitType.Pixel);
};
}
storyboard.Begin();
}
}
HTDataGridTemplateColumn:
public class HTDataGridTemplateColumn : DataGridTemplateColumn
{
/// <summary>
/// Declare how the <see cref="DataGridColumn"/> should be resized.
/// </summary>
public ResizeModeOptions ResizeMode
{
get { return (ResizeModeOptions)GetValue(ResizeModeProperty); }
set { SetValue(ResizeModeProperty, value); }
}
public static readonly DependencyProperty ResizeModeProperty = DependencyProperty.Register("ResizeMode", typeof(ResizeModeOptions), typeof(HTDataGridTemplateColumn), new PropertyMetadata(ResizeModeOptions.None));
/// <summary>
/// Declare how the <see cref="DataGridColumn"/> should be resized.
/// </summary>
public enum ResizeModeOptions
{
/// <summary>
/// No resize animation/action will be done.
/// </summary>
None,
/// <summary>
/// The width is adjusted.
/// </summary>
Fit,
/// <summary>
/// The width is streched.
/// </summary>
Stretch
}
}
预览
旧尝试:
这是我的解决方案的尝试。这里的问题是,如果列不在视图中,"cell variable" 将始终为空。
现在我将为我的 DataGridColumns 设置一个行为,以便在文本更改时通知父 DataGrid 它的大小。希望这能完成工作。
private double[,] _CellSizeArray;
private double[] _ColumnSize;
//Only call once!
private void CalculateCellSizeArray()
{
try
{
_CellSizeArray = new double[this.Columns.Count, this.Items.Count];
foreach (object item in this.Items)
{
DataGridRow row = this.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
DataGridCellsPresenter presenter = Helper.VisualTree.GetVisualChild<DataGridCellsPresenter>(row);
for (int i = 0; i < this.Columns.Count; i++)
{
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(i);
if (cell == null)
{
this.UpdateLayout();
this.ScrollIntoView(this.Columns[i]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(i);
}
TextBlock textBlock = Helper.VisualTree.GetVisualChild<TextBlock>(cell);
DependencyPropertyDescriptor dp = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
dp.AddValueChanged(textBlock, (object a, EventArgs b) =>
{
Size s = MeasureTextSize(textBlock.Text, textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch, textBlock.FontSize);
_CellSizeArray[i, row.GetIndex()] = s.Width;
});
Size size = MeasureTextSize(textBlock.Text, textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch, textBlock.FontSize);
_CellSizeArray[i, row.GetIndex()] = size.Width;
}
}
CalculateColumnSize();
}
catch (Exception exception)
{
}
}
private void CalculateColumnSize()
{
_ColumnSize = new double[this.Columns.Count];
for (int column = 0; column < _CellSizeArray.GetLength(0); column++)
{
for (int row = 0; row < _CellSizeArray.GetLength(1); row++)
{
if (_CellSizeArray[column, row] > _ColumnSize[column])
_ColumnSize[column] = _CellSizeArray[column, row];
}
}
}
/// <summary>
/// Get the required height and width of the specified text. Uses FortammedText
/// </summary>
public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
FormattedText ft = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(fontFamily, fontStyle, fontWeight, fontStretch), fontSize, Brushes.Black);
return new Size(ft.Width, ft.Height);
}
/// <summary>
/// Get the required height and width of the specified text. Uses Glyph's
/// </summary>
public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
}
double totalWidth = 0;
double height = 0;
for (int n = 0; n < text.Length; n++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];
double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;
double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;
if (glyphHeight > height)
{
height = glyphHeight;
}
totalWidth += width;
}
return new Size(totalWidth, height);
}
我正在尝试为 DataGrid 中的列设置动画。这也很完美。但是,我总是需要一个列的最大宽度,以便从中计算与 DataGrid 宽度的比率。
这适用于第一次通话。但是一旦列被动画化,"DesiredValue" 总是等于列的值。
((DataGridTemplateColumn)column).Width.DesiredValue
有没有人有解决方案或方法来始终使一列或所有列的宽度达到最大要求?
谢谢
问候语 多米尼克
它没有经过测试,但我认为应该可以满足您的需求?
int GetMaximumColumnWidth(DataGrid Grid, int ColumnIndex)
{
int maximum = 0;
foreach(DataRow row in Grid.Rows)
{
string text = row.ItemArray[ColumnIndex];
Size textSize = TextRenderer.MeasureText(text, Grid.Font);
if(textSize.Width > maximum)
{
maximum = textSize.Width;
}
}
return maximum;
}
这只是遍历列的所有值,测量文本和 returns 最大宽度。
编辑:
抱歉,我看到您正在寻找 wpf 中的解决方案。 TextRenderer 是 WinForms。但是也有一些方法可以测量 wpf 中的文本,如下所述: WPF equivalent to TextRenderer
也许有点帮助...
解决方案:
诀窍是将列的宽度设置为自动,以获得所需的 space(设置为自动后,不要忘记更新布局!)。我在 .NetFramework 的 Framework.dll 中找到了该提示(在 DataGridColumnHeader.cs 中找到)。如果您双击 "gripper",该列将获得您需要的宽度。
private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e)
{
DataGridColumnHeader header = this.HeaderToResize(sender);
if ((header != null) && (header.Column != null))
{
header.Column.Width = DataGridLength.Auto;
e.Handled = true;
}
}
这是我调整列宽的方法(获得自定义 DataGrid 控件):
妙法:
public void AdjustColumns()
{
double availableSpace = this.ActualWidth;
double starSpace = 0.0;
double starFactor = 0.0;
Dictionary<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimations = new Dictionary<HTDataGridTemplateColumn, DataGridLengthAnimation>();
Storyboard storyboard = new Storyboard();
foreach (DataGridColumn column in this.Columns.AsParallel())
{
if (column.Visibility == Visibility.Visible && column.GetType() == typeof(HTDataGridTemplateColumn) && ((HTDataGridTemplateColumn)column).ResizeMode != HTDataGridTemplateColumn.ResizeModeOptions.None)
{
DataGridLengthAnimation animation = new DataGridLengthAnimation
{
From = column.ActualWidth,
DataGridLengthUnitType = DataGridLengthUnitType.Pixel,
Duration = new Duration(TimeSpan.FromMilliseconds(250)),
FillBehavior = FillBehavior.Stop
};
column.Width = DataGridLength.Auto;
columnAnimations.Add((HTDataGridTemplateColumn)column, animation);
Storyboard.SetTarget(animation, column);
Storyboard.SetTargetProperty(animation, new PropertyPath(DataGridColumn.WidthProperty));
storyboard.Children.Add(animation);
}
}
this.UpdateLayout();
foreach (KeyValuePair<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimation in columnAnimations)
{
if (columnAnimation.Key.ResizeMode == HTDataGridTemplateColumn.ResizeModeOptions.Fit)
{
availableSpace -= columnAnimation.Key.Width.DesiredValue;
columnAnimation.Value.To = columnAnimation.Key.Width.DesiredValue;
columnAnimation.Value.Completed += (sender, args) =>
{
columnAnimation.Key.Width = new DataGridLength(columnAnimation.Key.Width.DesiredValue, DataGridLengthUnitType.Pixel);
};
}
else
starSpace += columnAnimation.Key.Width.DesiredValue;
}
if (starSpace > 0.0)
starFactor = availableSpace/starSpace;
foreach (KeyValuePair<HTDataGridTemplateColumn, DataGridLengthAnimation> columnAnimation in columnAnimations.Where(a => a.Key.ResizeMode == HTDataGridTemplateColumn.ResizeModeOptions.Stretch))
{
columnAnimation.Value.To = columnAnimation.Key.Width.DesiredValue * starFactor;
columnAnimation.Value.Completed += (sender, args) =>
{
columnAnimation.Key.Width = new DataGridLength(columnAnimation.Key.Width.DesiredValue * starFactor, DataGridLengthUnitType.Pixel);
};
}
storyboard.Begin();
}
}
HTDataGridTemplateColumn:
public class HTDataGridTemplateColumn : DataGridTemplateColumn
{
/// <summary>
/// Declare how the <see cref="DataGridColumn"/> should be resized.
/// </summary>
public ResizeModeOptions ResizeMode
{
get { return (ResizeModeOptions)GetValue(ResizeModeProperty); }
set { SetValue(ResizeModeProperty, value); }
}
public static readonly DependencyProperty ResizeModeProperty = DependencyProperty.Register("ResizeMode", typeof(ResizeModeOptions), typeof(HTDataGridTemplateColumn), new PropertyMetadata(ResizeModeOptions.None));
/// <summary>
/// Declare how the <see cref="DataGridColumn"/> should be resized.
/// </summary>
public enum ResizeModeOptions
{
/// <summary>
/// No resize animation/action will be done.
/// </summary>
None,
/// <summary>
/// The width is adjusted.
/// </summary>
Fit,
/// <summary>
/// The width is streched.
/// </summary>
Stretch
}
}
预览
旧尝试:
这是我的解决方案的尝试。这里的问题是,如果列不在视图中,"cell variable" 将始终为空。
现在我将为我的 DataGridColumns 设置一个行为,以便在文本更改时通知父 DataGrid 它的大小。希望这能完成工作。
private double[,] _CellSizeArray;
private double[] _ColumnSize;
//Only call once!
private void CalculateCellSizeArray()
{
try
{
_CellSizeArray = new double[this.Columns.Count, this.Items.Count];
foreach (object item in this.Items)
{
DataGridRow row = this.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
DataGridCellsPresenter presenter = Helper.VisualTree.GetVisualChild<DataGridCellsPresenter>(row);
for (int i = 0; i < this.Columns.Count; i++)
{
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(i);
if (cell == null)
{
this.UpdateLayout();
this.ScrollIntoView(this.Columns[i]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(i);
}
TextBlock textBlock = Helper.VisualTree.GetVisualChild<TextBlock>(cell);
DependencyPropertyDescriptor dp = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
dp.AddValueChanged(textBlock, (object a, EventArgs b) =>
{
Size s = MeasureTextSize(textBlock.Text, textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch, textBlock.FontSize);
_CellSizeArray[i, row.GetIndex()] = s.Width;
});
Size size = MeasureTextSize(textBlock.Text, textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch, textBlock.FontSize);
_CellSizeArray[i, row.GetIndex()] = size.Width;
}
}
CalculateColumnSize();
}
catch (Exception exception)
{
}
}
private void CalculateColumnSize()
{
_ColumnSize = new double[this.Columns.Count];
for (int column = 0; column < _CellSizeArray.GetLength(0); column++)
{
for (int row = 0; row < _CellSizeArray.GetLength(1); row++)
{
if (_CellSizeArray[column, row] > _ColumnSize[column])
_ColumnSize[column] = _CellSizeArray[column, row];
}
}
}
/// <summary>
/// Get the required height and width of the specified text. Uses FortammedText
/// </summary>
public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
FormattedText ft = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(fontFamily, fontStyle, fontWeight, fontStretch), fontSize, Brushes.Black);
return new Size(ft.Width, ft.Height);
}
/// <summary>
/// Get the required height and width of the specified text. Uses Glyph's
/// </summary>
public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
}
double totalWidth = 0;
double height = 0;
for (int n = 0; n < text.Length; n++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];
double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;
double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;
if (glyphHeight > height)
{
height = glyphHeight;
}
totalWidth += width;
}
return new Size(totalWidth, height);
}