1. 程式人生 > >WPF學習之使用DataGrid

WPF學習之使用DataGrid

在WinForm或者WebForm中我們有一大批的Grid控制元件供我們使用,DataGridView,GridView,Repeater等等,這樣的網格資料空間給我們提供了極大的方便去讓資料以可定義的方式顯示並提供諸如導航,分頁,排序,過濾,資料更新等附加操作 ,而程式設計師所需要付出的卻很少。但在WPF中我們通常並不具備這麼優越的網格控制元件,而要做到這些除了用Grid.RowDefinitions和Grid.ColumnDefinitions配合一起造出一個Grid,或者利用ListView控制元件的ListView.GridView檢視,我們好像別無選擇。而且對於這些的使用並不具備像WinForm中那麼強大的功能,更多的需要程式設計師去控制資料的展現並提供附加操作的實現。微軟新近在CodePlex上釋出了WPF Toolkit,它就提供了WPF版本的DataGrid,一如既往的強大和易用,今天我們就一起來體驗一下這個強大的控制元件。

1. Start with DataGrid

WPF開發團隊專門重寫了一套DataGrid讓其可以運行於WPF之上,在http://www.codeplex.com/wpf 你可以獲得這個ToolKit的原始碼及其DLL檔案,具體是如何寫這些控制元件的,作為大家對WPF感興趣的,您需要去研究一下。因為很值得去研究。

獲得WPFToolkit.dll檔案後,我們在專案中新增對它的引用,在XAML中因為WPFToolKit的名稱空間不屬於WPF和.NET Framework預設的對映名稱空間內的一部分,所以需要在XAML檔案中首先宣告對其名稱空間的引用,繼而宣告一個例項物件。為了簡化我們的程式,這裡我們將讓Grid控制元件自己根據資料來源生成列:AutoGenerateColumns=”True”.

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WpfToolkit"

Loaded="Window_Loaded"

Title="Simple DataGrid" Height="600" Width="800">

/>

和普通WinForm控制元件一樣,接下來需要對其資料來源賦予資料。不同的是和所有WPF資料控制元件相同,這裡的資料來源屬性為ItemSource.之前我們宣告過Window的Loaded事件,直接在其中賦值即可:

private void Window_Loaded(object sender, RoutedEventArgs e)

{

using (NorthwindDataContext dc = new NorthwindDataContext())

{

NorthwindDataGrid.ItemsSource = dc.Customers.ToList();

}

}

注意示例中使用了LINQ來獲取Northwind資料庫中Customers表的資料並賦予DataGrid作為資料來源。和在ASP.NET/WinForm中相同,將List<>資料直接賦予ItemSource屬性即可。這就是你需要做的所有一切,簡單吧?看看您的成果.

哦,真的很Nice,因為我們僅僅只寫了2行程式碼而已,就完成了這麼些功能,太美妙了。迫不及待想試試了吧?快來吧。

2. DataGrid Styling

你肯定不想讓你的資料展現給人一個過於樸素的感覺吧?讓你的Grid變得更漂亮些,這是你必須做的,因為你在用WPF,你不能讓客戶覺得你是用普通的.NET程式去糊弄他的。當然,提供給客戶一個煥然一新而美妙的User eXperience不是一個程式設計師就能做好的,那需要Designer的相當貢獻。這裡,只是提供個思路給大家去改變DataGrid的預設面貌。

很多時候我們對於每條記錄都有一個主鍵去標明,而在業務邏輯中我們也用這些鍵值去標識一個事物。為了讓這些更生動的表現出來,可以考慮加亮或者以特殊的方式顯示出來。對於Customer來說,CustomerID就是能唯一標識這個客戶的鍵值,我們為了更方便,將其以不同的風格顯示。因為在WPF中Binding是我們最喜歡去用的東西之一,特別是你在需要處理與資料更新有關的東西時,它會幫助你很多。我們將某個列的是否主鍵(唯一標識物件)設為一個屬性IsPrimaryKey,然後利用DataTrigger(因為是.NET Property)就可以動態更新資源。對於每一列,你都可以通過這個屬性去判斷是否需要去以特定風格顯示。

DataGrid中的每一列是一個DataGridColumn物件,而每個DataGridColumn對應到最小又是一個DataGridCell物件。IsPrimaryKey是需要和列對其的,而對於這列的風格的設定缺需要影響到每一個單元格。因為DataGridTextColum,或者DataGridComboBoxColumn等都不具有這些自定義屬性,所以我們需要宣告一個繼承於這個特定列型別的類來實現我們的自定義屬性:

public class CustomColumn : DataGridTextColumn

{

public bool IsPrimaryKey

{

get { return (bool)GetValue(IsPrimaryKeyProperty); }

set { SetValue(IsPrimaryKeyProperty, value); }

}

public static readonly DependencyProperty IsPrimaryKeyProperty =

DependencyProperty.Register("IsPrimaryKey", typeof(bool), typeof(DataGridColumn), newFrameworkPropertyMetadata(false, null, null));

}

在給DataGrid繫結資料時你需要通知這些特定的列根據資料來源中的某個屬性也罷,還是程式碼判斷也罷去設定IsPrimaryKey的值。在這裡如果你不使用AutoGenerateColumns,那對於每個列你可以設定Binding,並新增Converter來給IsPrimaryKey賦值。這個很容易,這裡我們介紹另外一種方法,在後臺程式碼裡賦值。因為使用AutoGenerateColumns,所以我們可以在AutoGeneratingColumn事件中來做這些事情:

