2013-02-18 66 views
0

我們已經將一堆綁定到BindingSource的DataGridView綁定到一個SortableBindingList。因爲這個控件是一個巨大的使用痛苦,我試圖在它周圍編寫一個簡單的包裝器,以便大量與網格相關的代碼可以變得更加可重用。通過使其成爲一個通用類,GridWrapper,這也提供了類型安全優勢和喜歡把自己所選擇的項目共同的東西「無噪聲」碼:如何跟蹤數據網格視圖中的排序

/// <summary> 
/// Gets the item bound to the selected row if and only if exactly one row is selected; otherwise null. 
/// </summary> 
public T SelectedItem 
{ 
    get 
    { 
     var rows = grid.SelectedRows; 
     return (rows.Count == 1 ? rows[0].DataBoundItem as T : null); 
    } 
} 

這將導致代碼像

var selectedCustomer = Customers.SelectedItem; 

這當然比寫作和讀取更容易

Customer selectedCustomer; 
if (customersGridView.SelectedRows.Count == 1) 
    selectedCustomer = (Customer)customersGridView.SelectedRows[0].DataBoundItem; 

並且達到相同。無論如何,這麼多爲什麼我正在寫一個包裝。我的問題與其他的東西有關,我現在要去做。

我希望包裝能夠知道網格的默認排序應該是什麼,但也跟蹤排序用戶執行。這適用於諸如主細節網格之類的東西,我希望在主細胞網格中選擇另一個項目時彈回細節網格。所以,如果我的欄3降序排序過的細節,我希望用戶代碼做一些像

Detail.Bind(GetDetailData(Master.SelectedItem)); 

其中詳細是詳細信息網格包裝,師父是主電網的包裝。

要做到這一點,我開始用一個簡單的類來存儲排序狀態:

public class SortInfo 
{ 
    public SortInfo(DataGridViewColumn col) 
    { 
     this.Column = col; 
     this.Direction = ListSortDirection.Ascending; 
    } 

    public DataGridViewColumn Column; 
    public ListSortDirection Direction; 
} 

的包裝附加一個處理程序,以網格的排序事件,以跟蹤用戶的排序。我也有一個排序的方法來我的狀態對象申請排序到網格:

void Sort() 
{ 
    if (GetBoundList() != null) 
     grid.Sort(sortInfo.Column, sortInfo.Direction); 
} 

很簡單。 (GetBoundList通過BindingSource返回綁定到網格的SortableBindingList,如果不可能,則返回null)

現在的問題:如果我在網格被綁定時調用Sort,我得到一個InvalidOperationException,抱怨網格只能在綁定到IBindingList時排序!這很奇怪,至少可以這樣說,因爲我在調用DataGridView.Sort之前做的最後一件事情就是檢查情況是如此。

爲了解決上述問題(難以做任何事情,只要我不明白!)我試圖改爲將處理程序附加到DataBindingComplete並在那裏調用Sort。這會導致另一個問題:當用戶嘗試對網格進行排序時,SortableBindingList會重置綁定,從而導致DataBindingComplete觸發。由於這發生在列表的ApplySortCore方法(我認爲)返回之前,它也發生在Sorted事件之前。因此,當用戶試圖按Column2進行排序時,我的包裝將按Column1排序(或者默認情況下),從而覆蓋用戶排序。效果很奇怪,因爲儘管顯示相同排序箭頭的列標題相同,但您可能會看到一些行移動;這是因爲的項目是正在排序,它們只是按照用戶要求先排序,然後再按默認排序才能真正注意到。結果的順序可能是,如果排序的列包含幾個相等的值,與原始順序不同...

在我看來,問題與使用包裝本身無關。也就是說,如果我試圖在窗體中使用我的所有代碼來做到這一點,我就會遇到完全相同的問題,我無法輕鬆地重用它。

所以問題是如果有人有一個想法如何解決它。正如我正在寫這個發生在我身上:只有綁定數據時,綁定DataBindingComplete的處理程序,當它觸發時,排序網格,然後分離處理程序。實際上,在我看來這應該解決這兩個問題。

