自定义路由事件
本博客为参考工具书,根据个人理解所写,目的在于个人的备忘(我是个新手),如文中有不正确的地方,望指正交流。
WPF中大部分的事件都是可路由的事件,如Button的Click。路由事件相对直接事件的好处在于,不用在消息发送和相应之间建立点对点的订阅关系,发送者只负责发送消息,不关心谁处理,响应者则负责侦听事件和响应。自定义路由事件可用于自定义控件等,如对一个充满自定义名片控件的列表中,对名片控件的共性操作可定义路由事件,然后在列表控件处统一处理。
路由事件的自定义:
public class TimeTextBox:TextBox
{
//1.注册路由事件
//注册路由事件第一个参数ReportChangeAndTime为事件的名字,类似Button的Click
//第二个参数为事件消息传递的类型,自下而上(冒泡Bubble)还是自上而下等
//第三个参数是该路由事件的处理方法,并约束该方法的参数为ReportChangeAndTimeArgs,该参数中可根据需要添加属性如本例的ChangedTime
//第四个参数是该路由事件的拥有者,类似Click的拥有者是Button
public static readonly RoutedEvent ReportChangeAndTimeEvent = EventManager.RegisterRoutedEvent("ReportChangeAndTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportChangeAndTimeArgs>), typeof(TimeTextBox));
//2.添加CLR事件添加包装器,类似于依赖项属性的包装器,关键字和方法不一样而已
public event RoutedEventHandler ReportChangeAndTime
{
add { this.AddHandler(ReportChangeAndTimeEvent,value); }
remove { this.RemoveHandler(ReportChangeAndTimeEvent,value); }
}
//3.定义激发自定义路由的方法
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);//原有功能
ReportChangeAndTimeArgs args = new ReportChangeAndTimeArgs(ReportChangeAndTimeEvent,this);
args.ChangedTime = DateTime.Now;//为路由事件添加自定义参数
this.RaiseEvent(args);//激发事件
}
}
public class ReportChangeAndTimeArgs :RoutedEventArgs { public DateTime ChangedTime; public ReportChangeAndTimeArgs(RoutedEvent routedEvent, object source):base( routedEvent, source) { } }
在xaml中引入命名空间,添加自定义的空间TimeTextbox
<Window x:Class="INotifyProperChangedPro.Views.FamilyMembersShow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc="clr-namespace:INotifyProperChangedPro.UserControl" Title="FamilyMembersShow" Height="300" Width="300"> <Grid uc:TimeTextBox.ReportChangeAndTime ="ReportChangedAndTimeEventHander" x:Name="Grid"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <StackPanel Grid.Row="0" uc:TimeTextBox.ReportChangeAndTime ="ReportChangedAndTimeEventHander" x:Name="stackPanel"> <TextBox Text="{Binding ThePerson.name}"></TextBox> <TextBox Text="{Binding ThePerson.age}"></TextBox> <Button Content="clickMe" Name="ClickButton" Width="60" Click="ClickMeEventHander"></Button> <ListBox x:Name="listBox"></ListBox> <uc:TimeTextBox Width="100" x:Name="textbox" ReportChangeAndTime="ReportChangedAndTimeEventHander"></uc:TimeTextBox> </StackPanel> <DataGrid Grid.Row="1" ItemsSource="{Binding FamilyMembers,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"> <DataGrid.Columns> <DataGridTextColumn Header="姓名" Binding="{Binding name}" Width="60"></DataGridTextColumn> <DataGridTextColumn Header="年龄" Binding="{Binding age}"></DataGridTextColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>
xaml.cs中添加路由事件的响应方法
private void ReportChangedAndTimeEventHander(object sender, ReportChangeAndTimeArgs e) { FrameworkElement element = sender as FrameworkElement; string timeStr = e.ChangedTime.ToLongDateString(); string text = string.Format("{0}路由至{1}", timeStr, element.Name); listBox.Items.Add(text); if(element == this.stackPanel) { e.Handled = true; } }
犹豫指定了冒泡传递方式,当事件自下而上传递至stackpanel时,e.Handled = true;通知该事件已经被处理,不再继续传递。
WPF的UI分为逻辑树Logical Tree和视觉树Visual Tree,当路由事件被激发后的传递,是沿着Visual Tree进行的。因为逻辑树的节点不是布局组件就是控件, 视觉树包含了所有的细节,这些细节是构成控件本身的更细微的派生自Visual的可视化组件,意思是视觉树比逻辑树更细,包含更多内容,只有路由事件沿着视觉树传递时,可视化组件中的消息才能被传递出来。
路由事件的传递其实是路由事件消息的传递,而路由事件的消息是包含在路由事件参数RoutedEventArgs中,该参数有Source和OriginalSource两个属性,都表示路由事件的源头,前者是逻辑树的源头,后者是视觉树的源头。
浙公网安备 33010602011771号