Как автоматически изменить размер и выровнять данные GridViewColumn по правому краю в WPF?

89

Как я могу:

  • выровнять текст в столбце идентификатора по правому краю
  • сделать каждый из столбцов автоматическим размером в соответствии с длиной текста ячейки с самыми длинными видимыми данными?

Вот код:

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="40"/>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="100" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
        </GridView>
    </ListView.View>
</ListView>

частичный ответ:

Спасибо Kjetil, GridViewColumn.CellTemplate работает хорошо, и автоматическая ширина, конечно, работает, но когда ObservativeCollection «Коллекция» обновляется данными, превышающими ширину столбца, размеры столбцов не обновляются сами по себе, поэтому это только решение для начальное отображение данных:

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" Width="Auto">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Id}" TextAlignment="Right" Width="40"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
        </GridView>
    </ListView.View>
</ListView>
Эдвард Тангуай
источник
1
Вы когда-нибудь находили решение своей проблемы с автоматическим размером? Я испытываю то же самое.
Оскар
2
@Oskar - виртуализация списка предотвращает автоматическое решение. Список знает только элементы, видимые в данный момент, и соответственно устанавливает размер. Если дальше по списку есть другие элементы, он не знает о них и, следовательно, не может их учитывать. Книга ProgrammingWPF - Sells-Griffith рекомендует устанавливать ширину столбцов вручную, если вы используете привязку данных. :(
Gishu
@Gishu Спасибо, это действительно имеет смысл ..
Оскар,
Если используются значения MVVM и Binding, см. Ответ @Rolf Wessels.
Джейк Бергер

Ответы:

104

Чтобы сделать каждый из столбцов автоматическим, вы можете установить Width = "Auto" для GridViewColumn.

Чтобы выровнять текст в столбце ID по правому краю, вы можете создать шаблон ячейки с помощью TextBlock и установить TextAlignment. Затем установите ListViewItem.HorizontalContentAlignment (используя стиль с установщиком в ListViewItem), чтобы шаблон ячейки заполнил весь GridViewCell.

Может быть, есть более простое решение, но оно должно работать.

Примечание: для решения требуются как HorizontalContentAlignment = Stretch в Window.Resources, так и TextAlignment = Right в CellTemplate.

<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
</Window.Resources>
<Grid>
    <ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" Width="40">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Id}" TextAlignment="Right" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
                <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>
Кьетил Ватнедал
источник
@Kjetil - Могу ли я применить этот параметр к определенному столбцу?
Гишу,
15
+1 за: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
круто, но у меня 15 столбцов, есть ли способ не повторять шаблон ячейки для всех?
Нитин Чаудхари
7
Это также не сработает, если вы забудете удалить DisplayMemberBinding из GridViewColumn. Тогда шаблон не будет иметь никакого эффекта.
floele
@Mohamed Почему нет?
НЕ ТАЛИ.
37

Если ширина содержимого изменится, вам придется использовать этот фрагмент кода для обновления каждого столбца:

private void ResizeGridViewColumn(GridViewColumn column)
{
    if (double.IsNaN(column.Width))
    {
        column.Width = column.ActualWidth;
    }

    column.Width = double.NaN;
}

Вам придется запускать его каждый раз, когда обновляются данные для этого столбца.

RandomEngy
источник
1
К чему бы вы это прикрепили?
Armentage
1
Запустите его вручную в GridViewColumn после обновления данных сетки. Если у вас есть ViewModel, вы можете подписаться на событие PropertyChanged и запустить его.
RandomEngy
+1 Спасибо за это! Это мне очень помогло! Не имеет отношения к этому вопросу, но в любом случае: я реализовал настраиваемый List / GridView, где вы можете динамически добавлять / удалять столбцы во время выполнения через графический интерфейс. Однако когда я удалил и снова добавил столбец, он больше не отображался. Сначала я подумал, что он вообще не добавлен (по какой-то причине), но затем (с помощью Snoop) я обнаружил, что он действительно добавлен, но имеет ActualWidth, равный 0 (он был автоматически изменен и, очевидно, сброшен, когда столбец был удалено). Теперь я использую ваш код, чтобы установить правильную ширину столбца после того, как я повторно добавил его в столбцы. Большое спасибо!
gehho
Простое решение моей проблемы!
Gqqnbig
+1 Отлично! Желаю, чтобы это было отмечено как ответ. Я добавил x: Name = "gvcMyColumnName" в XAML, где был определен столбец, чтобы я мог получить к нему доступ в коде позади. Работает как чемпион.
K0D4
19

Если ваш список также изменяет размер, вы можете использовать шаблон поведения, чтобы изменить размер столбцов, чтобы они соответствовали полной ширине ListView. Почти так же, как при использовании определений grid.column

<ListView HorizontalAlignment="Stretch"
          Behaviours:GridViewColumnResize.Enabled="True">
        <ListViewItem></ListViewItem>
        <ListView.View>
            <GridView>
                <GridViewColumn  Header="Column *"
                                   Behaviours:GridViewColumnResize.Width="*" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox HorizontalAlignment="Stretch" Text="Example1" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>

См. Следующие ссылки для некоторых примеров и ссылку на исходный код http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Рольф Вессельс
источник
Это круто. Решает проблему и дает вам все , п , Авто функциональность вы ищете.
Designpattern
ЭТО то, что я искал. : D
Джейк Бергер
Примечание: похоже, это ошибка. При изменении размера ListView по вертикали до точки, при которой появляется вертикальная полоса прокрутки, столбец будет постоянно увеличиваться в ширину, пока полоса прокрутки не исчезнет.
Джейк Бергер
1
Этот пост может дать представление о поведении, описанном в моем предыдущем комментарии.
Джейк Бергер
Это круто, я имею ввиду и код, и сайт :). Я считаю, что это будет полезно, когда у меня будут более строгие требования.
Gqqnbig
12

