2017-02-15 2 views
1

Ich habe eine WPF DataGrid, gebunden an eine DataTable, die eine variable Anzahl von Spalten enthalten kann. Diese DataTable kann zur Laufzeit das Schema und die Daten ändern, wenn der Benutzer eine neue Datendatei zum Laden in die App auswählt (im folgenden Beispiel wird dies durch Klicken auf die Schaltfläche Load Data simuliert).Wie kann ich die Spaltengröße bei Scroll in einem WPF DataGrid mit aktivierter Zeilenvirtualisierung stoppen?

Ich habe die Spaltenbreite auf Auto eingestellt, so dass Spalten automatisch die Größe anpassen, um ihren Kopftext oder den längsten Teil des Zeileninhalts in der Spalte (was immer größer ist) zu passen. Hier ist die MainWindow.xaml aus meinem Beispiel:

<Window x:Class="analog.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:analog" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="300" Width="600"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*" /> 
     </Grid.ColumnDefinitions> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="*"/> 
     </Grid.RowDefinitions> 
     <Button x:Name="loadData" Grid.Row="0" Margin="0" Content="Load Data" Click="loadData_Click" VerticalAlignment="Top" /> 
     <DataGrid x:Name="dataGrid" Grid.Row="1" Margin="0" IsReadOnly="True" CanUserAddRows="False" ColumnWidth="Auto" SelectionUnit="Cell" /> 
    </Grid> 
</Window> 

Und hier ist MainWindow.xaml.cs:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void loadData_Click(object sender, RoutedEventArgs e) 
    { 
     var data = new DataTable(); 
     data.Columns.Add("A", typeof(string)); 
     data.Columns.Add("B", typeof(string)); 
     data.Columns.Add("C", typeof(string)); 

     Enumerable.Range(1, 50).ToList().ForEach(i => { 
      var row = data.NewRow(); 
      row["A"] = "aaa"; 
      row["B"] = "bbb"; 
      row["C"] = "ccc"; 
      data.Rows.Add(row); 
     }); 

     var longRow = data.NewRow(); 
     longRow["A"] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 
     longRow["B"] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; 
     longRow["C"] = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"; 
     data.Rows.Add(longRow); 

     dataGrid.ItemsSource = data.DefaultView; 
    } 
} 

Das funktioniert perfekt, mit der Ausnahme, dass, weil die DataGrid Reihen virtuell sind (dh gerendert auf Anforderung, wenn die Liste gescrollt wird (im Gegensatz zu Upfront), können die Spalten nur automatisch auf den Inhalt angepasst werden, der gerade angezeigt wird.

Wenn Sie dann jedoch bis zum Ende der Liste scrollen, ändern sich die Spaltenbreiten plötzlich und dramatisch, wenn der längere Zelleninhalt sichtbar wird, was für eine wirklich schreckliche, desorientierende Benutzererfahrung sorgt.

Das Ausschalten der Zeilenvirtualisierung ist keine Option (z. B. EnableRowVirtualization="False"), da ich einige große Datenmengen lade und die Leistung ohne sie unbrauchbar ist.

Ich verstehe die Größenbeschränkung bei der Verwendung von Virtualisierung, und ich bin ziemlich glücklich mit diesem Verhalten. Es ist vollkommen in Ordnung, wenn die Spalten nur auf den Breiten bleiben, auf die sie eingestellt sind, wenn das Raster anfänglich gerendert wird, aber ich finde einfach keinen Weg, dies zu tun!

ich einige ziemlich schreckliche Hacks habe versucht, auch diese, wo ich Schleife über die Spalten nach dem ersten machen und programmatisch die Breite in Pixel an, was Größe, die sie derzeit gerendert auf gesetzt:

foreach (DataGridColumn column in dataGrid.Columns) 
{ 
    column.Width = new DataGridLength(column.ActualWidth, DataGridLengthUnitType.Pixel); 
} 

I Ich rufe das jetzt manuell auf, indem ich es in einen Button-Klick-Handler setze und auf die Schaltfläche klicke, nachdem die ersten Daten gerendert wurden - aber das hat keinerlei Auswirkungen, und die Größenänderung geschieht immer noch, wenn ich zu den längeren Werten komme.

Also, wie kann ich die Größenänderung der Spalten stoppen, wenn ich die DataGrid scrollen?

Antwort

0

Die Width sollte explizit eingestellt werden. Stellen Sie sicher, dass Sie dies tun, sobald die DataGrid geladen wurde.

Bitte beachten Sie den folgenden Beispielcode, der für mich funktioniert.

MainWindow.xaml.cs:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     List<DgItem> items = new List<DgItem>() 
     { 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abc", B = "abc" }, 
      new DgItem() { A = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc", B = "abc" } 
     }; 
     dg.ItemsSource = items; 
    } 

    private void dg_Loaded(object sender, RoutedEventArgs e) 
    { 
     foreach (DataGridColumn column in dg.Columns) 
     { 
      column.Width = new DataGridLength(column.ActualWidth, DataGridLengthUnitType.Pixel); 
     } 
    } 
} 

Mainwindow.XAML:

<DataGrid x:Name="dg" AutoGenerateColumns="False" Height="100" 
        Loaded="dg_Loaded"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="1" Binding="{Binding A}" /> 
     <DataGridTextColumn Header="2" Binding="{Binding B}" /> 
    </DataGrid.Columns> 
</DataGrid> 

enter image description here

+0

Hmm, das funktioniert nicht in meinem Fall. Dies ist ziemlich genau das, was ich gerade mache, außer dass die Schleife über die Spalten derzeit in einem Button-Klick-Handler (den ich gerade manuell anklicke, nachdem das Element geladen wurde) statt in dem Data-Grid-Load-Event-Handler erfolgt. –

+0

Der offensichtliche Unterschied zwischen Ihrem Beispiel und meinem Code ist, dass Sie die Spalten und Daten zur Kompilierzeit deklarieren (und 'AutoGenerateColumns =" ​​False "'), während in meiner App das Grid dynamisch aus dem Schema und den Daten von erstellt wird die aktuell an das Steuerelement gebundene 'DataTable' (die sich auch zur Laufzeit ändern kann). Entschuldigung, wenn ich das in der Frage klarer gemacht hätte ... jetzt bearbeitet. –

+0

Vielleicht könnten Sie dann eine reproduzierbare Probe Ihres Problems zur Verfügung stellen? Sie sollten dies lesen: http://stackoverflow.com/help/how-to-ask – mm8

0

Eine weitere Option (nicht genau als eine Lösung für die Frage, aber ähnliche Situationen, besonders wenn EnableColumnVirtualization wahr ist), ist, einfach die MaxWidth-Eigenschaft der Spalten festgelegt. Ein Beispiel:

public MainWindow() 
{ 
    InitializeComponent(); 

    DataTable tab = new DataTable(); 
    for (int i = 0; i < 1000; i++) 
     tab.Columns.Add("A" + i.ToString()); 
    for (int i = 0; i < 1000; i++) 
    { 
     DataRow r = tab.NewRow(); 
     for (int j = 0; j < 1000; j++) 
      r[j] = "something " + (i * i * j * j).ToString(); 
     tab.Rows.Add(r); 
    } 

    DataGrid dg = new DataGrid() { EnableColumnVirtualization = true, EnableRowVirtualization = true }; 
    this.Content = dg; 
    dg.ItemsSource = tab.AsDataView(); 
    dg.AutoGeneratingColumn += Dg_AutoGeneratingColumn; 
} 

private void Dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
{ 
    e.Column.MaxWidth = 100; 
} 
Verwandte Themen