it-swarm.com.de

Langsame Leistung beim Füllen von DatagridView mit großen Daten

Ich verwende ein BindingSource-Steuerelement, um das Datagridview-Steuerelement aufzufüllen. Es gibt mehr als 1000 Datensätze darauf. Ich benutze dafür Threading. Der Datagridview ist in diesem Fall sehr langsam.

Ich habe versucht, die DoubleBuffered-Eigenschaft auf true zu setzen, RowHeadersWidthSizeMode auf disabled, AutoSizeColumnsMode auf none. Aber immer noch dasselbe Verhalten.

Bitte hilf mir dabei. Wie kann ich die Leistung des Grids verbessern?.

Danke im Voraus,
Vijay

14
Vijay Balkawade

Wenn Sie eine große Anzahl von Reihen haben, etwa 10 000 und mehr,

so vermeiden Sie Leistungsverluste: Führen Sie vor der Datenbindung folgende Schritte aus:

dataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing; //or even better .DisableResizing. Most time consumption enum is DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders

// set it to false if not needed
dataGridView1.RowHeadersVisible = false;

nachdem die Daten gebunden sind, können Sie sie aktivieren.

20
okarpov

stellen Sie sicher, dass Sie die Spalten nicht automatisch skalieren. Dies verbessert die Leistung.

d. h. tun Sie dies nicht.

11
saumil patel

Wenn Sie die automatische Größenanpassung deaktivieren und doppelt puffern, können Sie die DataGridView-Population beschleunigen. Überprüfen Sie, ob die DGV-Doppelpufferung ordnungsgemäß aktiviert ist:

if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
  Type dgvType = dataGridView1.GetType();
  PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
    BindingFlags.Instance | BindingFlags.NonPublic);
  pi.SetValue(dataGridView1, value, null);
}

Das Deaktivieren des Neuzeichnens mit der WinAPI WM_SETREDRAW message hilft auch:

// *** API Declarations ***
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
private const int WM_SETREDRAW = 11;

// *** DataGridView population ***
SendMessage(dataGridView1.Handle, WM_SETREDRAW, false, 0);
// Add rows to DGV here
SendMessage(dataGridView1.Handle, WM_SETREDRAW, true, 0);
dataGridView1.Refresh();

Wenn Sie keine bidirektionale Datenbindung oder einige von BindingSource bereitgestellte Funktionen (Filterung usw.) benötigen, können Sie in Betracht ziehen, Zeilen mit der Methode DataGridView.Rows.AddRange () auf einmal hinzuzufügen.

Der Link zum Quellartikel mit dem Beispiel: http://10tec.com/articles/why-datagridview-slow.aspx

7
TecMan

Wenn Sie die im virtuellen Modus erforderlichen Methoden des DataGridView nicht überschreiben möchten, gibt es eine andere Alternative, wenn Sie Listview in Betracht ziehen könnten:

http://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView

  • Es hat eine Version (FastObjectListView), die eine Liste von 100.000 .__ erstellen kann. Objekte in weniger als 0,1 Sekunden. 
  • Es hat eine Version (DataListView) die Datenbindung unterstützt, und eine andere (FastDataListView), die unterstützt die Datenbindung für große (100.000+) Datensätze.
3
HischT

Ich weiß, dass ich zu spät auf die Party komme, aber ich hatte es satt, wie langsam die automatische Größenanpassung für das DataGridView-Steuerelement war. Ich hatte das Gefühl, dass irgendwo jemand von meiner Lösung profitieren könnte.

Ich habe diese Erweiterungsmethode zum manuellen Messen und Ändern der Größe von Spalten in einer DataGridView erstellt. Setzen Sie AutoSizeColumnsMode auf DataGridViewAutoSizeColumnsMode.None und rufen Sie diese Methode nach dem Festlegen der DataSource auf.

/// <summary>
/// Provides very fast and basic column sizing for large data sets.
/// </summary>
public static void FastAutoSizeColumns(this DataGridView targetGrid)
{
    // Cast out a DataTable from the target grid datasource.
    // We need to iterate through all the data in the grid and a DataTable supports enumeration.
    var gridTable = (DataTable)targetGrid.DataSource;

    // Create a graphics object from the target grid. Used for measuring text size.
    using (var gfx = targetGrid.CreateGraphics())
    {
        // Iterate through the columns.
        for (int i = 0; i < gridTable.Columns.Count; i++)
        {
            // Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values.
            string[] colStringCollection = gridTable.AsEnumerable().Where(r => r.Field<object>(i) != null).Select(r => r.Field<object>(i).ToString()).ToArray();

            // Sort the string array by string lengths.
            colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray();

            // Get the last and longest string in the array.
            string longestColString = colStringCollection.Last();

            // Use the graphics object to measure the string size.
            var colWidth = gfx.MeasureString(longestColString, targetGrid.Font);

            // If the calculated width is larger than the column header width, set the new column width.
            if (colWidth.Width > targetGrid.Columns[i].HeaderCell.Size.Width)
            {
                targetGrid.Columns[i].Width = (int)colWidth.Width;
            }
            else // Otherwise, set the column width to the header width.
            {
                targetGrid.Columns[i].Width = targetGrid.Columns[i].HeaderCell.Size.Width;
            }
        }
    }
}

