Jak automatycznie dopasować rozmiar i wyrównać dane GridViewColumn do prawej w WPF?

89

Jak mogę:

  • wyrównaj tekst w kolumnie ID do prawej
  • ustawić rozmiar każdej kolumny zgodnie z długością tekstu komórki z najdłuższymi widocznymi danymi?

Oto kod:

<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>

odpowiedź częściowa:

Dzięki Kjetil, GridViewColumn.CellTemplate działa dobrze, a automatyczna szerokość działa oczywiście, ale kiedy „Kolekcja” ObservativeCollection jest aktualizowana danymi dłuższymi niż szerokość kolumny, rozmiary kolumn nie aktualizują się same, więc jest to tylko rozwiązanie dla początkowe wyświetlanie danych:

<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>
Edward Tanguay
źródło
1
Czy kiedykolwiek znalazłeś rozwiązanie swojego problemu z automatycznym rozmiarem? Doświadczam tego samego.
Oskar
2
@Oskar - wirtualizacja listy zapobiega automatycznemu rozwiązaniu. Lista zawiera tylko informacje o aktualnie widocznych elementach i odpowiednio ustawia rozmiar. Jeśli w dalszej części listy znajduje się więcej elementów, nie zna ich i dlatego nie może ich uwzględnić. Książka ProgrammingWPF - Sells-Griffith zaleca ręczne szerokości kolumn, jeśli używasz wiązania danych. :(
Gishu,
@Gishu Dzięki, to rzeczywiście ma sens ...
Oskar,
Jeśli zmieniają się wartości MVVM i Binding, zobacz odpowiedź @Rolf Wessels.
Jake Berger

Odpowiedzi:

104

Aby automatycznie zmienić rozmiar każdej kolumny, można ustawić Width = "Auto" w GridViewColumn.

Aby wyrównać tekst w kolumnie ID, możesz utworzyć szablon komórki za pomocą TextBlock i ustawić TextAlignment. Następnie ustaw ListViewItem.HorizontalContentAlignment (przy użyciu stylu z funkcją ustawiającą w ListViewItem), aby szablon komórki wypełnił całą GridViewCell.

Może jest prostsze rozwiązanie, ale powinno działać.

Uwaga: rozwiązanie wymaga zarówno HorizontalContentAlignment = Stretch w Window.Resources, jak i TextAlignment = Right w 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 Watnedal
źródło
@Kjetil - Czy mogę zastosować to ustawienie do określonej kolumny?
Gishu,
15
+1 za: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
super, ale mam 15 kolumn, czy jest jakiś sposób, żebym nie musiał powtarzać szablonu komórki dla nich wszystkich?
Nitin Chaudhari,
7
Nie działa również, jeśli zapomnisz usunąć DisplayMemberBinding z GridViewColumn. Szablon nie będzie wtedy działał.
floele
@Mohamed Dlaczego tak nie jest?
NIE jest.
37

Jeśli szerokość treści się zmieni, będziesz musiał użyć tego fragmentu kodu, aby zaktualizować każdą kolumnę:

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

    column.Width = double.NaN;
}

Będziesz musiał go odpalać za każdym razem, gdy dane dla tej kolumny zostaną zaktualizowane.

