如何捕获父控件中子项生成的事件?
How can I catch an event generated by a child in a parent control?
我创建了一个列表框控件,并重新定义了 ListBoxItem 的样式,如下所示:
<Style TargetType="{x:Type customControls:CheckedListBox}"
BasedOn="{StaticResource {x:Type ListBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander x:Name="PART_Expander" IsExpanded="True">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="SelectionMode" Value="Multiple"/>
<Style.Resources>
<Style TargetType="ListBoxItem" x:Name="PART_ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<CheckBox Selector.IsSelected="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Content="{Binding Path=.}">
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsSelected" Value="True"/>
</Style>
</Style.Resources>
</Style>
在 CheckedListBox 后面的代码中,我在单击任何复选框时引发事件:
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
RaiseRefreshDataEvent(e.RemovedItems);
}
protected virtual void RaiseRefreshDataEvent(IList changedItems)
{
RefreshDataSetEventArgs args = new RefreshDataSetEventArgs(CheckedListBox.RefreshDataSetEvent, changedItems);
RaiseEvent(args);
}
public static readonly RoutedEvent RefreshDataSetEvent =
EventManager.RegisterRoutedEvent("RefreshDataSet", RoutingStrategy.Bubble,
typeof(RefreshDataSetEventHandler), typeof(CheckedListBox));
public event RefreshDataSetEventHandler RefreshDataSet
{
add { AddHandler(RefreshDataSetEvent, value); }
remove { RemoveHandler(RefreshDataSetEvent, value); }
}
public delegate void RefreshDataSetEventHandler(Object sender, RefreshDataSetEventArgs e);
public class RefreshDataSetEventArgs : RoutedEventArgs
{
public IList ValueChanged { get; set; }
public RefreshDataSetEventArgs(RoutedEvent routedEvent, IList valuesChanged) : base(routedEvent)
{
ValueChanged = valuesChanged;
}
}
现在,我想在 CheckedListBox 的父项之一中捕获 RefreshDataSetEvent。
我已经能够通过浏览可视化树对象、找到 CheckedListBox 并使用 RefreshDataSet 从父 class 添加一个方法到对象来做到这一点。
这 对我来说 像是一个 hack。我希望如果事件真的在可视化树中冒泡,我 应该 能够捕获它而不必显式向源对象添加处理程序。
知道如何在不显式设置对象处理程序的情况下捕获此事件吗?
编辑
CheckBoxList 在另一个名为 FilterDataGrid 的自定义控件中使用(这是 class 我想处理的事件)。这是 FilterDataGrid 的样式:
<Style TargetType="{x:Type customControls:FilterDataGrid}"
BasedOn="{StaticResource {x:Type DataGrid}}">
<Style.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}" x:Name="PART_DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<customControls:CheckedListBox ItemsSource="{Binding Path=.}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Background" Value="Black"/>
</Style>
</Style.Resources>
</Style>
因此,当我尝试将 customControls:CheckedListBox.RefreshDataSet="TestThis" 添加到样式时,我收到一条错误消息,提示我使用 EventSetter。
当我将以下内容添加到事件 setter 的样式时,我得到一个 "Unknown build error, Object reference not set to an instance of an object":
根据文档,您不能在 Generic.xaml 文件中使用事件 setter。或者至少这是我从中收集到的:
Event setters cannot be used in a style that is contained in a theme
resource dictionary. This is because a theme resource dictionary at
run time is often loose binary XAML (BAML) files, and does not have
any scope defined where accompanying code-behind that defines the
handlers can exist.
所以我想知道如果无法在 Themes/Generic.xaml 文件中指定事件处理程序,我该如何设置它。
我可以通过在 EventManager 中注册一个 class 处理程序来实现它。
在 FilterDataGrid class 的 public 构造函数中,放入以下代码:
EventManager.RegisterClassHandler(typeof(FilterDataGrid), CheckedListBox.RefreshDataSetEvent, new CheckedListBox.RefreshDataSetEventHandler(FilterDataGridRefreshEventHandler));
考虑到 FilterDataGrid 和 CheckedListBox 都是自定义控件,我认为在两者之间放置一个硬 link 是可以的(请随时让我知道潜在的陷阱,我对这个控件还是很陌生创作的东西)。
我创建了一个列表框控件,并重新定义了 ListBoxItem 的样式,如下所示:
<Style TargetType="{x:Type customControls:CheckedListBox}"
BasedOn="{StaticResource {x:Type ListBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander x:Name="PART_Expander" IsExpanded="True">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="SelectionMode" Value="Multiple"/>
<Style.Resources>
<Style TargetType="ListBoxItem" x:Name="PART_ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<CheckBox Selector.IsSelected="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Content="{Binding Path=.}">
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsSelected" Value="True"/>
</Style>
</Style.Resources>
</Style>
在 CheckedListBox 后面的代码中,我在单击任何复选框时引发事件:
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
RaiseRefreshDataEvent(e.RemovedItems);
}
protected virtual void RaiseRefreshDataEvent(IList changedItems)
{
RefreshDataSetEventArgs args = new RefreshDataSetEventArgs(CheckedListBox.RefreshDataSetEvent, changedItems);
RaiseEvent(args);
}
public static readonly RoutedEvent RefreshDataSetEvent =
EventManager.RegisterRoutedEvent("RefreshDataSet", RoutingStrategy.Bubble,
typeof(RefreshDataSetEventHandler), typeof(CheckedListBox));
public event RefreshDataSetEventHandler RefreshDataSet
{
add { AddHandler(RefreshDataSetEvent, value); }
remove { RemoveHandler(RefreshDataSetEvent, value); }
}
public delegate void RefreshDataSetEventHandler(Object sender, RefreshDataSetEventArgs e);
public class RefreshDataSetEventArgs : RoutedEventArgs
{
public IList ValueChanged { get; set; }
public RefreshDataSetEventArgs(RoutedEvent routedEvent, IList valuesChanged) : base(routedEvent)
{
ValueChanged = valuesChanged;
}
}
现在,我想在 CheckedListBox 的父项之一中捕获 RefreshDataSetEvent。 我已经能够通过浏览可视化树对象、找到 CheckedListBox 并使用 RefreshDataSet 从父 class 添加一个方法到对象来做到这一点。 这 对我来说 像是一个 hack。我希望如果事件真的在可视化树中冒泡,我 应该 能够捕获它而不必显式向源对象添加处理程序。 知道如何在不显式设置对象处理程序的情况下捕获此事件吗?
编辑
CheckBoxList 在另一个名为 FilterDataGrid 的自定义控件中使用(这是 class 我想处理的事件)。这是 FilterDataGrid 的样式:
<Style TargetType="{x:Type customControls:FilterDataGrid}"
BasedOn="{StaticResource {x:Type DataGrid}}">
<Style.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}" x:Name="PART_DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<customControls:CheckedListBox ItemsSource="{Binding Path=.}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Background" Value="Black"/>
</Style>
</Style.Resources>
</Style>
因此,当我尝试将 customControls:CheckedListBox.RefreshDataSet="TestThis" 添加到样式时,我收到一条错误消息,提示我使用 EventSetter。 当我将以下内容添加到事件 setter 的样式时,我得到一个 "Unknown build error, Object reference not set to an instance of an object": 根据文档,您不能在 Generic.xaml 文件中使用事件 setter。或者至少这是我从中收集到的:
Event setters cannot be used in a style that is contained in a theme resource dictionary. This is because a theme resource dictionary at run time is often loose binary XAML (BAML) files, and does not have any scope defined where accompanying code-behind that defines the handlers can exist.
所以我想知道如果无法在 Themes/Generic.xaml 文件中指定事件处理程序,我该如何设置它。
我可以通过在 EventManager 中注册一个 class 处理程序来实现它。 在 FilterDataGrid class 的 public 构造函数中,放入以下代码:
EventManager.RegisterClassHandler(typeof(FilterDataGrid), CheckedListBox.RefreshDataSetEvent, new CheckedListBox.RefreshDataSetEventHandler(FilterDataGridRefreshEventHandler));
考虑到 FilterDataGrid 和 CheckedListBox 都是自定义控件,我认为在两者之间放置一个硬 link 是可以的(请随时让我知道潜在的陷阱,我对这个控件还是很陌生创作的东西)。