Я создал следующий класс и использовал его во всем приложении вместо GridView:

/// <summary>
/// Represents a view mode that displays data items in columns for a System.Windows.Controls.ListView control with auto sized columns based on the column content     
/// </summary>
public class AutoSizedGridView : GridView
{        
    protected override void PrepareItem(ListViewItem item)
    {
        foreach (GridViewColumn column in Columns)
        {
            // Setting NaN for the column width automatically determines the required
            // width enough to hold the content completely.

            // If the width is NaN, first set it to ActualWidth temporarily.
            if (double.IsNaN(column.Width))
              column.Width = column.ActualWidth;

            // Finally, set the column with to NaN. This raises the property change
            // event and re computes the width.
            column.Width = double.NaN;              
        }            
        base.PrepareItem(item);
    }
}
user1333423
источник
7

Поскольку у меня был ItemContainerStyle, мне пришлось поместить HorizontalContentAlignment в ItemContainerStyle

    <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=FieldDef.DispDetail, Mode=OneWay}" Value="False">
                         <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
                <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
    ....
папарацци
источник
6

Мне понравилось решение user1333423, за исключением того, что оно всегда меняло размер каждого столбца; Мне нужно было разрешить некоторым столбцам фиксированную ширину. Таким образом, в этой версии столбцы с шириной, установленной на «Авто», будут иметь автоматический размер, а те, для которых установлено фиксированное значение, не будут автоматически изменяться.

public class AutoSizedGridView : GridView
{
    HashSet<int> _autoWidthColumns;

    protected override void PrepareItem(ListViewItem item)
    {
        if (_autoWidthColumns == null)
        {
            _autoWidthColumns = new HashSet<int>();

            foreach (var column in Columns)
            {
                if(double.IsNaN(column.Width))
                    _autoWidthColumns.Add(column.GetHashCode());
            }                
        }

        foreach (GridViewColumn column in Columns)
        {
            if (_autoWidthColumns.Contains(column.GetHashCode()))
            {
                if (double.IsNaN(column.Width))
                    column.Width = column.ActualWidth;

                column.Width = double.NaN;                    
            }          
        }

        base.PrepareItem(item);
    }        
}
jv_
источник
2

Я знаю, что это слишком поздно, но вот мой подход:

<GridViewColumn x:Name="GridHeaderLocalSize"  Width="100">      
<GridViewColumn.Header>
    <GridViewColumnHeader HorizontalContentAlignment="Right">
        <Grid Width="Auto" HorizontalAlignment="Right">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="Local size" TextAlignment="Right" Padding="0,0,5,0"/>
        </Grid>
    </GridViewColumnHeader>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
    <DataTemplate>
        <TextBlock Width="{Binding ElementName=GridHeaderLocalSize, Path=Width, FallbackValue=100}"  HorizontalAlignment="Right" TextAlignment="Right" Padding="0,0,5,0" Text="Text" >
        </TextBlock>
    </DataTemplate>
</GridViewColumn.CellTemplate>

Основная идея - привязать ширину элемента cellTemplete к ширине ViewGridColumn. Ширина = 100 - ширина по умолчанию, используемая до первого изменения размера. Нет никакого кода. Все в xaml.

Аззи Эльвул
источник
Это вдохновило меня на решение заполнить ширину одного столбца: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
Дж. Андерсен
1

У меня возникли проблемы с принятым ответом (потому что я пропустил часть HorizontalAlignment = Stretch и скорректировал исходный ответ).

Это еще одна техника. Он использует сетку с SharedSizeGroup.

Примечание: Grid.IsSharedScope = True на ListView.

<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}" Grid.IsSharedSizeScope="True">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" Width="40">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                             <Grid>
                                  <Grid.ColumnDefinitions>
                                       <ColumnDefinition Width="Auto" SharedSizeGroup="IdColumn"/>
                                  </Grid.ColumnDefinitions>
                                  <TextBlock HorizontalAlignment="Right" Text={Binding Path=Id}"/>
                             </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
                <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>
Адам Теген
источник
У вас есть ширина GridViewColumnas 40и вы установили ширину определения столбца Auto? В этом нет смысла.
BK
1

Я создал функцию для обновления заголовков столбцов GridView для списка и вызываю ее всякий раз, когда размер окна изменяется или список обновляет его макет.

public void correctColumnWidths()
{
    double remainingSpace = myList.ActualWidth;

    if (remainingSpace > 0)
    {
         for (int i = 0; i < (myList.View as GridView).Columns.Count; i++)
              if (i != 2)
                   remainingSpace -= (myList.View as GridView).Columns[i].ActualWidth;

          //Leave 15 px free for scrollbar
          remainingSpace -= 15;

          (myList.View as GridView).Columns[2].Width = remainingSpace;
    }
}
Андрей Рогобете
источник
0

Это твой код

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="40"/>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="100" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
        </GridView>
    </ListView.View>
</ListView>

Попробуй это

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Id}" Width="Auto">
               <GridViewColumnHeader Content="ID" Width="Auto" />
            </GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding FirstName}" Width="Auto">
              <GridViewColumnHeader Content="First Name" Width="Auto" />
            </GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding LastName}" Width="Auto">
              <GridViewColumnHeader Content="Last Name" Width="Auto" />
            </GridViewColumn
        </GridView>
    </ListView.View>
</ListView>
StriderWaR
источник