一、前言
在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