Wie werden GridViewColumn-Daten in WPF automatisch angepasst und rechtsbündig ausgerichtet?

85

Wie kann ich:

  • Richten Sie den Text in der ID-Spalte nach rechts aus
  • jede der Spalten automatisch entsprechend der Textlänge der Zelle mit den längsten sichtbaren Daten verkleinern?

Hier ist der Code:

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

Teilantwort:

Dank Kjetil funktioniert die GridViewColumn.CellTemplate gut und die automatische Breite funktioniert natürlich. Wenn jedoch die ObservativeCollection "Collection" mit Daten aktualisiert wird, die länger als die Spaltenbreite sind, werden die Spaltengrößen nicht selbst aktualisiert, sodass dies nur eine Lösung für die ist Erstanzeige der Daten:

<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
quelle
1
Haben Sie jemals eine Lösung für Ihr Problem mit der automatischen Größe gefunden? Ich erlebe das Gleiche.
Oskar
2
@Oskar - Die Virtualisierung der Liste verhindert eine automatische Lösung. Die Liste kennt nur aktuell sichtbare Elemente und legt die Größe entsprechend fest. Wenn sich weiter unten in der Liste weitere Elemente befinden, sind diese nicht bekannt und können daher nicht berücksichtigt werden. Das Buch ProgrammingWPF - Sells-Griffith empfiehlt manuelle Spaltenbreiten, wenn Sie Datenbindung verwenden. :(
Gishu
@ Gishu Danke, das macht eigentlich Sinn ..
Oskar
Wenn sich die Werte für MVVM und Bindung ändern, lesen Sie bitte die Antwort von @Rolf Wessels.
Jake Berger

Antworten:

101

Um die automatische Größe jeder Spalte zu ändern, können Sie in der GridViewColumn Width = "Auto" festlegen.

Um den Text in der ID-Spalte nach rechts auszurichten, können Sie mit einem TextBlock eine Zellenvorlage erstellen und die TextAlignment festlegen. Legen Sie dann das ListViewItem.HorizontalContentAlignment fest (unter Verwendung eines Stils mit einem Setter im ListViewItem), damit die Zellenvorlage die gesamte GridViewCell ausfüllt.

Vielleicht gibt es eine einfachere Lösung, aber das sollte funktionieren.

Hinweis: Die Lösung erfordert sowohl HorizontalContentAlignment = Stretch in Window.Resources als auch TextAlignment = Right in der 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
quelle
@Kjetil - Kann ich diese Einstellung auf eine bestimmte Spalte anwenden?
Gishu
15
+1 für: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
Genial, aber ich habe 15 Spalten. Muss ich die Celltemplate nicht für alle wiederholen?
Nitin Chaudhari
7
Es funktioniert auch nicht, wenn Sie vergessen, die DisplayMemberBinding aus der GridViewColumn zu entfernen. Die Vorlage hat dann keine Wirkung.
Floele
@Mohamed Warum ist es nicht?
wahr.
36

Wenn sich die Breite des Inhalts ändert, müssen Sie dieses Codebit verwenden, um jede Spalte zu aktualisieren:

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

    column.Width = double.NaN;
}

Sie müssten es jedes Mal auslösen, wenn die Daten für diese Spalte aktualisiert werden.

