Как изменить размер WPF DataGrid в соответствии с его содержанием?
Цель
Я хотел бы установить такой размер для DataGrid (стандарт, из WPF), чтобы все ячейки (текст) были полностью видимыми. У меня есть окно с DockPanel с DataGrid, поэтому при изменении размера окна все вложенные виджеты (DockPanel и DataGrid) изменяются соответственно.
Пример (edit-1)
Скажем, у вас есть окно шириной 100 пикселей, и у вас есть DataGrid с одним столбцом, ячейка которого является "быстрой коричневой лисицей..." (ширина 400 пикселей). Таким образом, DataGrid должен быть изменен до 400 пикселей (возможно, больше из-за заполнения), а окно должно быть изменено до 400 пикселей (также больше из-за заполнения).
Я не нашел какого-либо стандартного метода для этого (AFAIK WPF предоставляет способ обрезать содержимое до нужной ширины, моя проблема в точности противоположна), поэтому я придумываю такое уродливое обходное решение, которое не работает слишком хорошо.
Обходной путь
- перебирать заголовки DataGrid (при условии, что они являются только строками) и ширины вычислений, необходимых для текста
- перебирать строки DataGrid для каждого столбца (при условии, что они являются TextBlock или TextBox) и вычислять максимальную ширину, требуемую для текста, - добавлять горизонтальные прокладки для TextBlock/TextBox и горизонтальные поля для ячейки DataGrid
- суммируйте все различия между DataGrid ActualWidth для столбцов и максимальной шириной, вычисленными в (2)
- увеличить ширину окна по разности, вычисленной в (3)
ПРОБЛЕМА
Я сделал несколько тестов, и в некоторых случаях вычисленная ширина слишком велика (это небольшая проблема), в некоторых случаях слишком мала. Проблема начинается с его основной процедуры - вычисления требуемой ширины для TextBox/TextBlock, вычисленная ширина всегда на единицу меньше, чем должна быть (если я устанавливаю ширину для вычисления, один пиксель из текста всегда обрезается).
Итак, , какой фактор я игнорирую здесь? Или, может быть, лучше - существует ли какой-либо метод для изменения размера DataGrid для его содержимого?
Код
Ширина вычислений, требуемая для текста (здесь для TextBlock):
public static double TextWidth(this TextBlock widget, string text)
{
var formattedText = new FormattedText(text, // can use arbitrary text
System.Globalization.CultureInfo.CurrentCulture,
widget.FlowDirection,
widget.FontFamily.GetTypefaces().FirstOrDefault(),
widget.FontSize,
widget.Foreground);
return formattedText.Width+widget.Padding.Left+widget.Padding.Right;
}
Настройка размера окна в соответствии с содержимым DataGrid (ugly_factor - уродливое обходное решение;-), так как я не понял, как правильно его исправить, я установил его в 1.3, и таким образом мое окно "никогда" слишком мало):
public static void AdjustWidthToDataGrid(this Window window, DataGrid dataGrid, double ugly_factor)
{
var max_widths = dataGrid.Columns.Select(it => window.TextWidth(it.Header as string)
* ugly_factor).ToArray();
foreach (var row in Enumerable.Range(0, dataGrid.Items.Count))
foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
{
var cell = dataGrid.GetCell(row, col);
double width = 0;
if (cell.Content is TextBlock)
width = (cell.Content as TextBlock).TextWidth();
else if (cell.Content is TextBox)
width = (cell.Content as TextBox).TextWidth();
if (cell.Content is FrameworkElement)
{
var widget = cell.Content as FrameworkElement;
width = width + widget.Margin.Left + widget.Margin.Right;
}
max_widths[col] = Math.Max(max_widths[col],
width*ugly_factor+cell.Padding.Left+cell.Padding.Right);
}
double width_diff = 0;
foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
width_diff += Math.Max(0,max_widths[col] - dataGrid.Columns[col].ActualWidth);
if (width_diff > 0)
window.Width = window.ActualWidth+ width_diff;
}
Ответы
Ответ 1
Комментарий Marko - это ответ:
Я немного поменяю логику изменения размера. Прежде всего, если вы установите окно SizeToContent="WidthAndHeight"
, тогда окно будет показано на полный размер файла данных. С другой стороны, если вы показываете окно с определенным предопределенным размером и хотите изменить размер окна после отображения окна, то в этот момент изменение размера до требуемого значения datagrid очень интуитивно противно. Поскольку изменение размера скачкообразно скажется от 100px до 400px, но что, если я хочу изменить размер до 250px... (если вы измените размер, перетащив угол окна)
Ответ 2
Я только что вышел из той же проблемы, когда мне пришлось задавать параметры в столбце сетки данных, чтобы соответствовать его ширине в соответствии с содержимым как заголовка, так и ячейки. Я использовал следующий код:
private void FitToContent()
{
// where dg is my data grid name...
foreach (DataGridColumn column in dg.Columns)
{
//if you want to size your column as per the cell content
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
//if you want to size your column as per the column header
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToHeader);
//if you want to size your column as per both header and cell content
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);
}
}
Также я предоставил опцию для столбцов, чтобы соответствовать в соответствии с отображением (ширина сетки данных). для этого я использовал тот же код выше со следующим небольшим изменением:
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Star);
ДЛЯ ВСЕХ КОЛОНН: Убедитесь, что вы придерживаетесь HorizontalScrollVisibility
- Auto
.
Ответ 3
Если я правильно понял ваш вопрос, и вы хотите:
- DataGrid, где столбцы имеют ширину, максимально широкую.
- DataGrid подходит для его содержимого.
Это может быть достигнуто с привязкой данных:
<DataGrid AutoGenerateColumns="False"
EnableRowVirtualization="True"
Height="111"
HorizontalAlignment="Left"
ItemsSource="{Binding}"
Margin="72,203,0,0"
Name="dataGrid"
RowDetailsVisibilityMode="VisibleWhenSelected"
VerticalAlignment="Top"
Width="{Binding Path=ActualWidth, ElementName=grid}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Column1"
Binding="{Binding Path=Something1}"
Header="Column1"
Width="Auto" />
<DataGridTextColumn x:Name="Column2"
Binding="{Binding Path=Something2}"
Header="Column2"
Width="*" />
</DataGrid.Columns>
</DataGrid>
Здесь нужен первый столбец шириной, а второй - пространство, оставшееся. Однако ширина DataGrid совпадает с шириной сетки, которая находится вокруг него, поэтому желаемый результат достигается.
Ответ 4
SizeToCells - это то, что сработало для меня. Мне это нравится, потому что его в XAML и это кратким.
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="SizeToCells"/>
Ответ 5
В моем случае я обнаружил, что DataGrid по умолчанию использует HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
, поэтому я установил его на
<DataGrid ItemsSource="{Binding Source}" Width="Auto" Height="Auto"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
Тогда работает.