DataGridView设置DataSource太慢了 [英] DataGridView setting DataSource is too slow

查看:304
本文介绍了DataGridView设置DataSource太慢了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,



我目前有一个非常奇怪的问题。我创建了一个自定义控件,其上有2个gridviews和2个自定义滚动条,顶部是gridHeader,底部是gridData。第一个gridview(gridHeader)用于用户在每个列上输入搜索查询。第二个gridview(gridData)用于查看结果数据。对于gridHeader中的每一列,我还创建了一个过滤器控件,它将放置在列标题上,使用此过滤器控件,用户还可以通过检查列中列中的唯一值来过滤/关闭数据(单击一个小按钮)列,你会看到唯一的值。)



控件工作并证明了自己,但它太慢了。加载一些数据源可能需要1000毫秒。在通过添加计时器来检查代码之后,我发现问题不是我做的重度过滤,结果是在分配给数据表时将网格视图引起的,以及将过滤器控件添加到列表头时。



为了解决第二个问题我开始时没有检测到没有删除FilterColumnClass并将它们留在内存中,所以第二个加载会更快。但是,如果我分配只填充1行的header-gridview的数据源,则需要大约180 ms,而当我分配填充100行的data-gridview的数据源时,它只需要大约10 ms。



当分配标题网格时,1个事件被触发,由于_StopEventHandling标志设置为true,因此无法执行任何操作。



我把时间作为注释放在代码中,第一个意思是;第一次将数据表分配给控件时,第二次意味着;第二次分配数据表。你看到的ms的时间是与函数开头相比的时间或与上一次时间快照相比的时间。



以下是代码的一部分问题:



Hi there,

I'm currently having a really weird problem. I have created a custom control with 2 gridviews and 2 custom scrollbars on it, gridHeader on top, gridData on the bottom. The first gridview (gridHeader) is used for the user to enter a search query on every individual column. The second gridview (gridData) is used to view the resulting data. For each column in gridHeader I also create a filter control that will be placed over the columnheader, with this filter control the user can also filter the data by checking on/off the unique values in list of the column(click on a little button in the column and you'll see the unique values).

The control works and has proven itself, BUT it's too slow. It can take up to 1000 ms to load some datasource. After checking the code by adding timers I figured out the problem is not the heavy filtering I do, it turns out its caused by the gridviews when being assigned to a datatable and also while adding the filter controls to the columnheaders.

To kinda solve the second problem I started by not removing the FilterColumnClass when not detected and leaving them in memory, so the second load will be faster. But if I assign the datasource of the header-gridview filled with only 1 row it takes about 180 ms, while when I assign the datasource of the data-gridview filled with 100 rows it only takes about 10 ms.

When the header-grid is assigned, 1 event gets triggerd which doesn't do anything because of the "_StopEventHandling" flag which is set to true.

I've put the timings as a comment in the code, 1st means; The first time you assign a datatable to the control, 2nd means; The second time you've assigned a datatable. The time in ms you see is the time it took compaired to the beginning of the function or compaired to the last time-snapshot.

Here is part of the code in question:

public class SomeCustomUserControl
{
    private DataTable _AllData = null;
    private DataTable _ViewData = null;
        
    private List<FilterColumnClass> _FilterColumns = new List<FilterColumnClass>();
        
    private bool _StopEventHandling = false;
    private bool _DataBound = false;
    private bool _DataBoundToSendSelected = false;
        
    private int _LastSortColumnIndex = -1;
    private bool _LastSortColumnDir = false;

    public void SetDataSource(DataTable value)
    {
        // Set all filtercontrols to invisible
        foreach (FilterColumnClass filter in _FilterColumns)
        {
            filter.FilterCheckControl.Visible = false;
        }

        // Assigning the received datatable to a local variable
        _AllData = value;

        // Do the databinding
        DataBind();
    }

