J'ai un DGV non datamédiaire (pas de données de données, etc.; des lignes ajoutées manuellement). Pour filtrer, je faisais un chèque dans une boucle et régler la propriété des rangées visibles de manière appropriée. Cela a bien fonctionné avec des ensembles de tests plus petits, mais échoue complètement en performance avec des plus grands. 1k lignes filtrées à 5000 / sec. Des rangées 10K filtrées à seulement ~ 250 / sec. 50k à un simple 40 / sec. Mon hypothèse sur ce qui se passe est que chaque fois que je change une visibilité des lignes, la DGV reconstruit une liste des lignes affichées tournant le processus de filtrage en une opération O (n ^ 2).
tandis que même des lignes 10K indiquent que l'utilisateur abuse le système; Les utilisateurs mal élevés doivent être comptabilisés pour que j'ai besoin de faire quelque chose différemment. Y a-t-il un moyen plus rapide de filtrer un grand nombre de lignes que ce que je fais maintenant sans utiliser de liaison de données, ou dois-je revenir à la limitant / recréer toutes les lignes (pour des quantités raisonnables de données qu'il s'agit de manière significative plus lente)? p>
3 Réponses :
J'ai eu cette question il y a quelques années (avant que je connaisse des dindings) et j'ai trouvé un bogue post à Microsoft, affirmant que cela est confirmé, mais le problème ne sera probablement pas corrigé. Cependant, il y en a quelques-uns. possibilités de résoudre ceci. p>
instauré d'ajouter des lignes sur DataGridView, ajoutez des lignes à un jeu de données et de la lier à la DataGridView. P>
DataTable table = new DataTable(); table.Columns.Add("Name", typeof(String)); table.Columns.Add("...", typeof(String)); foreach (var element in list) table.Rows.Add(element.Name, element.Something); dataGridView1.DataSource = table1; table.DefaultView.RowFilter = "Name Like '...'";
Créez une classe qui hérite d'une liaison à la liaison et implémente IbindingList. Ensuite, liez-le à votre DataGridView. P> LI>
SET DataGridView virtualMode à true. p> li> ol>
La méthode deux est plus compliquée, car vous devez ajouter votre propre logique pour implémenter la méthode de la recherche de la recherche. p>
et vous devriez regarder ici: http://social.msdn.microsoft.com/forums/en-us / WinFormsDatacontrols / Thread / 68C8B93E-D273-4289-B2B0-0E9A644623A P> P>
Méthode1 On dirait que c'est probablement la meilleure façon de le faire. Malheureusement, il est probablement trop tard dans ce cycle de développement pour le faire. La solution «bon marché» dans la suspension du fil MSDN / Resumelayout n'a eu aucun gain de performance notable. Mon hypothèse serait que tout en ne répartissant pas la question fondamentale, la SEP était suffisamment gentille d'ajouter une sorte de suspension de mise en cache de mise en cache de modification pour limiter les dommages causés par mon type d'ignorance.
La performance globale devrait grandement améliorer si vous retirez temporairement les lignes du DataGridView tout en filtrant.
Copiez et collez ce code (N'oubliez pas d'ajouter des gestionnaires d'événements pour les événements de boutons) P>
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private Stopwatch watch = new Stopwatch(); private void Form1_Load(object sender, EventArgs e) { // populate dataGridView for (int i = 0; i < 10000; i++) dataGridView1.Rows.Add("Column", i+1, 10000 - i); for (int i = 0; i < 10000; i = i + 2) dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Red; } // remove filter private void button1_Click(object sender, EventArgs e) { watch.Reset(); watch.Start(); foreach (DataGridViewRow row in dataGridView1.Rows) row.Visible = true; watch.Stop(); MessageBox.Show(watch.ElapsedMilliseconds.ToString()); } // add filter (hide all odd rows) private void button2_Click(object sender, EventArgs e) { watch.Reset(); watch.Start(); foreach (DataGridViewRow row in dataGridView1.Rows) { if (Convert.ToInt32(row.Cells[1].Value) % 2 != 0) row.Visible = false; } watch.Stop(); MessageBox.Show(watch.ElapsedMilliseconds.ToString()); } // remove filter (improved) private void button3_Click(object sender, EventArgs e) { watch.Reset(); watch.Start(); List<DataGridViewRow> rows = new List<DataGridViewRow>(); foreach (DataGridViewRow row in dataGridView1.Rows) { rows.Add(row); } dataGridView1.Rows.Clear(); foreach (DataGridViewRow row in rows) row.Visible = true; dataGridView1.Rows.AddRange(rows.ToArray()); watch.Stop(); MessageBox.Show(watch.ElapsedMilliseconds.ToString()); } // add filer (improved) private void button4_Click(object sender, EventArgs e) { watch.Reset(); watch.Start(); List<DataGridViewRow> rows = new List<DataGridViewRow>(); foreach (DataGridViewRow row in dataGridView1.Rows) { rows.Add(row); } dataGridView1.Rows.Clear(); foreach (DataGridViewRow row in rows) { if (Convert.ToInt32(row.Cells[1].Value) % 2 != 0) { row.Visible = false; } } dataGridView1.Rows.AddRange(rows.ToArray()); watch.Stop(); MessageBox.Show(watch.ElapsedMilliseconds.ToString()); } }
Ça a marché. L'approche a également accéléré le processus de chargement initial de la grille.
Je suis abrasé la taille de la différence de performance: 19 vs. 0,1 sec sur ma machine. M'a beaucoup aidé. Merci.
En effet, je n'ai pas vérifié cela, mais aujourd'hui, je supposerais un appel à datagridview.suspendlayout (); code> avant les modifications et
datagridview.resumelayout (vrai); code> serait proporté avoir le même effet (j'aime bien envelopper ce type de code dans un
x.suspendlayout (); essayez {...} enfin {x.Resumelayout ();} code> bloc, sinon votre DataGridView gagnée ' t Mettez à jour l'UI plus si quelque chose échoue.
Ajoutez ces méthodes d'extension quelque part dans votre code: P>
[dllimport ("user32.dll", entréePoint = "sendmessagea", exactspelling = true, charset = charset.ansi, setlasterror = true)] privé statique externe int sendMessage (intPTR Hwnd, int WMSG, int wparam, int LPARAM); Private const int wm_sedraw = 0xb; p>
public static void SuspendDrawing(this Control target) { SendMessage(target.Handle, WM_SETREDRAW, 0, 0); } public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); } public static void ResumeDrawing(this Control target, bool redraw) { SendMessage(target.Handle, WM_SETREDRAW, 1, 0); if (redraw) { target.Refresh(); } }
Ajouter maintenant mydgv.suspendDrawing () code> Avant la boucle puis
mydgv.Resumedrawing () code> après la boucle. P>
li>
ol>
Pourquoi les lignes doivent-elles être générées manuellement? Il ne peut y avoir aucune réponse bonne ou rapide si cela est un problème architecturale profond.
Il n'y a pas une base de données pour fournir une source de données. Le backend de données utilise la série XML pour stocker les enregistrements de données dans des fichiers "pseudotable" et transmet les valeurs d'affichage (combinées à partir de plusieurs fichiers) sous forme de liste <> de structures contenant chacune une des données pour une seule ligne DGV.
Merci de le pointer. Je ne serais probablement pas au courant de ça jusqu'à ce qu'il soit trop tard.