Control.AddRange(...)是慢 [英] Control.AddRange(...) is slow

查看:1488
本文介绍了Control.AddRange(...)是慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

项目: 我有一个父面板持有一个组合框和FlowLayoutPanel的。该FlowLayoutPanel的拥有可变数量的子板(自定义控件继承自用户控件)。每个子面板包含一些标签,二组合框,按钮,并用3组合框列一个DataGridView和一个按钮列。在DataGridView可能有1-6行。该FlowLayoutPanel的是人口与孩子面板当一个项目是从父面板上的组合框选择。

Project: I have a parent panel which holds a ComboBox and FlowLayoutPanel. The FlowLayoutPanel holds a variable number of child panels (a custom control that inherits from UserControl). Each child panel contains some labels, two ComboBoxes, a button, and a DataGridView with 3 ComboBox columns and a button column. The DataGridView may have 1-6 rows. The FlowLayoutPanel is populated with child panels when an item is selected from the ComboBox on the parent panel.

问题: 填充FlowLayoutPanel的大约有50子板大约需要2.5秒。具体而言,我已经确定调用FlowLayoutPanel.Controls.AddRange()是罪魁祸首。

Problem: Populating the FlowLayoutPanel with about 50 child panels takes about 2.5 seconds. Specifically, I've determined that the call to FlowLayoutPanel.Controls.AddRange() is the culprit.

相关code:我不能发布我所有的code在这里(太code以及它的部分是保密的),但我会尽我的最好的解释发生了什么。

Relevant Code: I can't post all of my code here (too much code plus parts of it are confidential), but I'll do my best to explain what is happening.

父面板:

private void displayInformation(Suite suite)
{
    this.SuspendLayout();

    // Get dependencies.
    List<SuiteRange> dependents = new List<SuiteRange>(suite.dependencies.Keys);
    dependents.Sort(SuiteRange.Compare);

    // Create a ChildPanel for each dependent.
    List<ChildPanel> rangePanels = new List<ChildPanel>();
    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();
}

我尝试过的事情:

  • 呼叫SuspendLayout()/ ResumeLayout()的父面板和/或FlowLayoutPanel的上。最小的性能提升(大约需要0.2秒)。
  • 使用Control.FlatStyle.Flat的组合框,按钮和DataGridView列。最小的性能提升(±0.1秒)。
  • 验证,没有我的控件使用一个透明的背景色。
  • 设置ChildPanel.DoubleBuffered和ParentPanel.DoubleBuffered为true。
  • 从其父调用的AddRange(前取出FlowLayoutPanel的)后重新添加。

的事情,可能是相关的:

  • 的面板和控件使用锚(相对于自动调整大小或码头)。
  • 在我的控制是手工填写,不要使用DataSource属性。

编辑:解决方法:

@ HighCore的答案是正确的解决方案。不幸的是我不会实现它在这个时候(它可能发生的道路),因为我找到了一个解决办法。解决方法并不能真正解决问题,只是掩盖它,因此为什么我没有张贴此作为一个答案。我发现的形式加载了一半的时间,如果相关性选项卡的最前面(即产品列表选项卡中选择)。这减少了装载时间约1秒,这是可以接受的。当数据被加载,并且依存关系选项卡在上面,我切换到产品列表选项卡,在中间扔了一个暗灰色的盒子上的标签控制,说:载入中...,加载数据,然后切换回到依存关系选项卡。

@HighCore's answer is the correct solution. Unfortunately I won't be implementing it at this time (it could happen down the road) because I found a workaround. The workaround doesn't really solve the problem, just masks it, hence why I'm not posting this as an answer. I discovered that the form loads in half the time if the Dependencies tab isn't on top (i.e. the Product Lists tab is selected). This reduces loading time to about 1 second, which is acceptable. When data is being loaded and the Dependencies tab is on top, I switch to the Product Lists tab, throw up a dark grey box over the tab control that says "Loading..." in the middle, load the data, and then switch back to the Dependencies tab.

感谢大家的意见和建议,这是极大的AP preciated。

Thanks all for your comments and suggestions, it was greatly appreciated.

推荐答案

发布这个答案,因为OP提出请求:

Posting this answer because the OP requested it:

这是你如何做这样的事情在WPF:

This is how you'd do something like that in WPF:

<UserControl x:Class="WpfApplication7.ListBoxSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Button Content="Load" Click="Load_Click" DockPanel.Dock="Top"/>

        <ListBox ItemsSource="{Binding}"
                 HorizontalContentAlignment="Stretch">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="LightGray" BorderThickness="1" Padding="5"
                            Background="#FFFAFAFA">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>

                            <TextBlock Text="Dependent Versions" FontWeight="Bold"
                                       Grid.ColumnSpan="2" HorizontalAlignment="Center"/>

                            <TextBlock Text="From:" FontWeight="Bold"
                                       Grid.Row="1" HorizontalAlignment="Center"/>

                            <TextBlock Text="To (exclusive):" FontWeight="Bold"
                                       Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center"/>

                            <ComboBox SelectedItem="{Binding From}"
                                      ItemsSource="{Binding FromOptions}"
                                      Grid.Row="2" Margin="5"/>

                            <ComboBox SelectedItem="{Binding To}"
                                      ItemsSource="{Binding ToOptions}"
                                      Grid.Row="2" Grid.Column="1" Margin="5"/>

                            <DataGrid ItemsSource="{Binding ChildItems}"
                                      AutoGenerateColumns="False" CanUserAddRows="False"
                                      Grid.Column="2" Grid.RowSpan="4">
                                <DataGrid.Columns>
                                    <DataGridTextColumn Header="XXXX" Binding="{Binding XXXX}"/>
                                    <DataGridTextColumn Header="Dependee From" Binding="{Binding DependeeFrom}"/>
                                    <DataGridTextColumn Header="Dependee To" Binding="{Binding DependeeTo}"/>
                                    <DataGridTemplateColumn Width="25">
                                        <DataGridTemplateColumn.CellTemplate>
                                            <DataTemplate>
                                                <Button Content="X"/>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellTemplate>
                                    </DataGridTemplateColumn>

                                </DataGrid.Columns>
                            </DataGrid>

                            <Button Content="Delete"
                                    Grid.Column="3"
                                    HorizontalAlignment="Right" VerticalAlignment="Top"/>

                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </DockPanel>
</UserControl>

code的背后(只样板,支持的例子)

Code Behind (only boilerplate to support the example)

public partial class ListBoxSample : UserControl
{
    public ListBoxSample()
    {
        InitializeComponent();
    }

    public void LoadData()
    {
        Task.Factory.StartNew(() =>
        {
            var list = new List<DataItem>();

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

数据项:

public class DataItem
{
    public List<ChildItem> ChildItems { get; set; }

    public List<string> FromOptions { get; set; }

    public List<string> ToOptions { get; set; }

    public string From { get; set; }

    public string To { get; set; }

    public DataItem()
    {
        ChildItems = new List<ChildItem>();

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

然后你把在现有的WinForms UI使用的<一个href="http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost%28v=vs.110%29.aspx"相对=nofollow> 的ElementHost

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var elementHost = new ElementHost
        {
            Dock = DockStyle.Fill,
            Child = new ListBoxSample()
        };

        Controls.Add(elementHost);

    }
}

结果:

  • 请注意,我说的 10万条记录。不过,响应时间(滚动和与UI交互时都)是直接由于WPF的内置的 UI虚拟化
  • 还要注意,我使用的是 < STRONG>数据绑定 从而无需操作过程中的code UI元素。这很重要,因为在WPF可视树是一个复杂的结构,和数据绑定在WPF始终是preferred方法。
  • 同时通过调整表格的用户界面是完全独立的分辨率的通知。您可以通过使组合框固定,并具有的DataGrid 伸展的剩余空间进一步自定义。请参见 WPF布局
  • WPF岩 - 看看有多少你可以用这么少的code ++实现,而无需在第三方控件花很多$$$。你真的应该忘记的WinForms,直到永远。
  • 您将需要针对.NET 3.0在最低限度,不过4.0 / 4.5强烈建议,因为WPF在早期版本中,它被固定在4.0有几个问题。
  • 确保您引用 presentationCore.dll presentationFramework.dll WindowsBase.dll中 System.Xaml.dll WindowsFormsIntegration.dll程序,所有这些都属于.Net框架本身(无第三方)
  • Notice that I added 100,000 records. Still, response time (both when scrolling and interacting with the UI) is immediate due to WPF's built in UI Virtualization.
  • Also notice that I'm using DataBinding which removes the need to manipulate UI elements in procedural code. This is important because the WPF Visual Tree is a complex structure, and DataBinding is the preferred approach in WPF always.
  • Also notice by resizing the form that the UI is completely resolution independent. You can customize it further by making the ComboBoxes fixed and having the DataGrid stretch to the remaining space. See WPF Layouts.
  • WPF Rocks. - see how much you can achieve with so little code, and without spending lots of $$$ in third party controls. You should really forget winforms forever.
  • You will need to target .Net 3.0 at a minimum, but 4.0/4.5 is highly recommended because WPF had several issues in earlier versions, which were fixed in 4.0.
  • Make sure you reference PresentationCore.dll, PresentationFramework.dll, WindowsBase.dll, System.Xaml.dll and WindowsFormsIntegration.dll, all of which belong to the .Net Framework itself (no 3rd parties)

这篇关于Control.AddRange(...)是慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