RandomEngy
źródło
1
Do czego byś to dołączył?
Armentage
1
Uruchom go ręcznie na GridViewColumn po zaktualizowaniu danych siatki. Jeśli masz ViewModel, możesz zasubskrybować zdarzenie PropertyChanged na nim i uruchomić je następnie.
RandomEngy
+1 Dzięki za to! To mi bardzo pomogło! Nie jest to związane z tym pytaniem, ale w każdym razie: zaimplementowałem dostosowany List / GridView, w którym można dynamicznie dodawać / usuwać kolumny w czasie wykonywania za pośrednictwem GUI. Jednak kiedy usunąłem i ponownie dodałem kolumnę, już się nie pojawiała. Po pierwsze, pomyślałem, że w ogóle nie został dodany (z jakiegoś powodu), ale potem (używając Snoopa) dowiedziałem się, że jest faktycznie dodany, ale ma ActualWidth równą 0 (był automatycznie dopasowywany i oczywiście resetowany, gdy kolumna była oddalony). Teraz używam twojego kodu, aby ustawić poprawną szerokość kolumny po ponownym dodaniu jej do Kolumny. Wielkie dzięki!
gehho
Proste rozwiązanie mojego problemu!
Gqqnbig
+1 Idealnie! Chciałbym, żeby to zostało oznaczone jako odpowiedź. Dodałem x: Name = "gvcMyColumnName" do XAML, w którym zdefiniowano kolumnę, dzięki czemu mogłem uzyskać do niej dostęp w kodzie za nią. Działa jak mistrz.
K0D4
19

Jeśli Twój widok listy również zmienia rozmiar, możesz użyć wzorca zachowania, aby zmienić rozmiar kolumn, aby pasowały do ​​pełnej szerokości ListView. Prawie to samo, co przy użyciu definicji 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>

Zobacz poniższy link, aby uzyskać kilka przykładów i link do kodu źródłowego http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Rolf Wessels
źródło
Ten jest fajny. Rozwiązuje problem i daje wszystko , n , funkcja Auto szukasz.
Designpattern
To jest to, czego szukałem. : D
Jake Berger
Uwaga: wygląda na to, że jest błąd. Gdy rozmiar ListView zostanie zmieniony w pionie, do punktu, który spowoduje pojawienie się pionowego paska przewijania, szerokość kolumny będzie stale zwiększać, aż pasek przewijania zniknie.
Jake Berger
1
Ten post może dostarczyć wglądu w zachowanie opisane w moim poprzednim komentarzu.
Jake Berger
Fajnie, mam na myśli zarówno kod, jak i stronę :). Wierzę, że przyda się, gdy będę mieć bardziej rygorystyczne wymagania.
Gqqnbig
12

Utworzyłem następującą klasę i użyłem jej w całej aplikacji wszędzie tam, gdzie jest to wymagane zamiast 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
źródło
7

Ponieważ miałem ItemContainerStyle, musiałem umieścić HorizontalContentAlignment w 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" /> 
    ....
paparazzo
źródło
6

Podobało mi się rozwiązanie user1333423, poza tym, że zawsze zmieniało rozmiar każdej kolumny; Musiałem pozwolić niektórym kolumnom mieć stałą szerokość. Dlatego w tej wersji kolumny z szerokością ustawioną na „Auto” będą automatycznie dopasowywane, a kolumny ustawione na stałą kwotę nie będą automatycznie dopasowywane.

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_
źródło
2

Wiem, że jest już za późno, ale oto moje podejście:

<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>

Głównym pomysłem jest powiązanie szerokości elementu cellTemplete z szerokością ViewGridColumn. Szerokość = 100 jest domyślną szerokością używaną do pierwszej zmiany rozmiaru. Nie ma żadnego kodu. Wszystko jest w XAML.

Azzy Elvul
źródło
To zainspirowało mnie do tego rozwiązania, aby wypełnić szerokość jednej kolumny: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen
1

Miałem problem z zaakceptowaną odpowiedzią (ponieważ przegapiłem część HorizontalAlignment = Stretch i dostosowałem oryginalną odpowiedź).

To jest inna technika. Używa siatki z SharedSizeGroup.

Uwaga: Grid.IsSharedScope = true na 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>
Adam Tegen
źródło
Masz szerokość GridViewColumnas 40i ustawiłeś szerokość definicji kolumny na Auto? To nie ma sensu.
BK
1

Utworzyłem funkcję do aktualizowania nagłówków kolumn GridView dla listy i wywołuj ją za każdym razem, gdy okno jest zmieniane lub listview aktualizuje swój układ.

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;
    }
}
Andrei Rogobete
źródło
0

To jest twój kod

<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>

Spróbuj tego

<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
źródło