C# 如何在代码中为DataTemplate分配事件处理程序?

C# 如何在代码中为DataTemplate分配事件处理程序?,c#,wpf,mvvm,datagrid,C#,Wpf,Mvvm,Datagrid,我尝试通过代码动态创建DataGrid的HeaderTemplate,并将其分配给DataGrid。为此,我有一个方法GetDatatemplate(string fromstring),它定义了一个XML文本,然后使用它创建一个DataTemplate。只要在DataTemplate中不包含MouseLeftButtonDown事件处理程序,这就可以正常工作 MyDataTemplate保存在string变量中,该变量名为StringHeaderTemplate,位于MainWindow.xa

我尝试通过代码动态创建
DataGrid
HeaderTemplate
,并将其分配给
DataGrid
。为此,我有一个方法
GetDatatemplate(string fromstring)
,它定义了一个XML文本,然后使用它创建一个
DataTemplate
。只要在
DataTemplate
中不包含
MouseLeftButtonDown
事件处理程序,这就可以正常工作

My
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
应用到
DataGrid
HeaderTemplate

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,它的InnerException类型为ArgumentException,表示:

“无法绑定到目标方法,因为其签名或安全性 透明度与委托类型的透明度不兼容。“

你知道怎么做吗

更新:

我试图通过
命令
进行绑定,但是没有调用
调用排序命令
。也许你知道我做错了什么

<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 >

复选框Grid.Row=”“2”“HorizontalAlignment=”“Center”“IsChecked=”“True”“/>

这是一个有趣的问题,因为可能的解决方案是在应用数据模板后,在可视化树中查找要订阅的对象。但它是一个列,在这种情况下很难访问可视化树

我建议使用交互,它允许在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);
    }
私有字符串StringHeaderTemplate=@”
";
公共无效网格\u MouseLeftButtonDown(对象发送器,RoutedEventArgs e)
{
Show(DateTime.Now.ToString());
}
私有数据模板GetDatatemplate(string fromstring)
{
ParserContext上下文=新的ParserContext();
context.xmlnsdirectionary.Add(“,”http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.xmlnsdirectionary.Add(“x”http://schemas.microsoft.com/winfx/2006/xaml");
Add(“i”,“clr命名空间:System.Windows.Interactivity;assembly=System.Windows.Interactivity”);
添加(“si”,“clr命名空间:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions”);
return(DataTemplate)XamlReader.Parse(fromstring,context);
}
另外,请注意Grid_MouseLeftButtonDown事件处理程序的公共访问修饰符,使用private将无法工作

更新

完整源代码:

XAML


C#

公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DataContext=新的MainWindowViewModel();
}
私有字符串StringHeaderTemplate=@”
";
私有字符串DateTimeWithCommandHeaderTemplate=@”
";
专用字符串TimeCellTemplate=@”
";
私有字符串DescCellTemplate=@”
";
公共无效网格\u MouseLeftButtonDown(对象发送器,RoutedEventArgs e)
{
//Show(DateTime.Now.ToString());
var vm=作为MainWindowViewModel的DataContext;
vm.Items[0].Desc+=“+”;
}
私有数据模板GetDatatemplate(string fromstring)
{
ParserContext上下文=新的ParserContext();
context.xmlnsdirectionary.Add(“,”http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.xmlnsdirectionary.Add(“x”http://schemas.microsoft.com/winfx/2006/xaml");
Add(“i”,“clr命名空间:System.Windows.Interactivity;assembly=System.Windows.Interactivity”);
添加(“si”,“clr命名空间:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions”);
return(DataTemplate)XamlReader.Parse(fromstring,context);
}
私有void dg_AutoGeneratingColumn(对象发送方,DataGridAutoGeneratingColumnEventArgs e)
{
DataTemplate dtHeader=null;
string dtString=string.Empty;
字符串dtHeaderString=string.Empty;
DataGridTemplateColumn=null;
开关(Type.GetTypeCode(e.PropertyType))
{
大小写类型代码。字符串:
{
column=新的DataGridTemplateColumn()
{
CellTemplate=GetDatatemplate(DescCellTemplate),
HeaderTemplate=GetDatatemplate(StringHeaderTemplate),
};
}
打破
案例类型代码.DateTime:
{
column=新的DataGridTemplateColumn()
{
CellTemplate=GetDatatemplate(TimeCellTemplate),
HeaderTemplate=GetDatatemplate(DateTimeWithCommandHeaderTemplate),
};
}
打破
}
if(列!=null)
{
e、 列=列;
}
}
}
公共类MainWindowViewModel:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
公共主窗口视图模型()
{
    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);
    }
<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>
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);
    }
}