wake-up-neo.com

Wie kann ich die Breite einer DataGridColumn so einstellen, dass sie zum Inhalt passt ("Auto"), aber den verfügbaren Platz für das DataGrid in MVVM vollständig ausfüllen?

Ich habe eine WPF DataGrid, die einige Daten enthält. Ich möchte die Breite der Spalten so einstellen, dass der Inhalt passt und nie abgeschnitten wird (stattdessen sollte eine horizontale Bildlaufleiste sichtbar werden). Außerdem möchte ich, dass DataGrid den gesamten verfügbaren Platz ausfüllt (ich arbeite mit einem DockPanel). Ich verwende den folgenden Code (vereinfacht):

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

Dies funktioniert anscheinend nicht mit Width="Auto", da es immer so aussieht:

DataGrid: only the first part of the row is marked as "selected"

Das sieht offensichtlich hässlich aus. Ich hätte gerne die ganze Zeile ausgewählt, oder, was viel besser wäre, die Spalten, um die ganze Breite auszufüllen, aber wie man sieht, funktioniert das nicht.

Wenn ich stattdessen Width="*" verwende, wird der Inhalt der Spalten abgeschnitten, was für mich noch schlimmer ist.

Ich habe hier eine ähnliche Frage gefunden und dort wurde ein Workaround gepostet. Das funktioniert zwar, aber ich arbeite mit dem MVVM-Muster. Daher wird ItemsSource im ViewModel aktualisiert, und ich kann von dort aus nicht nachdenken, da ich nicht auf die ActualWidth-Eigenschaft von DataGridColumn zugreifen kann. Außerdem möchte ich es, wenn möglich, nur in XAML tun.

Ich würde mich über jede Hilfe freuen. Vielen Dank!

Edit: Da ich immer noch keine Ahnung habe, was ich dagegen tun soll, starte ich eine kleine Belohnung. Ich würde mich sehr über einen Vorschlag freuen, was man an meinem Problem tun könnte. Danke noch einmal!

Edit 2: Nach saus 'Antwort dachte ich noch einmal über die Optionen nach. Das Problem ist, dass ich die Width- und die MinWidth-Eigenschaften auch während der Ausführung der Anwendung aktualisieren muss, also nicht erst nach dem Laden des Fensters. Ich habe schon so etwas versucht

column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

in einigen Fällen wird dies ausgelöst, wenn das zugrunde liegende ItemsSource des DataGrid aktualisiert wird. Dies funktioniert jedoch nicht, da sich die ActualWidth-Eigenschaft nach dem Setzen von Width auf Auto scheinbar nicht ändert. Gibt es eine Option, um es irgendwie "neu zu malen", um die ActualWidth-Eigenschaft zu aktualisieren? Vielen Dank!

21
Sören

Ich würde vorschlagen, den Workaround zu verwenden, mit dem Sie verlinkt haben. Es funktioniert. Fügen Sie im Codebehind Ihrer Ansicht nach InitializeComponent () dem Konstruktor Folgendes hinzu:

Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy

und definieren Sie SetMinWidths als:

public void SetMinWidths(object source, EventArgs e )
        {
            foreach (var column in Griddy.Columns)
            {
                column.MinWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            }
        }

Ich gehe davon aus, dass Sie diese Lösung nicht verwenden möchten, weil Sie glauben, dass MVVM Code im Codebehind verbietet. Da es sich hierbei jedoch ausschließlich um eine sichtspezifische Logik handelt, halte ich es für gerechtfertigt. Die ganze "MVVM verbietet Code im Codebehind" ist ein bisschen ein Missverständnis. 

Wenn Sie jedoch an Stilvorlagen gebunden sind oder möchten, dass diese Logik für alle Datagrids in Ihrer App verfügbar ist, können Sie ein angefügtes Verhalten erstellen, das die folgenden Aufgaben erfüllt:

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

fügen Sie für jedes Datagrid, auf das Sie dieses Verhalten anwenden möchten, Folgendes hinzu:

<DataGrid ItemsSource="{Binding Table}" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">

Und dann wird dein Gewissen sowie dein Codebehind klar sein.

30
saus

Verwenden Sie einen TextBlock mit Textumbruch in der Vorlagenspalte:

<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>
4
Rafa Castaneda

Wenn Sie möchten, dass die Spalte Ihre Daten niemals abschneidet, fügen Sie der Window/UserControl-Definition Folgendes hinzu:

xmlns:System="clr-namespace:System;Assembly=mscorlib"

Dann können Sie für die Breite der Spalten Folgendes verwenden.

 Width="{x:Static System:Double.NaN}"

Dadurch wird die Spalte immer breit genug, um den längsten Wert in der Spalte aufzunehmen.

Da der obige Code im DataGrid nicht funktioniert hat, können Sie wie folgt einen ScrollViewer um das DataGrid hinzufügen:

     <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>
2
Wes Grant

wie wäre es damit:

für die MinWidth Ihrer Spalten richten Sie eine Bindung für die Breite des dataGrid-ScrollContent ein, mit einem Konverter, der diese Breite durch die Anzahl der Spalten teilt

sie benötigen etwas Code für den Konverter, dies würde jedoch Ihre MVVM-Struktur intakt halten.

vielleicht eine lange Zeit, hatte nicht die Zeit, es zu versuchen.

Edit: Ich habe noch etwas darüber nachgedacht und es gibt eine Pb: Es wird nicht gut funktionieren, wenn man beispielsweise eine große Kolumne und ein paar kleine Kolumnen hat. Ich nahm an, dass alle Spalten dieselbe Breite haben, was hier offensichtlich nicht der Fall ist.

Vielleicht möchten Sie diesen Weg jedoch erkunden. Die Verwendung von Bindungen auf den Breiten mit Konvertern ist ungefähr das einzige, woran ich denken kann, das funktionieren könnte: Da Sie grundsätzlich zwei Bedingungen bei der Berechnung der Spaltenbreite berücksichtigen müssen, gibt es keine einfache Möglichkeit, dies zu tun.

1
David

Ich habe die Event zur DataGrid hinzugefügt, die ihre Breite bezüglich der tatsächlichen Breite der DataGrid neu definiert:

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

Die Linie:

column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

war absolut sinnlos. Durch die Definition von DataGridLengthUnitType.Star wird die Breite der Spalten auf ihre minimale Breite verringert und ihre Breite kann statisch und nicht editierbar sein.

1
Mr.B

verwenden Sie dies für jede Spalte, die Sie an den Inhalt anpassen möchten: Width = "SizeToCells"

0
user534957

Erzwinge, dass die letzte Spalte den gesamten verbleibenden Speicherplatz in Datagrid beansprucht

grid.Columns[grid.Columns.Count -1].Width = new 
                  DataGridLength(250, DataGridLengthUnitType.Star);
0
Nabeel