Как установить ширину DataGridColumn в соответствии с содержимым ( "Авто" ), но полностью заполнить доступное пространство для DataGrid в MVVM?
У меня есть WPF DataGrid
, который содержит некоторые данные. Я хотел бы установить ширину столбцов таким образом, чтобы содержимое входило и никогда не обрезается (вместо этого горизонтальная полоса прокрутки должна стать видимой). Кроме того, я хочу, чтобы DataGrid
заполнил все место (я работаю с DockPanel
). Я использую следующий код (упрощенный):
<DataGrid ItemsSource="{Binding Table}">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1" Binding="{Binding Property1}" />
<DataGridTextColumn MinWidth="200" Width="Auto" Header="Column 2" Binding="{Binding Property2}" />
</DataGrid.Columns>
</DataGrid>
Это, по-видимому, не работает из коробки с Width="Auto"
, поскольку оно всегда выглядит примерно так:
![DataGrid: only the first part of the row is marked as "selected"]()
Это очевидно выглядит уродливым. Я хотел бы, чтобы вся строка была выбрана или, что было бы намного лучше, столбцы, чтобы заполнить всю ширину, но, как видно, это не работает.
Если я использую Width="*"
вместо этого, содержимое столбцов обрезается, что для меня еще хуже.
Я нашел здесь аналогичный вопрос, и там был найден обходной путь. Это может сработать, но я работаю с шаблоном MVVM, поэтому ItemsSource обновляется в ViewModel, и я не могу думать о способе делать это оттуда, потому что я не могу получить доступ к свойству ActualWidth
из DataGridColumn
. Кроме того, я хотел бы сделать это только в XAML, если это возможно.
Буду признателен за любую помощь. Спасибо!
Изменить: Поскольку я до сих пор не знаю, что с этим делать, я начинаю небольшую награду. Я был бы очень доволен предложением о том, что можно было бы сделать с моей проблемой. Еще раз спасибо!
Отредактируйте 2: После ответа saus я снова подумал о вариантах. Проблема в том, что мне нужно обновить свойства Width
и MinWidth
также во время работы приложения, поэтому не только после загрузки окна. Я уже пытался сделать что-то вроде
column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
в некотором случае, который запускается, когда базовый ItemsSource
из DataGrid
обновляется. Однако это не работает, поскольку свойство ActualWidth
, похоже, не изменяется после установки Width
на Auto
. Есть ли способ как-то "перекрасить" его, чтобы обновить свойство ActualWidth
? Спасибо!
Ответы
Ответ 1
Я бы предложил использовать обходной путь, с которым вы связались. Он работает. В коде вашего представления добавьте следующее к конструктору после InitializeComponent():
Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy
и определите SetMinWidths как:
public void SetMinWidths(object source, EventArgs e )
{
foreach (var column in Griddy.Columns)
{
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
}
Я предполагаю, что причина, по которой вы не хотите использовать это решение, заключается в том, что вы считаете, что MVVM запрещает код в коде. Но так как это полная логика просмотра, я считаю, что это оправдано в этом случае. Весь "MVVM запрещает код в кодебе" вещь - это немного неправильное представление.
Но если вы привязаны руководствами по стилям или хотите, чтобы эта логика была доступна для всех datagrids в вашем приложении, вы можете создать прикрепленное поведение, которое выполняет следующее задание:
public class SetMinWidthToAutoAttachedBehaviour
{
public static bool GetSetMinWidthToAuto(DependencyObject obj)
{
return (bool)obj.GetValue(SetMinWidthToAutoProperty);
}
public static void SetSetMinWidthToAuto(DependencyObject obj, bool value)
{
obj.SetValue(SetMinWidthToAutoProperty, value);
}
// Using a DependencyProperty as the backing store for SetMinWidthToAuto. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SetMinWidthToAutoProperty =
DependencyProperty.RegisterAttached("SetMinWidthToAuto", typeof(bool), typeof(SetMinWidthToAutoAttachedBehaviour), new UIPropertyMetadata(false, WireUpLoadedEvent));
public static void WireUpLoadedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid)d;
var doIt = (bool)e.NewValue;
if (doIt)
{
grid.Loaded += SetMinWidths;
}
}
public static void SetMinWidths(object source, EventArgs e)
{
var grid = (DataGrid)source;
foreach (var column in grid.Columns)
{
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
}
}
а затем для любого datagrid, к которому вы хотите применить это поведение, добавьте следующее:
<DataGrid ItemsSource="{Binding Table}" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">
И тогда ваша совесть, как и ваш код, будет понятной.
Ответ 2
Используйте TextBlock с переносом текста внутри столбца шаблона:
<DataGrid ItemsSource="{Binding Table}">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1"
Binding="{Binding Property1}" />
<DataGridTemplateColumn Width="*" Header="Column 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Property2}" TextWrapping="Wrap" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Ответ 3
Если вы хотите, чтобы столбец никогда не прерывал ваши данные, добавьте следующее в определение Window/UserControl:
xmlns:System="clr-namespace:System;assembly=mscorlib"
Затем для ширины столбцов, которые вы можете использовать для следующего.
Width="{x:Static System:Double.NaN}"
Это приводит к тому, что столбец будет всегда расширяться настолько, чтобы в нем было самое длинное значение.
Поскольку приведенный выше код не работал в DataGrid, как насчет добавления ScrollViewer вокруг DataGrid следующим образом:
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<DataGrid ItemsSource="{Binding Table}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="10" Width="Auto" Header="Column 1" Binding="{Binding Property1}" />
<DataGridTextColumn MinWidth="200" Width="*" Header="Column 2" Binding="{Binding Property2}" />
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
Ответ 4
как насчет этого:
для столбцов MinWidth, вы устанавливаете привязку к ширине прокрутки DataGrid ScrollContent Width с конвертером, который делит эту ширину на число столбцов
вам нужен код для конвертера, но это сохранит вашу структуру MVVM.
может быть длинным выстрелом, хотя у него не было времени попробовать.
Изменить: Я еще немного подумал об этом, и там pb: это не сработает, если у вас есть, скажем, одна огромная колонка и несколько небольших. Я предположил, что все cols имеют одинаковую ширину, что, очевидно, здесь не так.
Возможно, вам захочется изучить этот путь. Использование привязок на ширинах с конвертерами - единственное, что я могу думать о том, что это может сработать: поскольку у вас в основном есть 2 условия, которые нужно учитывать при расчете ширины столбца, не будет простого способа сделать это.
Ответ 5
Я добавил Event
в DataGrid
, которые переопределяют их ширину относительно фактической ширины DataGrid
:
private static void OnLoaded(object sender, RoutedEventArgs e)
{
DataGrid dg = sender as DataGrid;
foreach (var column in dg.Columns)
{
column.Width = new DataGridLength(dg.ActualWidth / dg.Columns.Count, DataGridLengthUnitType.Pixel);
}
}
Строка:
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
был абсолютно беспроблемным. Определение DataGridLengthUnitType.Star
сокращает ширину столбцов до минимальной ширины и делает их ширину статичной и неизменяемой.
Ответ 6
Заставить последний столбец взять все оставшееся место в datagrid
grid.Columns[grid.Columns.Count -1].Width = new
DataGridLength(250, DataGridLengthUnitType.Star);
Ответ 7
используйте это для любого столбца, который вы хотите вписать в его содержимое: Width = "SizeToCells"