我尝试动态创建和分配 DataTemplate
到 HeaderTemplate
的 DataGrid
通过代码。为此,我有一个方法 GetDatatemplate(string fromstring)
它定义了一个 XML 文字,然后使用它来创建一个 DataTemplate
.只要我不包含 MouseLeftButtonDown
就可以正常工作DataTemplate
中的事件处理程序.
我的 DataTemplate
保存在 string
名为 StringHeaderTemplate
的变量在 MainWindow.xaml.cs
的代码隐藏中:
private string StringHeaderTemplate =@"<DataTemplate>
<DataTemplate.Resources>
<ControlTemplate x:Key=""imgNo"" TargetType=""{x:Type Control}"">
<Image Source = ""pack://application:,,,/Images/upArrow.png"" />
</ControlTemplate >
<ControlTemplate x:Key=""imgUp"" TargetType=""{x:Type Control}"">
<Image Source = ""pack://application:,,,/Images/upArrow.png"" />
</ControlTemplate >
<ControlTemplate x:Key=""imgDown"" TargetType=""{x:Type Control}"" >
<Image Source = ""pack://application:,,,/Images/downArrow.png"" />
</ControlTemplate >
</DataTemplate.Resources>
<Grid Background=""Transparent"" MouseLeftButtonDown=""Grid_MouseLeftButtonDown"">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content=""Hello""/>
<TextBlock Grid.Row=""1"" HorizontalAlignment= ""Center"" Text = ""TextBlock"" />
<CheckBox Grid.Row= ""2"" HorizontalAlignment= ""Center"" IsChecked= ""True"" />
</Grid >
</DataTemplate>";
以及得到
DataTemplate
的方法:private DataTemplate GetDatatemplate(string fromstring)
{
ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
return (DataTemplate)XamlReader.Parse(fromstring, context);
}
然后我就应用这个
DataTemplate
至HeaderTemplate
的 DataGrid
:private void dg_AutoGeneratingColumn_1(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataTemplate dtCell = null;
DataTemplate dtHeader = null;
string dtString = string.Empty;
string dtHeaderString = string.Empty;
switch(Type.GetTypeCode(e.PropertyType))
{
case TypeCode.String:
dtString = StringTemplate.Replace("xxColumnxx", e.PropertyName);
dtHeaderString=StringHeaderTemplate;
break;
}
if(!string.IsNullOrEmpty(dtString))
{
dtCell = GetDataTemplateForDataGrid(dtCellString);
dtHeader = GetDataTemplateForDataGrid(dtHeaderString);
DataGridTemplateColumn c = new DataGridTemplateColumn()
{
CellTemplate = dtCell,
HeaderTemplate = dtHeader,
};
e.Column = c;
}
}
事件处理程序非常简单:
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show(DateTime.Now.ToString());
}
我得到的异常是一个 XamlParseException,它有一个 ArgumentException 类型的 InnerException,它说:
"Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."
有什么想法该怎么做?
更新:
我尝试通过
Command
绑定(bind),但是 CallSortingCommand
不叫。也许你知道我做错了什么?<Grid Background=""Transparent"">
<i:Interaction.Triggers>
<i:EventTrigger EventName=""MouseLeftButtonDown"">
<prism:InvokeCommandAction Command = ""{Binding
RelativeSource={RelativeSource AncestorType=Window,
Mode=FindAncestor}, Path=DataContext.CallSortingCommand}"" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content=""Hello""/>
<TextBlock Grid.Row=""1"" HorizontalAlignment= ""Center"" Text = ""TextBlock"" />
CheckBox Grid.Row= ""2"" HorizontalAlignment= ""Center"" IsChecked= ""True"" />
</Grid >
最佳答案
这是一个有趣的问题,因为可能的解决方案可能是在应用数据模板后,在可视化树中找到要订阅的对象。但它是一个列,在这种情况下很难访问可视化树。
我建议使用交互,它允许在 XAML 中定义事件触发器。请看下面的代码:
private string StringHeaderTemplate = @"<DataTemplate>
<Grid Background=""Transparent"">
<i:Interaction.Triggers>
<i:EventTrigger EventName=""MouseLeftButtonDown"">
<si:CallMethodAction MethodName = ""Grid_MouseLeftButtonDown"" TargetObject=""{Binding RelativeSource={RelativeSource AncestorType=Window}}""/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content=""Hello""/>
<TextBlock Grid.Row=""1"" HorizontalAlignment= ""Center"" Text = ""TextBlock"" />
<CheckBox Grid.Row= ""2"" HorizontalAlignment= ""Center"" IsChecked= ""True"" />
</Grid >
</DataTemplate>";
public void Grid_MouseLeftButtonDown(object sender, RoutedEventArgs e)
{
MessageBox.Show(DateTime.Now.ToString());
}
private DataTemplate GetDatatemplate(string fromstring)
{
ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
context.XmlnsDictionary.Add("i", "clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity");
context.XmlnsDictionary.Add("si", "clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions");
return (DataTemplate)XamlReader.Parse(fromstring, context);
}
附言请注意 Grid_MouseLeftButtonDown 事件处理程序的 public 访问修饰符,使用 private 将不起作用。
更新
完整源代码:
XAML
<Window x:Class="DataGridDataTemplateInCode.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataGridDataTemplateInCode"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="dg" ItemsSource="{Binding Items}" AutoGeneratingColumn="dg_AutoGeneratingColumn" />
</Grid>
</Window>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private string StringHeaderTemplate = @"<DataTemplate>
<Grid Background=""Transparent"">
<i:Interaction.Triggers>
<i:EventTrigger EventName=""PreviewMouseLeftButtonDown"">
<si:CallMethodAction MethodName = ""Grid_MouseLeftButtonDown"" TargetObject=""{Binding RelativeSource={RelativeSource AncestorType=Window}}""/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content=""Hello""/>
<TextBlock Grid.Row=""1"" HorizontalAlignment= ""Center"" Text = ""Grid_MouseLeftButtonDown"" />
<CheckBox Grid.Row= ""2"" HorizontalAlignment= ""Center"" IsChecked= ""True"" />
</Grid >
</DataTemplate>";
private string DateTimeWithCommandHeaderTemplate = @"<DataTemplate>
<Grid Background=""Transparent"">
<i:Interaction.Triggers>
<i:EventTrigger EventName=""MouseLeftButtonDown"">
<i:InvokeCommandAction Command = ""{Binding DataContext.CallSortingCommand, RelativeSource={RelativeSource AncestorType=Window}}""/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content=""Hello""/>
<TextBlock Grid.Row=""1"" HorizontalAlignment= ""Center"" Text = ""CallSortingCommand"" />
<CheckBox Grid.Row= ""2"" HorizontalAlignment= ""Center"" IsChecked= ""True"" />
</Grid >
</DataTemplate>";
private string TimeCellTemplate = @"<DataTemplate>
<TextBlock HorizontalAlignment= ""Center"" Text = ""{Binding Time}"" />
</DataTemplate>";
private string DescCellTemplate = @"<DataTemplate>
<TextBlock HorizontalAlignment= ""Center"" Text = ""{Binding Desc}"" />
</DataTemplate>";
public void Grid_MouseLeftButtonDown(object sender, RoutedEventArgs e)
{
//MessageBox.Show(DateTime.Now.ToString());
var vm = DataContext as MainWindowViewModel;
vm.Items[0].Desc += "+";
}
private DataTemplate GetDatatemplate(string fromstring)
{
ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
context.XmlnsDictionary.Add("i", "clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity");
context.XmlnsDictionary.Add("si", "clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions");
return (DataTemplate)XamlReader.Parse(fromstring, context);
}
private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataTemplate dtHeader = null;
string dtString = string.Empty;
string dtHeaderString = string.Empty;
DataGridTemplateColumn column = null;
switch (Type.GetTypeCode(e.PropertyType))
{
case TypeCode.String:
{
column = new DataGridTemplateColumn()
{
CellTemplate = GetDatatemplate(DescCellTemplate),
HeaderTemplate = GetDatatemplate(StringHeaderTemplate),
};
}
break;
case TypeCode.DateTime:
{
column = new DataGridTemplateColumn()
{
CellTemplate = GetDatatemplate(TimeCellTemplate),
HeaderTemplate = GetDatatemplate(DateTimeWithCommandHeaderTemplate),
};
}
break;
}
if (column != null)
{
e.Column = column;
}
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowViewModel()
{
for (int i = 0; i < 10; i++)
{
_collection.Add(new MyObject() { Time = DateTime.Now.AddSeconds(i), Desc = i.ToString() });
}
CallSortingCommand = new DelegateCommand(OnCallSortingCommand, (o) => true);
}
private void OnCallSortingCommand(object obj)
{
MessageBox.Show("From OnCallSortingCommand");
}
public ICommand CallSortingCommand { get; set; }
private ObservableCollection<MyObject> _collection = new ObservableCollection<MyObject>();
public ObservableCollection<MyObject> Items
{
get
{
return _collection;
}
}
protected void OnPropertyChanged([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public class MyObject : INotifyPropertyChanged
{
public DateTime Time { get; set; }
private string _desc;
public string Desc
{
get { return _desc; }
set { _desc = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Desc))); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
关于c# - 如何在代码中为 DataTemplate 分配事件处理程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35740779/