private void DataGrid_Standard_AutoGeneratingColumn(object sender,DataGridAutoGeneratingColumnEventArgs e)

{

CustomColumn customColumn = new CustomColumn();

customColumn.CanUserSort = e.Column.CanUserSort;

customColumn.Header = e.Column.Header;

customColumn.DataFieldBinding = (e.Column as DataGridBoundColumn).DataFieldBinding;

DataGrid dg = sender as DataGrid;

Table orderTable = dg.ItemsSource as Table;

MetaModel metaModel = orderTable.Context.Mapping.MappingSource.GetModel(orderTable.Context.GetType());

MetaTable metaTable = metaModel.GetTable(typeof(Order));

foreach (MetaDataMember identityMember in metaTable.RowType.IdentityMembers)

{

if (identityMember.MappedName == e.Column.Header.ToString())

{

customColumn.IsPrimaryKey = true;

break;

}

}

e.Column = customColumn;

}

這段程式碼去資料庫中去查詢當前列是否是Primary Key,如果是將生成一個自定義的Column型別(繼承於DataTextColumn)並將這個例項賦予當前列的例項物件,也就是說如果是Primary key那當前列就是CustomColumn,就具有IsPrimaryKey屬性。我們的目的是要給主鍵新增風格,下邊就來給他們定義樣式了。記住剛才說的列的樣式的影響實際上每個單元格的樣式組成的,你就不難理解下邊的樣式定義:

執行你的程式看看有沒有效果呢?是不是看上去好些了?但這不是全部,你仍然需要更好的去為這些列做你自己的樣式修改。或許,你可以自己試試值轉換器(在前邊的文章中講過),兩者配合會更好些。

DataGrid提供資料編輯,新增資料,刪除資料這些功能,其實這和DataGrid的工作方式是分不開的。DataGrid在背後是由IEditableCollectionView來支援的,而ListCollectionViewBindingListCollectionView兩個類實現了對這個介面的支援。簡單來說,ListCollectionView是為ItemsControl的資料來源建立檢視,你可以在檢視上來應用Group,Sort, Update等更高階的功能。BindingListCollectionView提供了為ADO.NET DataView對ItemsControl生成表現檢視的方法。在編輯某行某列時,對錶格中資料的特殊標註會很有效。這是根據IsEditing依賴屬性來做觸發器源併產生相應樣式變更通知的:

很清楚的你會知道你在編輯哪條紀錄的哪個值:

3. 控制更多Data Grid的視覺表現

有時候讓使用者可以隨意更改DataGrid的某些表現方式更具有親和力,比如背景顏色,GridLine等等,而這一切因為繫結的緣故讓你很省心。因為都是依賴屬性,應用TwoWay繫結模式,WPF會自動搞定這一切,一段程式碼加入到XAML中試試它能做到什麼?

All

Horizontal

None

Vertical

也許你已經看出來了,改變DataGrid的各種屬性及可視樣式。沒錯,就是這麼簡單。Binding幫我們搞定了一切,而這都是你的傑作。Nice:)

4. Column的表現及DataGridComboBoxColumn

想過給列定義某些附加操作嗎?比如讓各個列可以隨意調換位置,改變其BackGround,或者前景色?或者。。。。看看下邊這個示例吧,它能幫到你什麼嗎?

在CustomColumn中宣告IsHighLighted屬性:

public bool IsHighlighted

{

get { return (bool)GetValue(IsHighlightedProperty); }

set { SetValue(IsHighlightedProperty, value);}

}

public static readonly DependencyProperty IsHighlightedProperty =

DependencyProperty.Register("IsHighlighted", typeof(bool), typeof(DataGridColumn), newFrameworkPropertyMetadata(true, null, null));

在XAML中宣告CustomColum列並定義Header模板和繫結:

Header="Custom Column"

DataFieldBinding="{Binding Path=FirstName}">

IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Typedg:DataGridColumnHeader}},

Path=(Column).(local:CustomColumn.IsHighlighted),

Mode=OneWayToSource}"

Content="Highlight Cells (special)"/>

看到結果了吧?很美妙,但過程很簡單,對吧?你可以用Converter來幫助你一起做些事情,而且對於一些諸如IsFrozen等這些Column自帶屬性你並不用自定義來實現,不是更簡單嗎?

DataGridComboBoxColumn它總是能夠幫助我們做很多事情,直接上程式碼吧,你發現它簡直太容易了。

Header="Cake"

DataFieldBinding="{Binding Path=Cake}">

Chocolate

Vanilla

對於編輯時產生一個可選列表,你也可以通過定義編輯模板來產生,就像在ASP.NET中的GridView的TemplateColumn一樣。這樣在很多時候可能更有用,因為對編輯模板中的ComboBox控制元件來說你可以通過Binding給ItemSource來繫結資料,這樣更符合你的應用場景。

Images/02.jpg

Images/03.JPG

Images/04.jpg

Images/05.jpg

Images/06.jpg

僅僅是DataGird的初步介紹,獲取示例程式碼來看個究竟吧:)