Ich würde zwar niemals empfehlen, eine DGV mit 1000+ Zeilen zu füllen, diese Methode führt jedoch zu einem enormen Leistungsvorteil, während sie sehr ähnliche Ergebnisse erzielt wie die AutoResizeColumns -Methode. 

Für 10.000 Zeilen: (10.000 Zeilen * 12 Spalten)

AutoResizeColumns = ~ 3000 ms

FastAutoSizeColumns = ~ 140 ms

3
Bobby L

Ich hatte ein Problem mit der Leistung, wenn der Benutzer 10000 Elemente lädt oder sortiert.

this.dataEvents.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;

Alles ist gut geworden.

2
Serov Danil

Ich musste die automatische Größenanpassung an einigen Stellen deaktivieren, um die größte Leistungsverbesserung zu erzielen. In meinem Fall hatte ich automatische Größenmodi für AutoSizeRowsMode, AutoSizeColumnsMode und ColumnHeadersHeightSizeMode aktiviert. Daher musste ich jedes dieser Elemente deaktivieren, bevor ich die Daten an das DataGridView binden konnte:

dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;

// ... Bind the data here ...

// Set the DataGridView auto-size modes back to their original settings.
dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
0
squareskittles

@ Bobby L Antwort ist toll, blockiert aber den UI-Thread. Hier ist meine Anpassung, die die Spaltenbreiten in einem BackgroundWorker berechnet, bevor die berechneten Werte auf den UI-Thread angewendet werden

public partial class Form1 : Form
{
    private BackgroundWorker _worker;

    public Form1()
    {
        InitializeComponent();

        _worker = new BackgroundWorker();
        _worker.DoWork += _worker_DoWork;
        _worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
    }

    private void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = GetAutoSizeColumnsWidth(dataGridView1);
    }

    private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        SetAutoSizeColumnsWidth(dataGridView1, (int[])e.Result);
    }

    private int[] GetAutoSizeColumnsWidth(DataGridView grid)
    {
        var src = ((IEnumerable)grid.DataSource)
            .Cast<object>()
            .Select(x => x.GetType()
                .GetProperties()
                .Select(p => p.GetValue(x, null)?.ToString() ?? string.Empty)
                .ToArray()
            );

        int[] widths = new int[grid.Columns.Count];
        // Iterate through the columns.
        for (int i = 0; i < grid.Columns.Count; i++)
        {
            // Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values.
            string[] colStringCollection = src.Where(r => r[i] != null).Select(r => r[i].ToString()).ToArray();

            // Sort the string array by string lengths.
            colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray();

            // Get the last and longest string in the array.
            string longestColString = colStringCollection.Last();

            // Use the graphics object to measure the string size.
            var colWidth = TextRenderer.MeasureText(longestColString, grid.Font);

            // If the calculated width is larger than the column header width, set the new column width.
            if (colWidth.Width > grid.Columns[i].HeaderCell.Size.Width)
            {
                widths[i] = (int)colWidth.Width;
            }
            else // Otherwise, set the column width to the header width.
            {
                widths[i] = grid.Columns[i].HeaderCell.Size.Width;
            }
        }

        return widths;
    }

    public void SetAutoSizeColumnsWidth(DataGridView grid, int[] widths)
    {
        for (int i = 0; i < grid.Columns.Count; i++)
        {
            grid.Columns[i].Width = widths[i];
        }
    }
}
0
Déric Dallaire

Ich denke, Sie müssen in Erwägung ziehen, Ihr Datengitter im virtuellen Modus zu verwenden. Grundsätzlich setzen Sie die Ausdehnungen des Gitters nach vorne und überschreiben dann "OnCellValueNeeded" wie erforderlich.

Sie sollten (insbesondere für nur etwa 1000 Zeilen) feststellen, dass Ihre Netzbelegung effektiv ist.

Viel Glück,

0
Adrian Conlon