I'm trying to animate columns in a DataGrid. This also works perfectly. However, I always need the maximum width that wants to have a column, in order to calculate therefrom the ratio to the width of the DataGrid.
This works on the first call. But as soon as the columns were once animated the "DesiredValue" is always equal to the value the column is.
((DataGridTemplateColumn)column).Width.DesiredValue
Does anyone has a solution or a way to always have the maximum required width of one or all columns?
thank you
greeting Dominic
The trick is to set the width of the column to auto, to get the desired space (after setting to auto, don't forget to update your layout!). I found that hint in the Framework.dll of the .NetFramework (found in DataGridColumnHeader.cs). If you double click on the "gripper", the column get the desired width you need.
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;
}
}
Here is my Method to adjust the colum width (got a custom DataGrid Control):
Magical method:
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
}
}
Here is a try of my solution. The problem here is, that if the column is not in the view, the "cell variable" will be always null.
Now i will make a behavior for my DataGridColumns to inform the parent DataGrid of its size if the Text changes. Hope this will do the job.
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);
}