【问题标题】:C# DataGridView virtual mode: Enable sortingC# DataGridView 虚拟模式:启用排序
【发布时间】:2013-06-13 12:28:33
【问题描述】:

有没有办法在虚拟模式下对 DataGridView 进行排序?

我已经按照这个 microsoft 示例在虚拟模式下实现了 Gridview:http://msdn.microsoft.com/de-de/library/ms171624.aspx。我还修改了示例以便能够将数据写入数据库。这很好用,虚拟模式大大提高了速度,但我的客户需要对列进行排序。

在网上搜索了一段时间后,我找到了链接 http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/25b3f7c3-95b6-4c49-802b-b5a2a62915ac ,但无法正常工作。

谁能指出如果有办法在虚拟模式下对列进行排序,如果有,我该怎么做?

提前非常感谢!

【问题讨论】:

    标签: c# sorting datagridview gridview-sorting virtualmode


    【解决方案1】:

    好的,我现在已经解决了。使用 microsoft 示例 (http://msdn.microsoft.com/de-de/library/ms171624.aspx) 我修改了 DataRetrieverLog 类以在构造函数中接收一个附加值:要排序的列名(这包括排序方向,例如“Name ASC”)。对于任何感兴趣的人,这是修改后的代码:

    public class DataRetrieverLog : IDataPageRetriever
    {
        private string tableName;
        private string sortColumn;
        private SqlCommand command;
        private DataTable table;
        private SqlDataAdapter adapter;
    
        public DataRetrieverLog(string connectionString, string tableName, string sortColumn)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            connection.Open();
            command = connection.CreateCommand();
            this.tableName = tableName;
            this.sortColumn = sortColumn;
        }
    
        private int rowCountValue = -1;
    
        public int RowCount
        {
            get
            {
                // Return the existing value if it has already been determined. 
                if (rowCountValue != -1)
                {
                    return rowCountValue;
                }
    
                // Retrieve the row count from the database.
                command.CommandText = "SELECT COUNT(*) FROM " + tableName;
                rowCountValue = (int)command.ExecuteScalar();
                return rowCountValue;
            }
        }
    
        private DataColumnCollection columnsValue;
    
        public DataColumnCollection Columns
        {
            get
            {
                // Return the existing value if it has already been determined. 
                if (columnsValue != null)
                {
                    return columnsValue;
                }
    
                // Retrieve the column information from the database.
                command.CommandText = "SELECT * FROM " + tableName;
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = command;
                DataTable table = new DataTable();
                table.Locale = System.Globalization.CultureInfo.InvariantCulture;
                adapter.FillSchema(table, SchemaType.Source);
                columnsValue = table.Columns;
                return columnsValue;
            }
        }
    
        private string commaSeparatedListOfColumnNamesValue = null;
    
        private string CommaSeparatedListOfColumnNames
        {
            get
            {
                // Return the existing value if it has already been determined. 
                if (commaSeparatedListOfColumnNamesValue != null)
                {
                    return commaSeparatedListOfColumnNamesValue;
                }
    
                // Store a list of column names for use in the 
                // SupplyPageOfData method.
                System.Text.StringBuilder commaSeparatedColumnNames =
                    new System.Text.StringBuilder();
                bool firstColumn = true;
                foreach (DataColumn column in Columns)
                {
                    if (!firstColumn)
                    {
                        commaSeparatedColumnNames.Append("], [");
                    }
                    else
                    {
                        commaSeparatedColumnNames.Append("[");
                    }
    
                    commaSeparatedColumnNames.Append(column.ColumnName);
                    firstColumn = false;
                }
                commaSeparatedColumnNames.Append("]");
                commaSeparatedListOfColumnNamesValue =
                    commaSeparatedColumnNames.ToString();
                return commaSeparatedListOfColumnNamesValue;
            }
        }
    
        // Declare variables to be reused by the SupplyPageOfData method. 
        private string columnToSortBy;
    
    
        public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
        {
    
    
    
            // Store the name of the ID column. This column must contain unique  
            // values so the SQL below will work properly. 
            if (columnToSortBy == null)
            {
                columnToSortBy = this.Columns[0].ColumnName;
            }
    
            if (!this.Columns[columnToSortBy].Unique)
            {
                throw new InvalidOperationException(String.Format(
                    "Column {0} must contain unique values.", columnToSortBy));
            }
    
    
    
            // Retrieve the specified number of rows from the database, starting 
            // with the row specified by the lowerPageBoundary parameter.
            String text = "Select Top " + rowsPerPage + " " +
                CommaSeparatedListOfColumnNames + " From " + tableName +
                " WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
                lowerPageBoundary + " " + columnToSortBy + " From " +
                tableName + " Order By " + sortColumn +
                ") Order By " + sortColumn;
            command.CommandText = text;
    
            adapter = new SqlDataAdapter(text, GUI.dictSettings["connectionString"]);
            SqlCommandBuilder commandBuilder = new SqlCommandBuilder(adapter);
    
            table = new DataTable();
            table.Locale = System.Globalization.CultureInfo.InvariantCulture;
            adapter.Fill(table);
    
            return table;
        }
    
        public DataTable getDataTable()
        {
            return table;
        }
    
        public SqlDataAdapter getAdapter()
        {
            return adapter;
        }
    }
    

    除此之外,我还在我的 Gridview 中实现了 ColumnHeaderMouseClick-Event。在那我得到列名和排序方向,然后通过创建一个新的 DataRetrieverLog 实例并传递列名+排序方向来重新加载所有基础表数据。接下来我只刷新Gridview:gridView.Refresh()

    就是这样。

    编辑(20.07.2015):

    为了让事情更清楚一点,这里是让可排序的 DataGridView 在虚拟模式下工作的完整方法。自从我玩这个已经有一段时间了,所以我希望我能说得足够清楚,让你让它工作并且没有错过任何东西。需要三个 Helper 类才能使其工作:

    IDataPageRetriever.cs:

    using System.Data;
    using System.Data.SqlClient;
    
    namespace ASC.Code.Forms.Helper
    {
        public interface IDataPageRetriever
        {
            DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
    
            SqlDataAdapter getAdapter();
        }
    }
    

    CacheAll.cs:

    using System.Data;
    using System.Data.SqlClient;
    
    namespace ASC.Code.Forms.Helper
    {
        public class CacheAll
        {
            private static int RowsPerPage;
    
            // Represents one page of data.   
            public struct DataPage
            {
                public DataTable table;
                public SqlDataAdapter adapter;
                private int lowestIndexValue;
                private int highestIndexValue;
    
                public DataPage(DataTable table, SqlDataAdapter adapter, int rowIndex)
                {
                    this.table = table;
                    this.adapter = adapter;
                    lowestIndexValue = MapToLowerBoundary(rowIndex);
                    highestIndexValue = MapToUpperBoundary(rowIndex);
                    System.Diagnostics.Debug.Assert(lowestIndexValue >= 0);
                    System.Diagnostics.Debug.Assert(highestIndexValue >= 0);
                }
    
                public int LowestIndex
                {
                    get
                    {
                        return lowestIndexValue;
                    }
                }
    
                public int HighestIndex
                {
                    get
                    {
                        return highestIndexValue;
                    }
                }
    
                public static int MapToLowerBoundary(int rowIndex)
                {
                    // Return the lowest index of a page containing the given index. 
                    return (rowIndex / RowsPerPage) * RowsPerPage;
                }
    
                private static int MapToUpperBoundary(int rowIndex)
                {
                    // Return the highest index of a page containing the given index. 
                    return MapToLowerBoundary(rowIndex) + RowsPerPage - 1;
                }
    
                public DataTable getTable()
                {
                    return this.table;
                }
    
                public SqlDataAdapter getAdapter()
                {
                    return this.adapter;
                }
            }
    
            private DataPage[] cachePages;
            private IDataPageRetrieverAll dataSupply;
    
            public CacheAll(IDataPageRetrieverAll dataSupplier, int rowsPerPage)
            {
                dataSupply = dataSupplier;
                CacheAll.RowsPerPage = rowsPerPage;
                LoadFirstTwoPages();
            }
    
            // Sets the value of the element parameter if the value is in the cache. 
            private bool IfPageCached_ThenSetElement(int rowIndex,
                int columnIndex, ref string element)
            {
                if (IsRowCachedInPage(0, rowIndex))
                {
                    element = cachePages[0].table
                        .Rows[rowIndex % RowsPerPage][columnIndex].ToString();
                    return true;
                }
                else if (cachePages.Length > 1)
                {
                    if (IsRowCachedInPage(1, rowIndex))
                    {
                        element = cachePages[1].table.Rows[rowIndex % RowsPerPage][columnIndex].ToString();
                        return true;
                    }
    
                }
    
                return false;
            }
    
            public string RetrieveElement(int rowIndex, int columnIndex)
            {
                string element = null;
    
                if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
                {
                    return element;
                }
                else
                {
                    return RetrieveData_CacheIt_ThenReturnElement(
                        rowIndex, columnIndex);
                }
            }
    
            private void LoadFirstTwoPages()
            {
                DataTable table1 = dataSupply.SupplyPageOfData(DataPage.MapToLowerBoundary(0), RowsPerPage);
                SqlDataAdapter adapter1 = dataSupply.getAdapter();
    
                DataTable table2 = dataSupply.SupplyPageOfData(DataPage.MapToLowerBoundary(RowsPerPage), RowsPerPage);
                SqlDataAdapter adapter2 = dataSupply.getAdapter();
    
                cachePages = new DataPage[]{
                new DataPage(table1, adapter1, 0), 
                new DataPage(table2, adapter2, RowsPerPage)};
            }
    
            private string RetrieveData_CacheIt_ThenReturnElement(
                int rowIndex, int columnIndex)
            {
                // Retrieve a page worth of data containing the requested value.
                DataTable table = dataSupply.SupplyPageOfData(
                    DataPage.MapToLowerBoundary(rowIndex), RowsPerPage);
                SqlDataAdapter adapter = dataSupply.getAdapter();
    
                // Replace the cached page furthest from the requested cell 
                // with a new page containing the newly retrieved data.
                cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, adapter, rowIndex);
    
                return RetrieveElement(rowIndex, columnIndex);
            }
    
            // Returns the index of the cached page most distant from the given index 
            // and therefore least likely to be reused. 
            private int GetIndexToUnusedPage(int rowIndex)
            {
                if (rowIndex > cachePages[0].HighestIndex &&
                    rowIndex > cachePages[1].HighestIndex)
                {
                    int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex;
                    int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex;
                    if (offsetFromPage0 < offsetFromPage1)
                    {
                        return 1;
                    }
                    return 0;
                }
                else
                {
                    int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex;
                    int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex;
                    if (offsetFromPage0 < offsetFromPage1)
                    {
                        return 1;
                    }
                    return 0;
                }
    
            }
    
            // Returns a value indicating whether the given row index is contained 
            // in the given DataPage.  
            private bool IsRowCachedInPage(int pageNumber, int rowIndex)
            {
                return rowIndex <= cachePages[pageNumber].HighestIndex &&
                    rowIndex >= cachePages[pageNumber].LowestIndex;
            }
    
            public DataPage[] getCachePages()
            {
                return cachePages;
            }
    
        }
    }
    

    …和 DataRetrieverAll.cs 类:

    using System;
    using System.Data.SqlClient;
    using System.Data;
    
    namespace ASC.Code.Forms.Helper
    {
        public class DataRetrieverAll : IDataPageRetrieverAll
        {
            private string tableName;
            private string sortColumn;
            private SqlCommand command;
            private DataTable table;
            private SqlDataAdapter adapter;
    
            public DataRetrieverAll(string connectionString, string tableName, string sortColumn)
            {
                SqlConnection connection = new SqlConnection(connectionString);
                connection.Open();
                command = connection.CreateCommand();
                this.tableName = tableName;
                this.sortColumn = sortColumn;
            }
    
            private int rowCountValue = -1;
    
            public int RowCount
            {
                get
                {
                    // Return the existing value if it has already been determined. 
                    if (rowCountValue != -1)
                    {
                        return rowCountValue;
                    }
    
                    // Retrieve the row count from the database.
                    command.CommandText = "SELECT COUNT(*) FROM " + tableName;
                    rowCountValue = (int)command.ExecuteScalar();
                    return rowCountValue;
                }
            }
    
            private DataColumnCollection columnsValue;
    
            public DataColumnCollection Columns
            {
                get
                {
                    // Return the existing value if it has already been determined. 
                    if (columnsValue != null)
                    {
                        return columnsValue;
                    }
    
                    // Retrieve the column information from the database.
                    command.CommandText = "SELECT * FROM " + tableName;
                    SqlDataAdapter adapter = new SqlDataAdapter();
                    adapter.SelectCommand = command;
                    DataTable table = new DataTable();
                    table.Locale = System.Globalization.CultureInfo.InvariantCulture;
                    adapter.FillSchema(table, SchemaType.Source);
                    columnsValue = table.Columns;
                    return columnsValue;
                }
            }
    
            private string commaSeparatedListOfColumnNamesValue = null;
    
            private string CommaSeparatedListOfColumnNames
            {
                get
                {
                    // Return the existing value if it has already been determined. 
                    if (commaSeparatedListOfColumnNamesValue != null)
                    {
                        return commaSeparatedListOfColumnNamesValue;
                    }
    
                    // Store a list of column names for use in the 
                    // SupplyPageOfData method.
                    System.Text.StringBuilder commaSeparatedColumnNames =
                        new System.Text.StringBuilder();
                    bool firstColumn = true;
                    foreach (DataColumn column in Columns)
                    {
                        if (!firstColumn)
                        {
                            commaSeparatedColumnNames.Append("], [");
                        }
                        else
                        {
                            commaSeparatedColumnNames.Append("[");
                        }
    
                        commaSeparatedColumnNames.Append(column.ColumnName);
                        firstColumn = false;
                    }
                    commaSeparatedColumnNames.Append("]");
                    commaSeparatedListOfColumnNamesValue =
                        commaSeparatedColumnNames.ToString();
                    return commaSeparatedListOfColumnNamesValue;
                }
            }
    
            // Declare variables to be reused by the SupplyPageOfData method. 
            private string columnToSortBy;
    
    
            public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
            {
    
    
    
                // Store the name of the ID column. This column must contain unique  
                // values so the SQL below will work properly. 
                if (columnToSortBy == null)
                {
                    columnToSortBy = this.Columns[0].ColumnName;
                }
    
                if (!this.Columns[columnToSortBy].Unique)
                {
                    throw new InvalidOperationException(String.Format(
                        "Column {0} must contain unique values.", columnToSortBy));
                }
    
    
    
                // Retrieve the specified number of rows from the database, starting 
                // with the row specified by the lowerPageBoundary parameter.
                String text = "Select Top " + rowsPerPage + " " +
                    CommaSeparatedListOfColumnNames + " From " + tableName +
                    " WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
                    lowerPageBoundary + " " + columnToSortBy + " From " +
                    tableName + " Order By " + sortColumn +
                    ") Order By " + sortColumn;
                command.CommandText = text;
    
                adapter = new SqlDataAdapter(text, GUI.dictSettings["connectionString"]);
                SqlCommandBuilder commandBuilder = new SqlCommandBuilder(adapter);
    
                table = new DataTable();
                table.Locale = System.Globalization.CultureInfo.InvariantCulture;
                adapter.Fill(table);
    
                return table;
            }
    
            public DataTable getDataTable()
            {
                return table;
            }
    
            public SqlDataAdapter getAdapter()
            {
                return adapter;
            }
        }
    }
    

    那么,如何填充网格?我使用另一个 Helper-Class 并从 Main GUI- 类调用 Helper-Class 中的一个方法:

    Main.cs:

    //...
    private String columnToSortByAll;
    
    public GUI()
    {
        InitializeComponent();
        init();
    }
    private void init()
    {
        helper = new GUIHelper(grid, this);
    
        //Tabellen mit Werten füllen
        fillTablesInit();
    }
    
    private void fillTablesInit()
    {
        helper.getData("(SELECT * FROM TOOL_materialSumme WHERE Display IS NULL OR Display = 1)a", "ID", asc_all); //asc_all = Boolean value, indicating sort direction asc /desc
    }
    
    private void grid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        String columnName = grid.Columns[e.ColumnIndex].Name;
    
        if (columnName.Equals(columnToSortBy))
        {
            if (asc_all) asc_all = false; else asc_all = true;
        }
        else
        {
            columnToSortBy = columnName;
            asc_all = true;
        }
    
        helper.getDataALL("(SELECT * FROM TOOL_materialSumme WHERE Display IS NULL OR Display = 1)a", columnToSortBy, asc_all);
    }
    
    private void grid_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
    {
        String actualValue = helper.getMemoryCache().RetrieveElement(e.RowIndex, e.ColumnIndex);
        e.Value = actualValue;
    }
    
    private void grid_DataError(object sender, DataGridViewDataErrorEventArgs e)
    {
        //MessageBox.Show(e.Context.ToString());
    }
    
    private void grid_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)
    {
        String newValue = "";
        if (e.Value != null) newValue = e.Value.ToString();
    
        int column = e.ColumnIndex;
    
        ASC.Code.Forms.Helper.CacheAll.DataPage[] pages = helper.getMemoryCache().getCachePages();
        DataTable[] tables = new DataTable[2];
        SqlDataAdapter[] adapters = new SqlDataAdapter[2];
    
        tables[0] = pages[0].getTable();
        tables[1] = pages[1].getTable();
    
        adapters[0] = pages[0].getAdapter();
        adapters[1] = pages[1].getAdapter();
    
        String id = grid.Rows[e.RowIndex].Cells["ID"].Value.ToString();
    
        for (int x = 0; x < tables.Length; x++)
        {
            for (int a = 0; a < tables[x].Rows.Count; a++)
            {
    
                String temp = tables[x].Rows[a][column].ToString();
                if (tables[x].Rows[a]["ID"].ToString() == id)
                {
                    tables[x].Rows[a][column] = newValue;
                    adapters[x].Update(tables[x]);
                    break;
                }
            }
        }
    
        grid.Refresh();
    }
    //...
    

    GUIHelper.cs:

    private DataGridView grid
    private CacheAll memoryCache;
    private DataRetrieverAll retriever;
    
    public GUIHelper(DataGridView grid, GUI gui)
    {
        this.gui = gui;
        this.grid = grid;
        init();
    }
    
    private void init()
    {
        //...
    }
    
    public void getData(string selectCommand, string sortColumn, Boolean asc_all)
    {
        grid.VirtualMode = true;
        try
        {
            if (asc_all) sortColumn = "["+sortColumn + "] ASC"; else sortColumn = "["+sortColumn + "] DESC";
    
            retriever = new DataRetrieverAll("Insert ConnectionString here...", selectCommand, sortColumn);
            memoryCache = new CacheAll(retriever, GUI.amountDatasets); //amountDatasets = Amount of Datasets per cached-page
    
            if (grid.Columns.Count == 0)
            {
                foreach (DataColumn column in retriever.Columns)
                {
                    grid.Columns.Add(column.ColumnName, column.ColumnName);
                }
            }
    
            grid.Rows.Clear();
            grid.RowCount = retriever.RowCount;
    
            grid.Refresh();
        }
        catch (SqlException)
        {
            MessageBox.Show("Connection could not be established. " +
                "Verify that the connection string is valid.");
            Application.Exit();
        }
    }
    
    public CacheAll getMemoryCache()
    {
        return memoryCacheAll;
    }
    

    嗯,我就是这样做的,而且效果很好。我最终没有使用它,但仍然有代码......设置 grid.VirtualMode = true 并在 Main 类中为网格实现 ColumnHeaderMouseClick 事件很重要。快乐编码:-)

    【讨论】:

    • 我尝试按照您的说明与虚拟模式一起排序这是我的网址stackoverflow.com/questions/31458197/… 如果可能的话,请查看并回答如何实现我想要的。谢谢
    • 嗨!对不起,迟到的答案。过去几天我有很多事情要做。按照你的链接,我看到你已经解决了这个问题。很高兴我能提供帮助 - 通过在上面提供我的代码以一种间接的方式:-)
    • 你能指导我如何在数据加载开始和网格填充结束时在前端引发事件。我喜欢在数据加载开始时引发两个事件,并在网格数据加载结束时引发另一个事件。需要你的帮助。谢谢
    • 嗨!看看我上面的编辑。我认为现在应该很好地解释一切。我已经有一段时间没有做这个了,所以我可能错过了一些细节 - 但你应该明白了:-)
    • 哦,我想我误读了您的问题:您在更改或编辑数据源之前和之后需要一个事件吗?嗯 - 那怎么样? stackoverflow.com/questions/4784598/… ...and DataGridView.DataBindingComplete 用于加载数据后?
    猜你喜欢
    • 2011-06-11
    • 2015-10-06
    • 1970-01-01
    • 2012-01-05
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    相关资源
    最近更新 更多