注册一个路由事件,是向WPF的事件系统注册的,由静态类EventManager提供注册服务,具体则交给RegisterRoutedEvent方法成员完成。
public static readonly RoutedEvent 路由事件名称 = EventManager.RegisterRoutedEvent(
name: "路由事件名称",
routingStrategy: 冒泡事件/隧道事件/直接事件,
handlerType: 路由事件委托的反射实例,
ownerType: 路由事件拥有者的反射实例;
当一个路由事件注册到WPF的事件系统之后,还需要利用event关键词对其进行包装,包装成普通事件的样子,方便开发者调用。
/// <summary>
/// 通过event包装成普通事件的外观
/// </summary>
public event RoutedEventHandler 普通事件名称
{
add { AddHandler(路由事件名称, value); }
remove { RemoveHandler(路由事件名称, value); }
}
最后,寻找合适的时候,利用UIElement基类中的RaiseEvent,触发这个路由事件即可。这样就完成了路由事件的注册流程。
接下来,我们以一个实例来说明这一过程。在前面《DependencyProperty定义使用》一节中,我们定义了一个Widget自定义控件,以此说明依赖属性的用法。让我们回到那个案例中,并对Widget进行适当的修改。
假设这个控件用来显示公司的销售额,当完成即定销售目录时,引发一个“完成路由事件”。
第一步,修改Widget自定义类型,并添加一个CompletedEvent路由事件和TargetProperty依赖属性,当Value大于Target时,触发Completed事件。
Widget自定义控件的前端代码
<UserControl x:Class="HelloWorld.Controls.Widget"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HelloWorld.Controls"
mc:Ignorable="d"
x:Name="userControl"
FontSize="30"
Foreground="#666666"
BorderBrush="#8CDDCD"
d:DesignHeight="450"
d:DesignWidth="800">
<Border BorderBrush="{Binding ElementName=userControl,Path=BorderBrush}">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Padding" Value="10"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#8CDDCD"/>
<Setter Property="BorderThickness" Value="0 3 0 0"/>
<Setter Property="Margin" Value="0"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#F7F9F9"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Value,StringFormat={}{0:C}}"
Foreground="{Binding ElementName=userControl,Path=Foreground}"
FontSize="{Binding ElementName=userControl,Path=FontSize}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Title}"
Foreground="{Binding ElementName=userControl,Path=Foreground}"
FontSize="14" TextWrapping="Wrap"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Icon}"
Foreground="{Binding ElementName=userControl,Path=BorderBrush}"
FontSize="26" Grid.RowSpan="2" VerticalAlignment="Center"/>
</Grid>
</Border>
</UserControl>
Widget自定义控件的后端代码
/// <summary>
/// Widget.xaml 的交互逻辑
/// </summary>
public partial class Widget : UserControl
{
public Widget()
{
InitializeComponent();
DataContext = this;
}
/// <summary>
/// 注册RoutedEvent路由事件
/// </summary>
public static readonly RoutedEvent CompletedEvent = EventManager.RegisterRoutedEvent(
name: "CompletedEvent",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(Widget));
/// <summary>
/// 通过event包装成普通事件的外观
/// </summary>
public event RoutedEventHandler Completed
{
add { AddHandler(CompletedEvent, value); }
remove { RemoveHandler(CompletedEvent, value); }
}
/// <summary>
/// 触发路由事件
/// </summary>
void RaiseRoutedEvent()
{
RoutedEventArgs routedEventArgs = new RoutedEventArgs(CompletedEvent, this);
RaiseEvent(routedEventArgs);
}
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(string), typeof(Widget),
new PropertyMetadata("❤"));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Widget),
new PropertyMetadata("请输入标题"));
/// <summary>
/// 销售目标
/// </summary>
public double Target
{
get { return (double)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(double), typeof(Widget),
new PropertyMetadata(0.0));
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(Widget),
new PropertyMetadata(0.0,new PropertyChangedCallback(OnValuePropertyChangedCallback)));
private static void OnValuePropertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if(d is Widget control && e.NewValue is double value )
{
if (value >= control.Target && control.Target != 0)//当业绩大于100万美元时
{
control.Icon = "☻";
control.RaiseRoutedEvent();//触发路由事件,完成销售目标
}
else
{
control.Icon = "❤";
}
}
}
}
我们在ValueProperty 的回调函数中去判断销售业务,如果完成销售目标,就引发完成事件。接下来演示怎么使用这个控件。首先在主窗体中实例化Widget,并将其Value绑定到一个Slider的Value。
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:controls="clr-namespace:HelloWorld.Controls"
xmlns:helper="clr-namespace:HelloWorld.MVVM"
mc:Ignorable="d" Background="Lavender"
MouseUp="Window_MouseUp" PreviewMouseUp="Window_PreviewMouseUp"
Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Border MouseUp="Border_MouseUp" PreviewMouseUp="Border_PreviewMouseUp"
Background="Transparent">
<Canvas MouseUp="Canvas_MouseUp" PreviewMouseUp="Canvas_PreviewMouseUp"
Background="Transparent">
<controls:Widget Value="{Binding ElementName=slider,Path=Value}"
Target="1000000"
Completed="Widget_Completed"
Title="第四季度北美市场总销售额统计"
Canvas.Left="116" Canvas.Top="17"
Height="103" Width="269"/>
<Slider x:Name="slider" Value="0" Maximum="2000000"
Canvas.Left="116" Canvas.Top="138" Width="269"/>
<ListBox x:Name="listBox" Height="133"
Canvas.Left="116" Canvas.Top="167" Width="269"/>
</Canvas>
</Border>
</Window>
当用户拖动Slider时,Widget的Value值就跟着改变,并判断当前销售业绩与目标业绩,我们像订阅普通事件一样,Completed="Widget_Completed"表示订阅完成事件。
private void Widget_Completed(object sender, RoutedEventArgs e)
{
Widget widget = sender as Widget;
listBox.Items.Insert(0, $"完成目标销售额:{widget.Value}");
}
当该事件被触发,将结果显示到ListBox列表框中。
CompletedEvent路由事件在注册时被注册成RoutingStrategy.Bubble类型,即冒泡事件,如果想注册成隧道事件,建议取名为:PreviewCompletedEvent,并使用PreviewCompleted进行包装。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:087-《注册路由事件》-源代码》-源代码.rar
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年10月27日