RandomEngy
quelle
1
Womit würden Sie das verbinden?
Armentage
1
Führen Sie es manuell in der GridViewColumn aus, nachdem Sie die Rasterdaten aktualisiert haben. Wenn Sie ein ViewModel haben, können Sie das PropertyChanged-Ereignis darauf abonnieren und dann ausführen.
RandomEngy
+1 Danke dafür! Das hat mir sehr geholfen! Nicht im Zusammenhang mit dieser Frage, aber trotzdem: Ich habe eine angepasste List / GridView implementiert, in der Sie zur Laufzeit über die GUI dynamisch Spalten hinzufügen / entfernen können. Als ich jedoch eine Spalte entfernte und wieder hinzufügte, wurde sie nicht mehr angezeigt. Zuerst dachte ich, dass es überhaupt nicht hinzugefügt wurde (aus irgendeinem Grund), aber dann (mit Snoop) fand ich heraus, dass es tatsächlich hinzugefügt wurde, aber eine ActualWidth von 0 hat (es wurde automatisch angepasst und offensichtlich zurückgesetzt, als die Spalte war entfernt). Jetzt verwende ich Ihren Code, um die Spalte auf die richtige Breite einzustellen, nachdem ich sie erneut zu den Spalten hinzugefügt habe. Danke vielmals!
gehho
Eine einfache Lösung für mein Problem!
Gqqnbig
+1 Perfekt! Ich wünschte, dies wäre als Antwort markiert. Ich habe x: Name = "gvcMyColumnName" zu der XAML hinzugefügt, in der die Spalte definiert wurde, damit ich im Code dahinter darauf zugreifen kann. Funktioniert wie ein Champion.
K0D4
19

Wenn die Größe Ihrer Listenansicht ebenfalls geändert wird, können Sie die Spalten mithilfe eines Verhaltensmusters an die volle Breite der ListView anpassen. Fast das gleiche wie bei der Verwendung von grid.column-Definitionen

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

Unter dem folgenden Link finden Sie einige Beispiele und einen Link zum Quellcode http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Rolf Wessels
quelle
Dieser ist cool. Löst das Problem und bietet Ihnen alle Funktionen , n , Auto, die Sie suchen.
Designpattern
DAS habe ich gesucht. : D
Jake Berger
Hinweis: Es scheint einen Fehler zu geben. Wenn die Größe der ListView vertikal geändert wird, bis eine vertikale Bildlaufleiste angezeigt wird, wird die Spalte kontinuierlich breiter, bis die Bildlaufleiste verschwindet.
Jake Berger
1
Dieser Beitrag bietet möglicherweise einen Einblick in das in meinem vorherigen Kommentar beschriebene Verhalten.
Jake Berger
Es ist cool, ich meine sowohl den Code als auch die Seite :). Ich glaube, es wird nützlich sein, wenn ich strengere Anforderungen habe.
Gqqnbig
11

Ich habe die folgende Klasse erstellt und in der gesamten Anwendung verwendet, wo immer dies erforderlich ist 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
quelle
7

Da ich einen ItemContainerStyle hatte, musste ich die HorizontalContentAlignment in den ItemContainerStyle einfügen

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

Ich mochte die Lösung von user1333423, außer dass sie immer jede Spalte neu dimensionierte. Ich musste zulassen, dass einige Spalten eine feste Breite hatten. In dieser Version werden Spalten mit einer Breite, die auf "Auto" eingestellt ist, automatisch angepasst, und Spalten mit einem festen Betrag werden nicht automatisch angepasst.

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_
quelle
2

Ich weiß, dass dies zu spät ist, aber hier ist mein Ansatz:

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

Die Hauptidee besteht darin, die Breite des cellTemplete-Elements an die Breite der ViewGridColumn zu binden. Breite = 100 ist die Standardbreite, die bis zur ersten Größenänderung verwendet wird. Dahinter steckt kein Code. Alles ist in xaml.

Azzy Elvul
quelle
Dies inspirierte mich zu dieser Lösung, um die Breite einer Spalte zu füllen: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen
1

Ich hatte Probleme mit der akzeptierten Antwort (weil ich den Abschnitt HorizontalAlignment = Stretch verpasst und die ursprüngliche Antwort angepasst habe).

Dies ist eine andere Technik. Es wird ein Raster mit einer SharedSizeGroup verwendet.

Hinweis: Das Grid.IsSharedScope = true in der 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
quelle
Sie haben eine Breite von GridViewColumnas 40und setzen die Spaltendefinitionsbreite auf Auto? Das macht keinen Sinn.
BK
1

Ich habe eine Funktion zum Aktualisieren von GridView-Spaltenüberschriften für eine Liste erstellt und sie immer dann aufgerufen, wenn die Größe des Fensters geändert wird oder die Listenansicht das Layout aktualisiert.

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

Dies ist Ihr Code

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

Versuche dies

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