    private void DataBind()
    {
        if (_AllData != null)
        {
            // Letting the control know we've started databinding the data.
            _DataBound = true;

            // Letting the control know it should NOT handle any events from the grids
            _StopEventHandling = true; 

            // Included in example: scroll down!
            PrepareGridViews(); 
 
            // 1st: 0ms 2nd: 0ms

            // Included in example: scroll down!
            PrepareFilterColumns(); 
 
            // 1st: 472ms 2nd: 252ms
 
            // ================ !!! HERE !!! ====================
            // Creates a new datatable according the filters
            _ViewData = FilterRows(_AllData, _FilterColumns); 
            gridData.DataSource = _ViewData;  
            // ================ !!! HERE !!! ====================
            // THIS IS REALLY FAST: 1st: 12ms 2nd: 10ms
            // The _ViewData datatable contains around 100 records

            // Sorts the new datasource according the last sort
            if (_LastSortColumnIndex != -1)
            {
                if (gridHeader.Columns.Count > _LastSortColumnIndex)
                {
                    try
                    {
                        if (_LastSortColumnDir)
                            gridData.Sort(gridData.Columns[gridHeader.Columns[_LastSortColumnIndex].Name], ListSortDirection.Ascending);
                        else
                            gridData.Sort(gridData.Columns[gridHeader.Columns[_LastSortColumnIndex].Name], ListSortDirection.Descending);
                    }
                    catch
                    {
                    }
                }
            }

            // 1st: 0ms 2nd: 0ms

            // Sets the columns to the users preferences, like column-name, column-width and column-order
            if (gridData.Columns.Count > 0) Personalize_Columns();

            // 1st: 194ms 2nd: 187ms

            // Sets the custom scrollbars min, max and value settings
            PrepareScrollBars();

            // 1st: 0ms 2nd: 1ms

            // Resets the vertical position of the custom scrollbars
            SetVerticalPosition(vScrollBar1.Value);

            // 1st: 0ms 2nd: 0ms

            // Resets the horizontal position of the custom scrollbars
            foreach (DataGridViewColumn col in gridHeader.Columns)
            {
                if (col.DisplayIndex == 0)
                {
                    _LastScrollingColumnIndex = -1;
                    SetHorizontalPosition(col.Index);
                }
            }
            // Checks if the parent-control wants to mark some items in the grid
            AskMarkedItems();

            // 1st: 196ms 2nd: 36ms

            // Letting the control know we're done databinding the data
            _DataBoundToSendSelected = true;

            // Letting the control know it should continue handling the events from the grids
            _StopEventHandling = false;
        }
        else
        {
            // Reset the grids
            gridHeader.DataSource = null;
            gridData.DataSource = null;
            _ViewData = null;
        }
 
        // TOTAL 1st: 874ms 2nd: 486ms
    }

    private void PrepareGridViews()
    {
        // Speeds up the gridviews
        SetDoubleBuffered(gridData, true); 
        SetDoubleBuffered(gridHeader, true);

        // Templating
        gridHeader.RowTemplate.Height = PublicFunctions.UniversalHeight - 6; 
        gridData.RowTemplate.Height = PublicFunctions.UniversalHeight - 6;

        // No columnheader because we use the one from gridHeader
        gridData.ColumnHeadersVisible = false; 
    }
    private void PrepareFilterColumns()
    {
        // Index counter for the column order
        int index = 0;

        // Suspend the layout to speed up the adding of the filter controls
        this.SuspendLayout();

        foreach (DataColumn col in _AllData.Columns)
        {
            // First try to find the the filter column class, if it is already added
            FilterColumnClass filter = _FilterColumns.Find(delegate(FilterColumnClass item) { return item.ColumnName == col.ColumnName; });

            if (filter != null)
            {
                // filter column class found, reset the index to be sure
                filter.Index = index;
                filter.FilterCheckControl.Visible = true;
            }
            else
            {
                // filter column not found, create one and add it to this control
                FilterColumnClass new_filter = new FilterColumnClass();
                new_filter.ColumnName = col.ColumnName;
                new_filter.Index = index;
                new_filter.FilterCheckControl.Visible = true;
                new_filter.FilterCheckControl.eventAskCurrentDataTable += new AskDataTableByInt(FilterCheckControl_eventAskCurrentDataTable);
                new_filter.FilterCheckControl.eventAskSourceDataTable += new AskDataTableByInt(FilterCheckControl_eventAskSourceDataTable);
                new_filter.FilterCheckControl.eventApplyFilter += new EventHandler(FilterCheckControl_eventApplyFilter);

                _FilterColumns.Add(new_filter);
                this.Controls.Add(new_filter.FilterCheckControl);
            }
            index++;
        }

        // Adding of filter controls complete resume the layout
        this.ResumeLayout();

        // ================ !!! HERE !!! ====================
        // 1st: 256ms 2nd: 46ms 
        // ================ !!! HERE !!! ====================

        // Set the filter columns that are not found to invisible
        foreach (FilterColumnClass filter in _FilterColumns)
        {
            bool found = false;
            foreach (DataColumn col in _AllData.Columns)
            {
                if (filter.ColumnName == col.ColumnName)
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                filter.FilterCheckControl.Visible = false;
            }
        }
        // 1st: 0ms 2nd: 0ms

        // Create a datatable for the header with empty strings
        DataTable header = new DataTable("FilterRow");

        // Add all columns of the datasource to it
        foreach (DataColumn col in _AllData.Columns)
        {
            header.Columns.Add(new DataColumn(col.ColumnName, typeof(string)));
        }

        // 1st: 0ms 2nd: 0ms

        // Create a empty row for it
        DataRow filterrow = header.NewRow();

        foreach (FilterColumnClass filter in _FilterColumns)
        {
            // If the filter control is visible we have to take over the old value of that filter.
            if (filter.FilterCheckControl.Visible)
            {
                try
                {
                    filterrow.ItemArray[filter.Index] = filter.FilterTextString;
                }
                catch (Exception ex)
                {
                    PublicFunctions.ReportBug(ex, _Shared);
                }
            }
        }

        // Add the filter row to the header datatable
        header.Rows.Add(filterrow);

        // 1st: 0ms 2nd: 0ms

        // ================ !!! HERE !!! ====================
        // Assign the header datatable to the grid
        gridHeader.DataSource = header; 
        // ================ !!! HERE !!! ====================
        // THIS IS REALLY SLOW 1st: 162ms 2nd: 171ms
        // The header datatable contains 1 row

        // TOTAL 1st: 418ms 2nd: 217ms
    }

