2013-06-13 3 views
4

Есть ли способ сортировки DataGridView в виртуальном режиме?C# 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, но не могу заставить ее работать.

Может ли кто-нибудь указать, как можно сортировать столбцы в виртуальном режиме, и если да, то как бы мне это нужно?

Большое спасибо!

ответ

4

Хорошо, я решил это сейчас. Используя пример microsoft (http://msdn.microsoft.com/de-de/library/ms171624.aspx), я модифицировал класс DataRetrieverLog для получения дополнительного значения в конструкторе: Имя столбца для сортировки (это включает в себя направление сортировки, например «Имя ASC»). Для тех, кто заинтересован, here's модифицированный код:

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

В дополнение к этому, я Реализована ColumnHeaderMouseClick-событие в моей Gridview. В этом случае я получаю имя столбца и направление сортировки, а затем перезагружаю все базовые данные таблицы, создавая новый экземпляр DataRetrieverLog и передавая имя столбца + направление сортировки. Затем я просто обновляю Gridview: gridView.Refresh()

Thats it.

EDIT (20.07.2015):

Чтобы сделать вещи немного яснее, here's полный путь получения Сортируемый 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-класса и вызвать метод в Helper-класса из класса Main GUI-:

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 и реализовать событие ColumnHeaderMouseClick для сетки в классе Main. Счастливое кодирование :-)

+0

Я стараюсь следовать вашей инструкции, чтобы сортировать вместе с виртуальным режимом здесь, мой url http: // stackoverflow.com/questions/31458197/how-to-sort-datagridview-data-when-virtual-mode-enable, если возможно, посмотрите и ответьте, как добиться того, чего я хочу. спасибо – Mou

+0

привет! Извини за поздний ответ. Мне пришлось много делать последние несколько дней. Следуя вашей ссылке, я вижу, что вы решили проблему. Рад, что я мог бы помочь - в смысле indirekt, предоставив мой код выше :-) – Chris

+0

может помочь мне поднять событие в переднем конце, когда начнется загрузка данных и сетка. Мне нравится поднимать два события, когда загрузка данных начинается и поднять другую, когда заканчивается загрузка данных сетки. нужна ваша помощь. спасибо – Mou

Смежные вопросы