Control.AddRange (…) es lento

Proyecto: tengo un panel principal que contiene un ComboBox y FlowLayoutPanel. El FlowLayoutPanel contiene una cantidad variable de paneles secundarios (un control personalizado que hereda de UserControl). Cada panel secundario contiene algunas tags, dos ComboBoxes, un botón y un DataGridView con 3 columnas ComboBox y una columna de botón. DataGridView puede tener de 1 a 6 filas. FlowLayoutPanel se llena con paneles secundarios cuando se selecciona un elemento del ComboBox en el panel principal.

enter image description here

Problema: poblar el FlowLayoutPanel con aproximadamente 50 paneles secundarios toma aproximadamente 2.5 segundos. Específicamente, he determinado que la llamada a FlowLayoutPanel.Controls.AddRange () es la culpable.

Código relevante: no puedo publicar todo mi código aquí (demasiados códigos y partes de él son confidenciales), pero haré todo lo posible para explicar lo que está sucediendo.

Panel de padres:

private void displayInformation(Suite suite) { this.SuspendLayout(); // Get dependencies. List dependents = new List(suite.dependencies.Keys); dependents.Sort(SuiteRange.Compare); // Create a ChildPanel for each dependent. List rangePanels = new List(); foreach (SuiteRange dependent in dependents) { ChildPanel sdp = new ChildPanel(); sdp.initialize(initialSuite.name, dataAccess); sdp.displayInformation(dependent, suite.dependencies[dependent]); rangePanels.Add(sdp); } // Put the child panels in the FlowLayoutPanel. flpDependencyGroups.SuspendLayout(); // Takes ~2.5 seconds flpDependencyGroups.Controls.AddRange(rangePanels.ToArray()); flpDependencyGroups.ResumeLayout(); // Takes ~0.5 seconds updateChildPanelSizes(); this.ResumeLayout(); } 

Cosas que he intentado:

  • Llame a SuspendLayout () / ResumeLayout () en el panel principal y / o FlowLayoutPanel. Aumento mínimo del rendimiento (~ 0.2 segundos).
  • Use Control.FlatStyle.Flat en las columnas ComboBoxes, Buttons y DataGridView. Aumento mínimo del rendimiento (~ 0.1 segundos).
  • Verificado que ninguno de mis controles usa un color de fondo transparente.
  • Establezca ChildPanel.DoubleBuffered y ParentPanel.DoubleBuffered en true.
  • Elimine FlowLayoutPanel de su principal antes de llamar a AddRange () y volver a agregarlo después.

Cosas que podrían ser relevantes:

  • Los paneles y controles utilizan anclajes (a diferencia de autosize o dock).
  • Mis controles se completan manualmente y no usan la propiedad DataSource.

EDITAR: Solución:

La respuesta de @ HighCore es la solución correcta. Lamentablemente, no lo implementaré en este momento (podría suceder en el futuro) porque encontré una solución alternativa. La solución no resuelve realmente el problema, simplemente lo enmascara, por lo tanto, no estoy publicando esto como una respuesta. Descubrí que el formulario se carga en la mitad del tiempo si la pestaña Dependencias no está en la parte superior (es decir, la pestaña Listas de productos está seleccionada). Esto reduce el tiempo de carga a aproximadamente 1 segundo, lo cual es aceptable. Cuando se cargan los datos y la pestaña Dependencias está en la parte superior, cambio a la pestaña Listas de productos, lanzo un cuadro gris oscuro sobre el control de tabs que dice “Cargando …” en el centro, cargo los datos, y luego cambio volver a la pestaña Dependencias.

Gracias a todos por sus comentarios y sugerencias, fue muy apreciado.

Publicar esta respuesta porque el OP lo solicitó:

Así es como harías algo así en WPF:

                                                

Código detrás (solo repetitivo para apoyar el ejemplo)

 public partial class ListBoxSample : UserControl { public ListBoxSample() { InitializeComponent(); } public void LoadData() { Task.Factory.StartNew(() => { var list = new List(); for (int i = 0; i < 100000; i++) { var item = new DataItem() { From = "1", To = "2", ChildItems = { new ChildItem() { DependeeFrom = i.ToString(), DependeeTo = (i + 10).ToString(), XXXX = "XXXX" }, new ChildItem() { DependeeFrom = i.ToString(), DependeeTo = (i + 10).ToString(), XXXX = "XXXX" }, new ChildItem() { DependeeFrom = i.ToString(), DependeeTo = (i + 10).ToString(), XXXX = "XXXX" } } }; list.Add(item); } return list; }).ContinueWith(t => { Dispatcher.Invoke((Action) (() => DataContext = t.Result)); }); } private void Load_Click(object sender, System.Windows.RoutedEventArgs e) { LoadData(); } } 

Elementos de datos:

 public class DataItem { public List ChildItems { get; set; } public List FromOptions { get; set; } public List ToOptions { get; set; } public string From { get; set; } public string To { get; set; } public DataItem() { ChildItems = new List(); FromOptions = Enumerable.Range(0,10).Select(x => x.ToString()).ToList(); ToOptions = Enumerable.Range(0, 10).Select(x => x.ToString()).ToList(); } } public class ChildItem { public string XXXX { get; set; } public string DependeeFrom { get; set; } public string DependeeTo { get; set; } } 

Luego, coloca eso en una interfaz de usuario de ElementHost existente usando ElementHost :

 public partial class Form1 : Form { public Form1() { InitializeComponent(); var elementHost = new ElementHost { Dock = DockStyle.Fill, Child = new ListBoxSample() }; Controls.Add(elementHost); } } 

Resultado:

enter image description here

  • Tenga en cuenta que agregué 100.000 registros . Aún así, el tiempo de respuesta (tanto al desplazarse como al interactuar con la interfaz de usuario) es inmediato debido a la virtualización de la interfaz de usuario integrada de WPF.
  • También tenga en cuenta que estoy usando DataBinding, que elimina la necesidad de manipular los elementos de la interfaz de usuario en el código de procedimiento. Esto es importante porque WPF Visual Tree es una estructura compleja, y DataBinding es el enfoque preferido en WPF siempre.
  • También observe al redimensionar el formulario que la UI es completamente independiente de la resolución . Puede personalizarlo aún más haciendo que los ComboBoxes sean fijos y haciendo que DataGrid extienda al espacio restante. Ver diseños de WPF .
  • Rocas WPF. – vea cuánto puede lograr con tan poco código y sin gastar muchos $$$ en controles de terceros. Deberías olvidarte de las formas de ganar para siempre.
  • Tendrá que apuntar a .Net 3.0 como mínimo, pero 4.0 / 4.5 es muy recomendable porque WPF tenía varios problemas en versiones anteriores, que se corrigieron en 4.0.
  • Asegúrese de hacer referencia a PresentationCore.dll , PresentationFramework.dll , WindowsBase.dll , System.Xaml.dll y WindowsFormsIntegration.dll , todos los cuales pertenecen a .Net Framework en sí (sin terceros)