DataGrid的DataSource绑定的ObservableCollection<UserType>属性如果在运行中会发生变动,则不能在xaml手动定义Columns,否则当该属性发生变动时,UI不会随之更新,需要使用以下方法来实现单元格编辑类型的替换:
一、
设置DataGrid的AutoGenerateColumns为True,自动生成列的同时在AutoGeneratingColumn事件的方法将e.Column属性替换为DataGridTemplateColumn实例。
xaml:
<DataGrid ItemsSource="{Binding DgvItemSource}" AutoGenerateColumns="True" AutoGeneratingColumn="DataGridAutoGeneratingColumn" />
cs:
private void DataGridAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
string columnName = e.PropertyName;
DataGridTemplateColumn column = new DataGridTemplateColumn();
e.Column = column;
}
在定义的DataGridTemplateColumn实例的CellTemplate和CellEditingTemplate属性赋值新的DataTemplate实例, 创建FrameworkElementFactory实例给DataTemplate实例的VisualTree赋值;FrameworkElementFactory实例可以使用SetBinding方法绑定数据源,使用SetValue方法指定模板,模板可定义在App.xaml里,通过Application.Current.FindResource方法获取。
DataGridTemplateColumn column = new DataGridTemplateColumn();
DgvCellDataTemplateSelector cellTemplate = Application.Current.FindResource("DgvCellTemplateSelector") as DgvCellDataTemplateSelector;
DgvCellDataTemplateSelector cellEditingTemplate = Application.Current.FindResource("DgvCellEditingTemplateSelector") as DgvCellDataTemplateSelector;
column.Header = columnName;
column.CellTemplate = new DataTemplate();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(ContentPresenter));
factory.SetBinding(ContentPresenter.ContentProperty, new Binding(columnName));
factory.SetValue(ContentPresenter.ContentTemplateSelectorProperty, cellTemplate);
column.CellTemplate.VisualTree = factory;
column.CellEditingTemplate = new DataTemplate();
FrameworkElementFactory editingfactory = new FrameworkElementFactory(typeof(ContentPresenter));
editingfactory.SetBinding(ContentPresenter.ContentProperty, new Binding(columnName));
editingfactory.SetValue(ContentPresenter.ContentTemplateSelectorProperty, cellEditingTemplate);
column.CellEditingTemplate.VisualTree = editingfactory;
二、
编写模板选择器,DgvCellDataTemplateSelector是个实现了DataTemplateSelector接口的类型:
public class TemplateMapping : List<TemplateMappingItem>
{
}
public class TemplateMappingItem
{
public object Value { get; set; }
public DataTemplate Template { get; set; }
}
public class DgvCellDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate { get; set; }
public TemplateMapping TemplateMap { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item == null) return DefaultTemplate;
if (item is DataBase data)
{
if (data == null) return DefaultTemplate;
return TemplateMap?.Find(m => Equals(m.Value, data.dataType))?.Template ?? DefaultTemplate;
}
return DefaultTemplate;
}
}
三、
定义模板和创建全局模板选择器实例,app.xaml:
<Application x:Class="TestSystem.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:utilities="clr-namespace:TestSystem.Utilities"
xmlns:ut="clr-namespace:TestSystem.UserType"
StartupUri="View/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate x:Key="TemplateDefault">
<TextBlock Text="{Binding Data.Value}" />
</DataTemplate>
<utilities:TemplateMapping x:Key="DgvCellEditingDataTypeTemplateMap">
<utilities:TemplateMappingItem
Value="{x:Static ut:ControlUserType+DataType.Readonly}"
Template="{StaticResource TemplateDefault}" />
</utilities:TemplateMapping>
<utilities:DgvCellDataTemplateSelector
x:Key="DgvCellEditingTemplateSelector"
DefaultTemplate="{StaticResource TemplateDefault}"
TemplateMap="{StaticResource DgvCellEditingDataTypeTemplateMap}" />
</ResourceDictionary>
</Application.Resources>
</Application>
四、
创建存放数据的UserType。
public enum DataType
{
Readonly
}
public abstract class DataBase : NotifyPropertyChanged,ICloneable
{
private object value;
public DataType dataType;
public object Value { get { return value; } set { this.value = value;OnPropertyChanged(nameof(Value)); } }
public DataBase() { }
public abstract object Clone();
}
public class ReadOnlyData : DataBase
{
public ReadOnlyData()
{
dataType = DataType.Readonly;
}
public override object Clone()
{
var clone = new ReadOnlyData
{
Value = this.Value
};
return clone;
}
}