Як автоматично розподілити та вирівняти дані 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 працює добре, і Auto Width працює, звичайно, але коли 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.
Jake Berger

Відповіді:


104

Щоб зробити кожен із стовпців автоматичним, ви можете встановити Width = "Auto" у стовпці GridViewColumn.

Для вирівнювання тексту за стовпцем ідентифікатора за правою панеллю можна створити шаблон комірки за допомогою TextBlock та встановити TextAlignment. Потім встановіть ListViewItem.HorizontalContentAlignment (використовуючи стиль із сеттером на ListViewItem), щоб шаблон комірки заповнив всю GridViewCell.

Можливо, є більш просте рішення, але це має спрацювати.

Примітка: рішення вимагає як HorizontalContentAlignment = Розтягнути у Window.Resources, так і TextAlignment = Прямо в 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 - Чи можу я застосувати це налаштування до певного стовпця?
Gishu

15
+1 для: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein

приголомшливо, але у мене 15 стовпців, чи є спосіб, яким мені не доведеться повторювати шаблон комірки для всіх них?
Нітін Чаудхарі,

7
Це також не працює, якщо ви забудете видалити DisplayMemberBinding із стовпця GridViewColumn. Тоді шаблон не матиме жодного ефекту.
floele

@Mohamed Чому це не так?
ЦеNotALie.

37

Якщо ширина вмісту змінюється, вам доведеться використовувати цей біт коду для оновлення кожного стовпця:

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

    column.Width = double.NaN;
}

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


1
До чого б ви прикріпили це?
Armentage

1
Запустіть його вручну на GridViewColumn після оновлення даних сітки. Якщо у вас є ViewModel, ви можете підписатись на подію PropertyChanged і запустити її потім.
RandomEngy

+1 Дякую за це! Це мені дуже допомогло! Не пов’язане з цим питанням, але в будь-якому випадку: я реалізував індивідуальний List / GridView, де ви можете динамічно додавати / видаляти стовпці під час виконання через графічний інтерфейс. Однак, коли я видалив і повторно додав стовпець, він більше не з'являвся. По-перше, я думав, що його взагалі не додано (з якихось причин), але потім (за допомогою Snoop) я виявив, що він насправді доданий, але має фактичну ширину 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
Jake Berger

Примітка: здається, є помилка. Коли розмір ListView змінюється вертикально, до точки, через яку з'являється вертикальна смуга прокрутки, стовпець буде постійно збільшуватися в ширину, поки смуга прокрутки не зникне.
Jake Berger

1
Цей допис може дати розуміння поведінки, описаної в моєму попередньому коментарі.
Jake Berger

Це круто, я маю на увазі і код, і сайт :). Я вважаю, що це буде корисно, коли у мене будуть жорсткіші вимоги.
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);
    }
}

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);
    }        
}

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}">
J. Andersen,

1

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

Це ще одна техніка. Він використовує сітку з 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>

У вас є ширина GridViewColumnяк, 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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.