    private void SetDoubleBuffered(DataGridView grid, bool setting)
    {
        Type dgvType = grid.GetType();
        PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
        pi.SetValue(grid, setting, null);
    }
}





我希望你们能看到大量的代码。



I hope you guys can see through the big amount of code.

推荐答案

我找到了解决方案!!!!



将数据表数据绑定到网格的主要原因是这么慢是因为绘制列名需要很多时间。简单的代码行:

I'VE FOUND THE SOLUTION!!!!

The main reason that databinding a datatable to a grid is so slow is because the drawing of the columnnames takes alot time. The simple line of code:
gridHeader.ColumnHeadersVisible = false;



加速数据绑定过程 180-300 ms 4 ms !!!



现在这可能不是很好的解决方案,因为tablecolumns对于数据的显示非常重要,但对我来说这不是问题,因为我已经创建了一些我放在列名称上的自定义控件。因此,通过禁用列名,我自己的列名控件仍然存在,并让用户了解哪个列是什么。


Speeds up the databinding process from 180-300 ms to 4 ms!!!

Now this might not be a good solution for you because the tablecolumns are quite important for the showing of data, but for me it's not a problem as I already create some custom controls that I place over the column names. So by disabling the column names, my own column-name-controls remain present and give the user insight in which column is what.


为什么这个.spendpendlay()然后resumelayout( )在执行标头数据的数据源设置之前?如果您暂停所有数据源设置的布局会发生什么?
Why do you this.suspendlayout() and then resumelayout() before performing the datasource setting of the header datatable? What happens if you suspend layout for all of the data source setting?


我还要提到 SuspendLayout()



此外,在(非常)快速测试中,更改:

I was going to mention the SuspendLayout() thing too.

Also, in a (very) quick test, changing:
FilterColumnClass new_filter = new FilterColumnClass();
  new_filter.ColumnName = col.ColumnName;
  new_filter.Index = index;
  new_filter.FilterCheckControl.Visible = true;



to:


to:

FilterColumnClass new_filter = new FilterColumnClass(col.ColumnName, index);





并设置 new_filter.FilterCheckControl.Visible = true; 在构造函数中,因为你为所有实例都这样做。



给予边际(3%ish)改进。



该代码确实应该通过分析器运行,以便更准确地识别瓶颈。



and setting new_filter.FilterCheckControl.Visible = true; in the constructor, since you do it for all instances.

Gave a marginal (3% ish) improvement.

THe code really should be run through a profiler though to more accurately identify the bottleneck.


这篇关于DataGridView设置DataSource太慢了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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