Подтвердить что ты не робот

Как установить ширину 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? Спасибо!

4b9b3361

Ответ 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"