C#DataGridView虚拟模式:启用排序

时间:2013-06-13 12:28:33

标签: c# sorting datagridview gridview-sorting virtualmode

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

我在虚拟模式下实现了一个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,但无法让它发挥作用。

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

提前多多感谢!

1 个答案:

答案 0 :(得分:4)

好的,我现在解决了这个问题。使用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的完整方法。自从我玩这个游戏以来已经有一段时间了,所以我希望我能说清楚它让它运转起来并且没有错过任何事情。需要三个助手类来实现这一目标:

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事件非常重要。快乐的编码: - )