如何在WPF的DataTemplate中以编程方式添加Interaction Trigger
问题:通过代码实现ComboBox的Interaction Trigger功能
我需要用代码实现以下XAML中ComboBox的Interaction Trigger功能:
<ComboBox ...> <i:Interaction.Triggers> <i:EventTrigger EventName="DropDownClosed"> <i:InvokeCommandAction Command="{Binding DataContext.DropDownCommand, RelativeSource={RelativeSource Findancestor, AncestorType = { x:Type Window}}}" CommandParameter="{Binding Path=.}" /> </i:EventTrigger> </i:Interaction.Triggers> ... </ComboBox>
现有创建ComboBox的代码能正常运行,但添加触发器时调用Interaction.GetTriggers(combobox)报错,相关代码片段如下:
//ComboBox FrameworkElementFactory combobox = new FrameworkElementFactory(typeof(ComboBox)); combobox.SetBinding(ComboBox.SelectedItemProperty, selectedItembinding); combobox.SetBinding(ComboBox.ItemsSourceProperty, itemsourcebinding); combobox.SetValue(ComboBox.DisplayMemberPathProperty, columnName.DisplayMemberPathName); combobox.SetValue(ComboBox.StyleProperty, comboBoxStyle); combobox.SetValue(ComboBox.ItemContainerStyleProperty, itemContainerStyle);
尝试添加触发器的代码:
//Interaction.Triggers InvokeCommandAction invokeCommandAction = new InvokeCommandAction { CommandParameter = "{Binding Path=.}" }; Binding binding = new Binding { Path = new PropertyPath("DataContext.DropDownCommand") }; BindingOperations.SetBinding(invokeCommandAction, InvokeCommandAction.CommandProperty, binding); Microsoft.Xaml.Behaviors.EventTrigger eventTrigger = new Microsoft.Xaml.Behaviors.EventTrigger { EventName = "DropDownClosed" }; eventTrigger.Actions.Add(invokeCommandAction); TriggerCollection triggers = Interaction.GetTriggers(combobox); // 此处报错 triggers.Add(eventTrigger);
完整方法代码:
private static DataGridTemplateColumn GetComboBoxUniqueColumn(ComboBoxUniqueColumn columnName) { DataGridTemplateColumn column = new DataGridTemplateColumn(); column.IsReadOnly = true; column.Header = columnName.DisplayColumnName; column.Width = columnName.Width; column.MinWidth = GetMinWidth(columnName.Width); //Binding Binding selectedItembinding = new Binding(); selectedItembinding.Path = new PropertyPath(columnName.SelectedItemPropertyName); selectedItembinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; Binding itemsourcebinding = new Binding(); itemsourcebinding.Path = new PropertyPath(string.Format("DataContext.{0}", columnName.ItemSourcePropertyName)); itemsourcebinding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Window), 1); Binding isselectablebinding = new Binding(); isselectablebinding.Path = new PropertyPath("IsSelectable"); //Style Style comboBoxStyle = Application.Current.Resources["DataGridComboBox"] as Style; Style itemContainerStyle = new Style(); itemContainerStyle.TargetType = typeof(ComboBoxItem); itemContainerStyle.BasedOn = Application.Current.Resources["DataGridComboBoxItemContainerStyle"] as Style; itemContainerStyle.Setters.Add(new Setter(ComboBoxItem.IsEnabledProperty, isselectablebinding)); //ComboBox FrameworkElementFactory combobox = new FrameworkElementFactory(typeof(ComboBox)); combobox.SetBinding(ComboBox.SelectedItemProperty, selectedItembinding); combobox.SetBinding(ComboBox.ItemsSourceProperty, itemsourcebinding); combobox.SetValue(ComboBox.DisplayMemberPathProperty, columnName.DisplayMemberPathName); combobox.SetValue(ComboBox.StyleProperty, comboBoxStyle); combobox.SetValue(ComboBox.ItemContainerStyleProperty, itemContainerStyle); //Interaction.Triggers //InvokeCommandAction invokeCommandAction = new InvokeCommandAction { CommandParameter = "{Binding Path=.}" }; //Binding binding = new Binding { Path = new PropertyPath("DataContext.DropDownCommand") }; //BindingOperations.SetBinding(invokeCommandAction, InvokeCommandAction.CommandProperty, binding); //Microsoft.Xaml.Behaviors.EventTrigger eventTrigger = new Microsoft.Xaml.Behaviors.EventTrigger { EventName = "DropDownClosed" }; //eventTrigger.Actions.Add(invokeCommandAction); //TriggerCollection triggers = Interaction.GetTriggers(??); //triggers.Add(eventTrigger); //Set to DataTemplate var datatemplate = new DataTemplate(); datatemplate.VisualTree = combobox; //Set to DataGridTemplateColumn column.CellTemplate = datatemplate; return column; }
需要解决如何通过代码重现XAML中的Interaction Trigger功能。
解决方案
问题核心:Interaction.GetTriggers仅适用于已实例化的UI元素,而FrameworkElementFactory是模板生成器,并非实际元素实例。正确做法是通过附加属性直接给工厂设置触发器集合,同时修正绑定逻辑。
修正后的触发器实现代码
替换原注释掉的触发器部分,使用以下代码:
// 创建InvokeCommandAction并设置绑定 InvokeCommandAction invokeCommandAction = new InvokeCommandAction(); // 设置CommandParameter的绑定(不能直接赋值字符串,需创建Binding对象) Binding commandParamBinding = new Binding { Path = new PropertyPath(".") }; invokeCommandAction.SetBinding(InvokeCommandAction.CommandParameterProperty, commandParamBinding); // 设置Command的绑定,匹配XAML中查找Window的DataContext逻辑 Binding commandBinding = new Binding { Path = new PropertyPath("DataContext.DropDownCommand"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Window), 1) }; invokeCommandAction.SetBinding(InvokeCommandAction.CommandProperty, commandBinding); // 创建EventTrigger并添加Action Microsoft.Xaml.Behaviors.EventTrigger eventTrigger = new Microsoft.Xaml.Behaviors.EventTrigger { EventName = "DropDownClosed" }; eventTrigger.Actions.Add(invokeCommandAction); // 创建触发器集合并添加事件触发器 TriggerCollection triggerCollection = new TriggerCollection(); triggerCollection.Add(eventTrigger); // 将触发器集合通过附加属性赋值给ComboBox工厂 combobox.SetValue(Interaction.TriggersProperty, triggerCollection);
关键注意点
- CommandParameter绑定修正:原代码直接赋值
"{Binding Path=.}"是字符串,不是绑定,必须创建Binding对象并通过SetBinding设置。 - Command绑定修正:必须添加
RelativeSource指向Window,否则无法找到目标DataContext。 - 触发器赋值方式:通过
SetValue给FrameworkElementFactory设置Interaction.TriggersProperty附加属性,替代Interaction.GetTriggers方法。
替换后运行代码,即可实现与XAML完全一致的Interaction Trigger功能。
内容的提问来源于stack exchange,提问作者Yannick
相关产品推荐
相关产品推荐

