說明 我在項目中根據需求需要用到WPF Dev CellTemplateSelector時,遇到不少坑。曾一度想要放棄使用模板轉換器,但又心有不甘,終於在不斷努力下,達到了需求的要求。所以寫下來和大家分享。如果有同樣困惑的人,可以少走些彎路。筆者第一次寫博客,文筆不好,還請見諒。 需求 需求很簡單, ...
說明
我在項目中根據需求需要用到WPF Dev CellTemplateSelector時,遇到不少坑。曾一度想要放棄使用模板轉換器,但又心有不甘,終於在不斷努力下,達到了需求的要求。所以寫下來和大家分享。如果有同樣困惑的人,可以少走些彎路。筆者第一次寫博客,文筆不好,還請見諒。
需求
需求很簡單,選擇方式下拉框選中時間控制方式或者價格控制方式,後面的控制點單元格對應顯示日期控制項或文本控制項。
思路
剛拿到這個需求,就想到了模板選擇器。但是之前也沒有用過模板轉換器,所以走了很多彎路。我們先看dev官方文檔的說明
When using CellTemplate (or DataViewBase.CellTemplate) note the following: To enable data editing, use an editor shipped with the DevExpress Data Editors Library for WPF. The editor's Name must be set to 'PART_Editor'. When the editor's Name is set to PART_Editor, the grid automatically adjusts its appearance and synchronizes the editor with a source field specified by the FieldName or Binding properties. Standard controls can be used in CellTemplate only for display purposes. Data editing is not allowed. Templates specified via the DisplayTemplate and/or EditTemplate are ignored. A column's in-place editor specified via EditSettings, is also ignored.
意思就是說模板控制項必須命名為“PART_Editor”,並且模板不需要綁定數據源,對應的GridControl的列綁定數據源即可,GridControl會自動將模板嵌入展示。我剛開始就是沒有命名規範,並且在模板控制項中綁定了數據源所以顯示一直有問題。根據需求寫出模板如下。
<Window.Resources> <DataTemplate x:Key="FirstTemplate"> <StackPanel> <dxe:DateEdit x:Name="PART_Editor" Mask="yyyy-MM-dd" MaskUseAsDisplayFormat="True"> </dxe:DateEdit> </StackPanel> </DataTemplate> <DataTemplate x:Key="SecondTemplate"> <StackPanel> <dxe:TextEdit x:Name="PART_Editor" MaskType="Numeric" Mask="n" MaskUseAsDisplayFormat="True" AllowNullInput="False" > </dxe:TextEdit> </StackPanel> </DataTemplate> </Window.Resources>
同時CellTemplateSelector需要繼承DataTemplateSelector類,並且實現SelectTemplate方法根據條件返回一個你所需的Template.代碼如下
public class SettingDataTemplateSelector: DataTemplateSelector { public DataTemplate FirstTemplate { get; set; } public DataTemplate SecondTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { FrameworkElement element = container as FrameworkElement; EditGridCellData data = (EditGridCellData)item; Setting setting = data.RowData.Row as Setting; if (setting != null) { if (setting.ControlMode == 1) return element.FindResource("FirstTemplate") as DataTemplate; else return element.FindResource("SecondTemplate") as DataTemplate; } return base.SelectTemplate(item, container); } }
然後前臺xmal的CellTemplateSelector中引用選擇器,代碼如下
<dxg:GridColumn x:Name="ControlPointDataTime" Binding="{Binding Path=ControlPoint,Mode=TwoWay}" Header="控制點" > <dxg:GridColumn.CellTemplateSelector> <local:SettingDataTemplateSelector FirstTemplate="{StaticResource FirstTemplate}" SecondTemplate="{StaticResource SecondTemplate}" /> </dxg:GridColumn.CellTemplateSelector> </dxg:GridColumn>
基本的需求就完成了,是不是看著很簡單。其實當你覺得簡單是因為你瞭解了DEV的一些機制,比如模板控制項的命名必須是“PART_Editor”,還有綁定的地方,任何一個地方出錯了前臺運行顯示都是不會稱心如意的,這個時候你就會找不到解決的辦法,從而達不到你想要的目的。上面的需求其實還有一個問題,比如:當你添加一條記錄是金額控制的,此時這條記錄已經添加到數據中了。如果你在界面上修改將下拉框選中改為時間控制,後面一列由於因為無法轉換為時間格式,所以顯示仍然有問題。此時怎麼辦呢,我想到了GridControl的CellValueChanged事件,當我切換控制方式時,後面一列我給它清空,這樣直接讓用戶輸入不就好了。在這裡我也將代碼貼出來。
private void ViewSimulate_CellValueChanged(object sender, CellValueChangedEventArgs e) { if (e.Column == ComControlMode) { ((GridControl) e.Source.DataControl).SetCellValue(e.RowHandle,ControlPointDataTime,null); } }
到此,項目運行起來看著還行,雖然需求很簡單,但也折騰了我一段時間,今後我會不斷的寫博客來記錄我遇到的坑來和大家分享,同時也方便我自己查看。最後,我將完整的demo上傳供大家參考,我用的dev的版本是15.2.4,如果你的版本和我的不一樣,可以將demo中的引用刪除換成自己的就可以了。
補充:當系統時間設置如下時,前臺界面將顯示混亂。
前臺顯示如下
當我發現這個問題,我一時沒有更好的解決辦法。前臺模板DateEdit的Mask、DisplayFormatString以及MaskUseAsDisplayFormat都設為True。於是我向DevSupport尋求幫助。DevSupport給出解釋如下:
The cause of the issue is that date values are stored as string values. In this scenario, DXGrid posts the values in the current culture to maintain end-user input.
DateEdit editors, however, use an invariant culture to parse string values. To resolve this issue, you can either store the date values as DateTime or additionally convert posted values. With your current implementation,
the second approach can be implemented by setting Binding.Converter to a custom converter
在此,我給出第二種方法的實現方式。
public class ConvertDateTime : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date; if (value is string && DateTime.TryParse((string)value, out date)) return date.ToString(CultureInfo.InvariantCulture); return value; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } }
前臺添加Convert轉化綁定
<dxg:GridColumn x:Name="ControlPointDataTime" Binding="{Binding Path=ControlPoint,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True,Converter={local:ConvertDateTime}}" Header="控制點" >
到此,問題就解決了。
補充2:對於上面的切換控制方式時,後面一列清空的需求這裡有更簡單的實現方式,只需要將你自定義的類中的屬性加上一些邏輯控制就好了,代碼如下:
private int _ControlMode; public int ControlMode { get { return _ControlMode; } set { _ControlMode = value; ControlPoint = null; } }
將修改後的源碼重新上傳。
源碼下載:http://files.cnblogs.com/files/damon-xu/demo.rar