WPF 核心庫未內建資料網格,早期開發者只能組合 ListView + GridView 或第三方控件。微軟於 CodePlex 發布 WPF Toolkit,提供功能完整、原生支援虛擬化、排序、分頁、編輯、樣式化的 Microsoft.Windows.Controls.DataGrid,並隨後整合入 .NET Framework 4.0 核心庫(System.Windows.Controls.DataGrid)。本文以 Toolkit 版本為例,API 兩者相容。
<Window xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WpfToolkit"
Loaded="Window_Loaded">
<dg:DataGrid x:Name="grid" AutoGenerateColumns="True"/>
</Window>
void Window_Loaded(object sender, RoutedEventArgs e) {
using (var dc = new NorthwindDataContext())
grid.ItemsSource = dc.Customers.ToList(); // IEnumerable 即可
}
自動產生欄位、支援點擊欄標排序、預設分頁(設定 PageSize 需配合 PagedCollectionView),零程式碼即擁有基礎 CRUD 體驗。
<dg:DataGrid x:Name="grid" AutoGenerateColumns="False">
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Header="客戶編號" Binding="{Binding CustomerID}" IsReadOnly="True"/>
<dg:DataGridComboBoxColumn Header="城市" SelectedItemBinding="{Binding City}"
ItemsSource="{Binding Source={StaticResource CityList}}"/>
<dg:DataGridTemplateColumn Header="操作">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="詳情" Click="Detail_Click" Tag="{Binding CustomerID}"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
DataGridComboBoxColumn 直接指定 ItemsSource(靜態資源或繫結集合),編輯時自動呈現下拉選單。DataGridTemplateColumn 區分 CellTemplate(檢視模式)與 CellEditingTemplate(編輯模式),支援任意複雜 UI。繼承 DataGridTextColumn 加入 IsPrimaryKey 依賴屬性:
public class CustomColumn : DataGridTextColumn {
public static readonly DependencyProperty IsPrimaryKeyProperty =
DependencyProperty.Register("IsPrimaryKey", typeof(bool), typeof(CustomColumn), new PropertyMetadata(false));
public bool IsPrimaryKey { get => (bool)GetValue(IsPrimaryKeyProperty); set => SetValue(IsPrimaryKeyProperty, value); }
}
在 AutoGeneratingColumn 事件中依據 LINQ to SQL 中繼資料判斷主鍵並替換欄位實例:
void grid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) {
var metaTable = dc.Mapping.GetTable(typeof(Customer));
var isPK = metaTable.RowType.IdentityMembers.Any(m => m.MappedName == e.Column.Header.ToString());
if (isPK) {
var col = new CustomColumn { Header = e.Column.Header, Binding = (e.Column as DataGridBoundColumn).Binding };
col.IsPrimaryKey = true;
e.Column = col;
}
}
CellStyle 使用 DataTrigger 綁定欄位屬性:
<Style x:Key="cellStyle" TargetType="dg:DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Column.(local:CustomColumn.IsPrimaryKey)}" Value="True">
<Setter Property="Background" Value="Tan"/>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<Trigger Property="IsEditing" Value="True">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
RowStyle 同理高亮編輯中列:
<Style x:Key="rowStyle" TargetType="dg:DataGridRow">
<Style.Triggers>
<Trigger Property="IsEditing" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
將 DataGrid 所有行為屬性(CanUserAddRows、CanUserDeleteRows、CanUserSortColumns、GridLinesVisibility、ColumnHeaderHeight 等)透過 CheckBox/ComboBox 雙向繫結暴露給使用者,零程式碼實現「使用者自訂網格外觀」:
<CheckBox Content="允許新增" IsChecked="{Binding ElementName=grid, Path=CanUserAddRows, Mode=TwoWay}"/>
<ComboBox SelectedItem="{Binding ElementName=grid, Path=GridLinesVisibility}">
<dg:DataGridGridLinesVisibility>All</dg:DataGridGridLinesVisibility>
<dg:DataGridGridLinesVisibility>Horizontal</dg:DataGridGridLinesVisibility>
<dg:DataGridGridLinesVisibility>Vertical</dg:DataGridGridLinesVisibility>
<dg:DataGridGridLinesVisibility>None</dg:DataGridGridLinesVisibility>
</ComboBox>
PagedCollectionView 並設定 PageSize,DataGrid 自動顯示分頁導航(需 Template 定制)。Repeater 並擴充 PageIndex/PageSize 的輕量控件,配合 NBearDataSource 或 SqlDataSource 實現伺服器端分頁,適合非表格式清單呈現。VirtualizingStackPanel.IsVirtualizing="True"(預設開啟),大量資料僅實例化可視列。RowHeight="28" 避免測量開銷。FrozenColumnCount="2" 鎖定左側欄位水平捲動時不動。ItemsSource 繫結 Task<List<T>> 或使用 BackgroundWorker,UI 執行緒不阻塞。| 現象 | 原因 | 解法 |
|---|---|---|
| 編輯後資料不回寫 | 實體未實作 INotifyPropertyChanged 或 Binding Mode 錯誤 | 實體繼承 ObservableObject;Binding 加 Mode=TwoWay |
| ComboBoxColumn 下拉無資料 | ItemsSource 繫結上下文錯誤(預設為 DataGrid DataContext) | 使用 StaticResource 或 RelativeSource 找正確來源 |
| 排序無效 | ItemsSource 非 ICollectionView 或未支援排序 | 包裝 ListCollectionView 或 DomainDataSource |
| 記憶體隨捲動增長 | CellTemplate 含未釋放事件訂閱或大型圖片 | 使用 WeakEventPattern;圖片設定 DecodePixelWidth |