但是,既然我已經寫了一噸,我會發布這個呢!如果上述想法確實起作用,我會將其作爲答案發布。

同時,這裏是在使「用戶排序是被重寫」的形式形式的包裝代碼:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows.Forms; 

namespace Snippets.SortableGrid 
{ 
    public class GridWrapper<T> where T: class 
    { 
     public GridWrapper(DataGridView grid, DataGridViewColumn defaultSortColumn) 
     { 
      this.sortInfo = new SortInfo(defaultSortColumn); 
      this.grid = grid; 
      grid.Sorted += new EventHandler(grid_Sorted); 
      grid.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(grid_DataBindingComplete); 
     } 


     public void Bind(IEnumerable<T> data) 
     { 
      BindingSource bs = grid.DataSource as BindingSource; 
      if (bs == null) 
       grid.DataSource = bs = new BindingSource(); 

      bs.DataSource = new SortableBindingList<T>(data); 
     } 


     /// <summary> 
     /// Gets the item bound to the selected row if and only if exactly one row is selected; otherwise null. 
     /// </summary> 
     public T SelectedItem 
     { 
      get 
      { 
       var rows = grid.SelectedRows; 
       return (rows.Count == 1 ? rows[0].DataBoundItem as T : null); 
      } 
     } 


     SortInfo sortInfo; 
     DataGridView grid; 


     ListSortDirection ToListSortDirection(SortOrder order) 
     { 
      return (order == SortOrder.Descending ? ListSortDirection.Descending : ListSortDirection.Ascending); 
     } 


     void Sort() 
     { 
      if (GetBoundList() != null && sortInfo.Column != null) 
       grid.Sort(sortInfo.Column, sortInfo.Direction); 
     } 


     SortableBindingList<T> GetBoundList() 
     { 
      var bs = grid.DataSource as BindingSource; 
      return (bs != null ? (bs.DataSource as SortableBindingList<T>) : null); 
     } 


     void grid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) 
     { 
      Sort(); 
     } 


     void grid_Sorted(object sender, EventArgs e) 
     { 
      sortInfo.Column = grid.SortedColumn; 
      sortInfo.Direction = ToListSortDirection(grid.SortOrder); 
     } 
    } 


    public class SortInfo 
    { 
     public SortInfo(DataGridViewColumn col) 
     { 
      this.Column = col; 
      this.Direction = ListSortDirection.Ascending; 
     } 

     public DataGridViewColumn Column; 
     public ListSortDirection Direction; 
    } 
} 
+0

你的SortableBindingList確實實現了'IBindingList',我希望? – Mr47 2013-02-18 15:48:04

回答

0

我已經不完全與此代碼住了很長時間,但這個想法似乎上班!

更改綁定到

public void Bind(IEnumerable<T> data) 
{ 
    BindingSource bs = grid.DataSource as BindingSource; 
    if (bs == null) 
     grid.DataSource = bs = new BindingSource(); 

    grid.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(grid_DataBindingComplete); 
    bs.DataSource = new SortableBindingList<T>(data); 
} 

和處理程序

void grid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) 
{ 
    Sort(); 
    grid.DataBindingComplete -= grid_DataBindingComplete; 
} 

我現在可以重新綁定「網格」(即調用GridWrapper.Bind一些新的數據)和電網並「記住「這是排序狀態。這就是我想要開始的一切。

-

我真的不明白,爲什麼在2013年應該有必要在所有編寫任何代碼,這種事情。我們得到的控制應該更好地支持像這種內置的常見和有用的東西。

雖然我在咆哮,但我也非常懷疑整個事件,事件總是最好的方式來鉤入事情。在一個人們嘗試使用MVC或MVP或類似軟件的世界中,爲什麼不給我們更緊密的對齊 - 更類似於這種包裝 - 開箱即用?如果控件允許你至少在某些方面把它們看作是數據結構,那麼實現一個視圖會容易得多。