我們已經將一堆綁定到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;
}
}
你的SortableBindingList確實實現了'IBindingList',我希望? – Mr47 2013-02-18 15:48:04