it-swarm-ru.tech

Как я могу установить ширину 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, и я не могу думать о способе сделать это оттуда, потому что я не могу получить доступ к свойству ActualWidthDataGridColumn. Кроме того, я хотел бы сделать это только в XAML, если это возможно.

Буду признателен за любую помощь. Спасибо!

Правка: Поскольку я до сих пор не знаю, что с этим делать, я начинаю маленькую награду. Я был бы очень рад предложению, что можно сделать с моей проблемой. Еще раз спасибо!

Правка 2: После ответа Сауса я снова подумал о вариантах. Проблема в том, что мне нужно обновлять свойства Width и MinWidth также во время работы приложения, а не только после загрузки окна. Я уже пытался сделать что-то вроде

column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

в некоторых случаях это происходит, когда обновляется базовая ItemsSourceDataGrid. Однако это не работает, так как свойство ActualWidth, по-видимому, не изменяется после установки Width для Auto. Есть ли возможность как-то «перекрасить» его, чтобы обновить свойство ActualWidth? Спасибо!

21
Sören

Я бы предложил использовать обходной путь, с которым вы связались. Это работает. В коде вашего представления добавьте следующее в конструктор после 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 запрещает код в коде позади" - это немного заблуждение. 

Но если вы привязаны к руководствам по стилю или хотите, чтобы эта логика была доступна для всех наборов данных в вашем приложении, вы можете выполнить прикрепленное поведение, которое выполнит эту работу следующим образом:

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 ItemsSource="{Binding Table}" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">

И тогда твоя совесть, а также твой код будут ясны.

30
saus

Используйте 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>
4
Rafa Castaneda

Если вы хотите, чтобы столбец никогда не обрезал ваши данные, добавьте следующее в определение 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>
2
Wes Grant

как насчет этого:

для MinWidth ваших столбцов вы устанавливаете привязку к ширине ScrollContent объекта DataGrid с помощью преобразователя, который делит эту ширину на количество столбцов

вам нужен некоторый код для конвертера, но это сохранит вашу структуру MVVM нетронутой.

может быть длинный выстрел, хотя, не было времени, чтобы попробовать это.

Правка: Я еще немного подумал об этом, а там о пб: это не будет работать хорошо, если у вас есть, скажем, один огромный столбец и несколько маленьких. Я предположил, что все столбцы имеют одинаковую ширину, что здесь явно не так.

Вы могли бы хотеть исследовать этот путь все же. Использование привязок для Widths с конвертерами - это единственное, о чем я могу подумать: это может сработать: поскольку у вас есть два условия, которые необходимо учитывать при расчете ширины столбца, простого способа сделать это не будет.

1
David

Я добавил 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 уменьшает ширину столбцов до их минимальной ширины и делает их ширину статичной и недоступной для редактирования.

1
Mr.B

используйте это для любого столбца, который вы хотите вписать в его содержимое: Width = "SizeToCells"

0
user534957

Заставить последний столбец занять все оставшееся пространство в сетке данных

grid.Columns[grid.Columns.Count -1].Width = new 
                  DataGridLength(250, DataGridLengthUnitType.Star);
0
Nabeel