一、前言
在WPF編程中,有時候我們使用DataGrid會需要在一個DataColumn中既有TextBox,也要有ComboBox或者TextBlock等其他數據顯示樣式。
這個時候我們就需要DataGridTemplateColumn去自定義我們的Column樣式,通過數據類型去判斷該信息是以TextBox顯示還是以ComboBox來顯示。
PS:初入WPF-MVVM模式編程的童鞋可以先了解一下what is MVVM
二、從數據庫出發
所謂兵馬未到,糧草先行。數據庫的字段應該明確告訴我們該條數據是哪個數據類型?是字符串型還是多選型?是否可編輯?
這些清晰的信息都能為我們之後的MVVM綁定帶來極大的便利。
數據庫的字段可以大致這樣:
1. ID
2. Keyword
3. Name
4. Value
5. ItemsValue (用來告知有哪些選擇項)
6. DataType (是字符串型,還是多選型,還是其他?)
7. IsAcceptInput (顯示在界面上後是否可編輯)
范例:
我們可以從上表看出,第1與第2條數據應該是TextBox顯示,而第3與第4條則是ComboBox顯示。
三、在代碼中准備好相應的枚舉
當我們准備完數據庫的數據時,在代碼中我們會用Dapper, EF, Nhibernate等等將數據庫字段映射為相應的數據類型:
public Class ExampleInfoData { public long Id {get;set;} public string Keyword {get;set;} public string PropertyName {get;set;} public DataItem PropertyValue {get;set;} public List<DataItem> ItemValues {get;set;} public int DataType {get;set;} public bool IsAcceptInput {get;set;} }
這裡我們看到有個類叫 DataItem, 這是為了什麼呢?我們看下范例:
public class DataItem { public string DisplayName { get; set; } //顯示值 用來在界面上顯示用的 public string ItemValue { get; set; } //原始值 //這個方法是為了能讓界面正常顯示從數據庫讀取的值,不用這個方法的話就算數據庫中存有默認值,綁定之後它也不會正常顯示在界面上 public override bool Equals(object obj) { if (!(obj is DataItem)) { return false; } DataItem di = obj as DataItem; return di != null && di.ItemValue == ItemValue; } public override int GetHashCode() //配合Equals方法,兩者一起使用 { return ItemValue.GetHashCode(); } }
對於多選型的數據,我們也應該准備好相應的枚舉值,有了Description能方便的給之前的DisplayName提供值。
public enum ProjectType { [Description("類型一")] T_1 = 0, [Description("類型二")] T_2 = 1, [Description("類型三")] T_3 = 2, } public enum MemberType { [Description("成員類型一")] M_1 = 0, [Description("成員類型二")] M_2 = 1, [Description("成員類型三")] M_3 = 2, }
四、ViewModel的准備
准備好上述工作,我們就要開始使用MVVM了,首先要把ViewModel的數據填充上,這裡我不詳寫代碼,看清套路就能自己開車了。
using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using DevExpress.Mvvm; namespace Example { public class ProjectSettingViewModel : ViewModelBase { public ObservableCollection<ExampleInfoData> ProjectInfo { get; set; } public New_ProjectSettingViewModel() { ProjectInfo = new ObservableCollection<ExampleInfoData>(FillProjectInfo()); } public List<ExampleInfoData> FillProjectInfo() { List<ExampleInfoData> projectSettingInfoList = new List<ExampleInfoData>(); var dB_projectSettingInfo = projectSettingDB.GetAll(); //get Data From DB foreach (var item in dB_projectSettingInfo) { ExampleInfoData projectSettingInfo = new ExampleInfoData (); projectSettingInfo.Id = item.Id; projectSettingInfo.KeyWord = item.Keyword; projectSettingInfo.PropertyName = item.Name; projectSettingInfo.TabId = item.TabId; projectSettingInfo.DataType = item.DataType; projectSettingInfo.AcceptInput = item.AcceptInput; if (item.ItemValues == null) { DataItem smText = new DataItem(); smText.DisplayName = smText.ItemValue = item.Value; projectSettingInfo.ProjectSettingValue = smText; projectSettingInfo.ItemValues = null; } else { DataItem smCombox = new DataItem(); smCombox.ItemValue = item.Value; smCombox.DisplayName = JudgeType(item.Value); // 這個函數判斷是哪種枚舉類型的!!!並返回相應的Description projectSettingInfo.ProjectSettingValue = smCombox; projectSettingInfo.ItemValues = new List<DataItem>(); foreach (var iv in item.ItemValues.Split(',')) { DataItem sm = new DataItem(); sm.ItemValue = iv; sm.DisplayName = JudgeType(iv); projectSettingInfo.ItemValues.Add(sm); } } projectSettingInfoList.Add(projectSettingInfo); }
return projectSettingInfoList; }
public string JudgeType(string strValue)
{
if (!string.IsNullOrEmpty(strValue))
{
string strType = strValue.Split('_')[0];
if (string.Equals(strType, "T", StringComparison.CurrentCultureIgnoreCase))
{
return GetDescriptionFromEnumValue((ProjectType)Enum.Parse(typeof(ProjectType), strValue)); //獲取Description的方法各位自己寫
}
else if (string.Equals(strType, "M", StringComparison.CurrentCultureIgnoreCase))
{
return GetDescriptionFromEnumValue((MemberType)Enum.Parse(typeof(MemberType), strValue));
}
else
{
return null;
}
}
return null;
} } }
五、View的准備
<UserControl x:Class="Example" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="../../Controls/ProjectSettingDataGrid.xaml"/> !!!Here </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources> <Grid Margin="0,15,0,0"> <DataGrid x:Name="dgPJInfo" CanUserSortColumns="False" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" AlternatingRowBackground="#EBEBEB" Background="White" ItemsSource ="{Binding ProjectInfo}"> <DataGrid.Columns> <DataGridTextColumn Width=".4*" IsReadOnly="True" Header="屬性名稱" FontSize="15" Binding="{Binding PropertyName}"></DataGridTextColumn> <DataGridTemplateColumn Width=".4*" Header="屬性值" CellTemplateSelector="{StaticResource DataGridTemplateSelector}"></DataGridTemplateColumn> !!!Here </DataGrid.Columns> </DataGrid> </Grid> </UserControl>
上面這個View告訴我們這個DataGridTemplateColumn的CellTemplateSelector是
綁定到<ResourceDictionary Source="../../Controls/ProjectSettingDataGrid.xaml"/>裡的DataGridTemplateSelector
那麼ProjectSettingDataGrid.xaml 該怎麼寫呢?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:Example.Controls" xmlns:view="clr-namespace:Example.UI.View"> <DataTemplate x:Key="TextBoxTemplate"> //TextBox的Template <TextBox Text="{Binding PropertyValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" FontSize="15"/> </DataTemplate> <DataTemplate x:Key="TextBlockTemplate"> //TextBlock的Template <TextBlock Text="{Binding PropertyValue}" FontSize="15"/> </DataTemplate> <DataTemplate x:Key="ComboBoxTemplate"> //Combobox的Template <ComboBox ItemsSource="{Binding ItemValues}" FontSize="15" IsEditable="{Binding IsAcceptInput}"
SelectedItem="{Binding PropertyValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="DisplayName"/> !!!注意這裡的DisplayMemberPath !!! </DataTemplate> <controls:DataGridTemplateSelector x:Key="DataGridTemplateSelector" TextBoxDataTemplate="{StaticResource TextBoxTemplate}" TextBlockDataTemplate="{StaticResource TextBlockTemplate}" ComboBoxDataTemplate="{StaticResource ComboBoxTemplate}"/> </ResourceDictionary>
這下好了,定義好了各種Template,我剩下的事就是根據數據,判斷采用哪種Template,
ProjectSettingDataGrid.xaml.cs可以這樣寫:
using System; using System.Windows; using System.Windows.Controls; using Example.ProjectSetting; namespace Example.Controls { public partial class PropertyDataGrid : DataGrid { public PropertyDataGrid() { } } public class DataGridTemplateSelector : DataTemplateSelector { public DataTemplate TextBoxDataTemplate { get; set; } public DataTemplate TextBlockDataTemplate { get; set; } public DataTemplate ComboBoxDataTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) //這裡的object item傳進來的就是ViewModel中ProjectInfo的一條條數據!!! { if (null == item) { return null; } if (item is ExampleInfoData) { ExampleInfoData projectInfo = item as ExampleInfo; if (projectInfo.DataType == (int) ((DataEnum) Enum.Parse(typeof (DataEnum), "DATA_ENUM"))) !!!注意這裡,在數據庫定義的DataType此時就起到了判斷Template的作用!!! { return ComboBoxDataTemplate; } else { return TextBoxDataTemplate; } } // else if (item is OtherInfoData) // { // //do something // }
else { return null; } } } }
六、總結
以上內容就是所有的套路,
簡單的說就是:
1. 數據庫字段
2. 映射字段
3. 枚舉類對應
4. ViewModel 數據填充
5. DataGridTemplateColumn的綁定
6. 定義各種Template並作出判斷選擇哪種Template