C# 控件。AddRange(…)速度较慢

C# 控件。AddRange(…)速度较慢,c#,.net,winforms,.net-2.0,flowlayoutpanel,C#,.net,Winforms,.net 2.0,Flowlayoutpanel,项目: 我有一个包含ComboBox和FlowLayoutPanel的父面板。FlowLayoutPanel包含数量可变的子面板(从UserControl继承的自定义控件)。每个子面板包含一些标签、两个组合框、一个按钮和一个DataGridView,其中包含3个组合框列和一个按钮列。DataGridView可能有1-6行。当从父面板上的组合框中选择项目时,FlowLayoutPanel将填充子面板 问题: 用大约50个子面板填充FlowLayoutPanel大约需要2.5秒。具体来说,我已经确

项目: 我有一个包含ComboBox和FlowLayoutPanel的父面板。FlowLayoutPanel包含数量可变的子面板(从UserControl继承的自定义控件)。每个子面板包含一些标签、两个组合框、一个按钮和一个DataGridView,其中包含3个组合框列和一个按钮列。DataGridView可能有1-6行。当从父面板上的组合框中选择项目时,FlowLayoutPanel将填充子面板

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

相关代码:我不能在这里发布我所有的代码(太多的代码加上部分代码是保密的),但我会尽力解释发生了什么

父面板:

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();
}
private void显示信息(套件)
{
这个.SuspendLayout();
//获取依赖项。
列表依赖项=新列表(suite.dependentials.Keys);
dependents.Sort(SuiteRange.Compare);
//为每个依赖项创建一个子面板。
列表范围面板=新列表();
foreach(从属中的SuiteRange dependent)
{
ChildPanel sdp=新的ChildPanel();
初始化(initialSuite.name,dataAccess);
显示信息(依赖,套件依赖[依赖]);
范围面板。添加(sdp);
}
//将子面板放入FlowLayoutPanel中。
flpDependencyGroups.SuspendLayout();
//大约需要2.5秒
flpDependencyGroups.Controls.AddRange(rangePanels.ToArray());
flpDependencyGroups.ResumeLayout();
//大约需要0.5秒
UpdateChildPanelizes();
这是resumeloayout();
}
我尝试过的事情:

  • 在父面板和/或FlowLayoutPanel上调用SuspendLayout()/ResumeLayout()。最低性能提升(约0.2秒)
  • 在组合框、按钮和DataGridView列上使用Control.FlatStyle.Flat。最低性能提升(约0.1秒)
  • 已验证我的控件均未使用透明背景色
  • 将ChildPanel.DoubleBuffered和ParentPanel.DoubleBuffered设置为true
  • 在调用AddRange()并在之后重新添加之前,请从其父级移除FlowLayoutPanel
可能相关的事情:

  • 面板和控件使用定位(与自动调整大小或停靠相反)
  • 我的控件是手动填充的,不使用DataSource属性
编辑:解决方案:

@HighCore的答案是正确的解决方案。不幸的是,我不会在这个时候实现它(它可能会在未来发生),因为我找到了一个解决办法。解决方法并不能真正解决问题,只是掩盖了它,因此我不把它作为一个答案。我发现,如果依赖项选项卡不在顶部(即选择了产品列表选项卡),则表单加载的时间会缩短一半。这将加载时间减少到1秒左右,这是可以接受的。当加载数据和依赖项选项卡在上面时,我切换到“产品列表”选项卡,在Tab控件上抛出一个深灰色框,在中间显示“加载…”,加载数据,然后切换回依赖项选项卡。


感谢大家的意见和建议,非常感谢。

发布此答案,因为OP要求:

以下是在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>
结果:

  • 请注意,我添加了100000条记录。尽管如此,由于WPF的内置,响应时间(在滚动和与UI交互时)仍然是即时的
  • 还请注意,我使用的是,它消除了在过程代码中操作UI元素的需要。这一点很重要,因为WPF可视化树是一个复杂的结构,数据绑定始终是WPF中的首选方法
  • 通过调整表单的大小,还可以注意到UI完全与分辨率无关。您可以通过固定组合框并将
    DataGrid
    延伸到剩余空间来进一步定制它。看
  • wpfrocks.-看看你能用这么少的代码实现多少,而不用在第三方控件中花费大量的$$$。你真的应该永远忘记winforms
  • 您至少需要以.NET3.0为目标,但强烈建议使用4.0/4.5,因为WPF在早期版本中存在几个问题,这些问题在4.0中已修复
  • 确保引用了
    PresentationCore.dll
    PresentationFramework.dll
    WindowsBase.dll
    System.Xaml.dll
    WindowsFormsIntegration.dll
    ,所有这些都属于.Net Framework本身(无第三方)

解决方案:WPF。winforms不支持大UI。可以在添加控件之前将WrapContents设置为false,然后将其重置为true,这样布局可能只执行一次。(不应该有帮助,因为xxRange()应该暗示这一点,但谁知道呢…)设计UI的方法可能是错误的,请显示屏幕截图,我们将尽力帮助您。在WinForms中,如此繁忙的UI肯定会有点迟钝。虽然这可能并不理想,也就是说,这是一种变通方法而不是解决方案,但您可以尝试在添加子项之前隐藏或删除FLP,然后再显示或添加它。我不确定这是否会有帮助,因此是评论而不是答案。我已经在WPF中创建了一个你的截图。对于10000个数据项,由于WPF的内置,响应时间是即时的。您可以通过将其集成到现有的winforms应用程序中。让我知道,如果这是一个选项为你,我会张贴一个完整的源代码的答案。
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; }
}
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

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

        Controls.Add(elementHost);

    }
}