路由事件
基础点
WPF支持直接事件,如 winform样直达事件。
最重要的是wpf支持路由事件,路由是通过“可视树”来传递的,而非逻辑树。
一个事件可以由多个事件处理器来处理(多播),一个事件处理器可以响应多个事件。
一个元素添加了事件侦听器,就不关心是谁触发的。
事件可以由触发元素一层一层向外传递,或由页面最外层一直往里传递,而非由元素开始才向内传。
事件的e.OriginalSource 为事件的可视树为源头,e.Source为逻辑树源头
事件就是定义一个路由事件,然后元素侦听此事件就行,相当于多播
为元素添加事件侦听
public partial class eventsim : Window
{
public eventsim()
{
InitializeComponent();
//主要是通过AddHandler来添加对事件的侦听
this.root.AddHandler(Button.ClickEvent, new RoutedEventHandler(buttonClick));
}
private void buttonClick(object sender, RoutedEventArgs e)
{
//使用e.OriginalSource获取触发者,而不是sender
var source = e.OriginalSource as Button;
MessageBox.Show(source.Name);
}
}
xaml里的写法
<Grid x:Name="root" Button.Click="buttonClick">
自定义路由事件
的写法
<Grid x:Name="grid1" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="grid2" local:TimeButton.ReportTime="ReportTimeHandler">
<Grid x:Name="grid3" local:TimeButton.ReportTime="ReportTimeHandler">
<local:TimeButton x:Name="time" Content="报时" local:TimeButton.ReportTime="ReportTimeHandler"></local:TimeButton>
<ListBox x:Name="listBox" H />
</Grid>
</Grid>
</Grid>
public partial class eventsim : Window
{
public eventsim()
{
InitializeComponent();
}
private void ReportTimeHandler(object sender, TimeRouteEventArgs e)
{
var ele = sender as FrameworkElement;
listBox.Items.Add(string.Format("事件到达{0},时间{1}",ele.Name,e.DateTime));
}
}
//自定义事件触发后所携带的消息
public class TimeRouteEventArgs : RoutedEventArgs
{
public TimeRouteEventArgs(RoutedEvent routedEvent, object source)
:base(routedEvent, source)
{ }
public DateTime DateTime { get; set; }
}
//自定义一个按钮
public class TimeButton : Button
{
//注册“报告时间”路由事件
//变更名为"XXEvent",注册后事件名则为“XX”,这是默认的格式,要遵守
public static readonly RoutedEvent ReportTimeEvent =
EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<TimeRouteEventArgs>), typeof(TimeButton));
//CLR事件包装器,就是给别人注册此事件使用
public event RoutedEventHandler ReportTime
{
add { this.AddHandler(ReportTimeEvent, value); }
remove { this.RemoveHandler(ReportTimeEvent, value); }
}
//激发路由事件,借助Click事件
protected override void OnClick()
{
base.OnClick();//保留原始功能
TimeRouteEventArgs arg = new TimeRouteEventArgs(ReportTimeEvent,this);
arg.DateTime = DateTime.Now;
//使用RaiseEvent激发事件
this.RaiseEvent(arg);
}
}
参数说明:
public static readonly RoutedEvent ReportTimeEvent =
EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<TimeRouteEventArgs>), typeof(TimeButton));
第一个:注册的事件名,一般为变更名不加Event
第二个:传递方向,Bubble由激发元素向外传,Tunnel由树根向内传,Direct直接事件
第三个:事件的处理器类型
第四个:事件的宿主
停止事件继续传递
private void ReportTimeHandler(object sender, TimeRouteEventArgs e)
{
var ele = sender as FrameworkElement;
if (ele.Name == "grid2")
e.Handled = true;//停止继续传递
listBox.Items.Add(string.Format("事件到达{0},时间{1}",ele.Name,e.DateTime));
}
附加事件
路由事件的宿主是可视元素,而附加事件的宿主都是不可视元素,附加事件的触发还是得依靠元素的RaiseEvent。
例子:一个Student类,当名称变更时的事件
public partial class eventsim : Window
{
public eventsim()
{
InitializeComponent();
//为元素grid1添加(student7的NageChange变更事件)侦听
Student7.AddNameChangeHandler(grid1, new RoutedEventHandler(NameChangeEvenHandler));
}
//捕获变更后的事件处理器
private void NameChangeEvenHandler(object sender, RoutedEventArgs e)
{
MessageBox.Show((e.OriginalSource as Student7).Id.ToString());
}
private void button_Click(object sender, RoutedEventArgs e)
{
Student7 stu = new Student7() { Id=1,Name="111"};
stu.Name = "222";
RoutedEventArgs arg = new RoutedEventArgs(Student7.NameChangeEvent, stu);//注意这里的source为stu,而非this
this.button1.RaiseEvent(arg);
}
}
public class Student7
{
public int Id { get; set; }
public string Name { get; set; }
//定义路由事件
public static readonly RoutedEvent NameChangeEvent =
EventManager.RegisterRoutedEvent("NameChange", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student7));
//附加事件不能像路由事件那样添加CLR事件包装器,只能分开两个方法写
//为界面元素添加路由事件侦听,注意命名规则
public static void AddNameChangeHandler(DependencyObject d, RoutedEventHandler h)
{
UIElement e = d as UIElement;
if (e != null)
{
e.AddHandler(Student7.NameChangeEvent, h);
}
}
//移除侦听
public static void RemoveNameChangeHandler(DependencyObject d, RoutedEventHandler h)
{
UIElement e = d as UIElement;
if (e != null)
{
e.RemoveHandler(Student7.NameChangeEvent, h);
}
}
}