一直对WPF中的RoutedEvent和Command不太理解其原理,最近看了
源代码才逐渐明白一些,所以赶快写下来,以备后观。
编写自己的 RoutedEvent
要编写自己的RoutedEvent需要两大步骤,1是定义和注册RoutedEvent, 2是引发RoutedEvent。
1、定义和注册RoutedEvent
需要三个步骤,a, definite your event. b, Register your event with EventManager. c, Wrap your event with traditional event wrapper(Property).
比如我要写一个自己的Custom Control, 一个自己的Button,该Button不只有Click事件,同时还有自己定义的MyClick事件。那么如何定义和注册RoutedEvent呢,如下:
// 步骤1.要添加一个 RoutedEvent 首先要用EventManager注册一个 RoutedEvent public static readonly RoutedEvent MyClickEvent = EventManager.RegisterRoutedEvent( "MyClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton)); // 步骤2.要定义一个Property 然后增加 对add/remove RoutedEventHandler的处理 public event RoutedEventHandler MyClick { add { base.AddHandler(MyButton.MyClickEvent, value); } remove { base.RemoveHandler(MyButton.MyClickEvent, value); } }
上面的步骤1就是我合并后的a,b两步。
2、引发RoutedEvent
要引发RoutedEvent, 需要处理更底层的Event事件,然后再Raise自己的RoutedEvent。我本来采用处理鼠标左键按下的事件来引发自定义的事件,但是好像屏蔽掉了Base的Click和Command处理。我想原因应该是没有处理左键弹起的事件。后来干脆OverRide ButtonBase.OnClick。
// 3. 要通过对底层事件的处理引发自定义的事件。这里重写MouseDoubleClick //protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) //{ // //base.OnMouseLeftButtonDown(e); //base 的函数不起作用 Click 和 Command 都没有处理 // // new一个RoutedEventArgs // RoutedEventArgs re = new RoutedEventArgs(MyButton.MyClickEvent, this); // // Raise it. // base.RaiseEvent(re); // base.OnMouseLeftButtonDown(e); //} // 由于ButtonBase有virtual OnClick 会在OnMouseLeftButtonDown时被调用,可以重写此函数试一试 protected override void OnClick() { // 首先调用base的OnClick try { base.OnClick(); } finally { // new一个RoutedEventArgs RoutedEventArgs re = new RoutedEventArgs(MyButton.MyClickEvent, this); // // Raise it. base.RaiseEvent(re); } }//结果与设想的一样 引发Click,执行命令,并且处理MyClick.
然后就是编写自己的Button样式,并且使用该Button
样式:
<Style TargetType="{x:Type local:MyButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MyButton}"> <Border Background="LightBlue" BorderBrush="Black" BorderThickness="1"> <ContentPresenter></ContentPresenter> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
使用之
<local:MyButton Click="Button_Click" Command="local:ExampleCommands.Requery" MyClick="MyButton_MyClick">My button</local:MyButton>
详细代码如下
// MyButton.CS
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Controls.Primitives; namespace CommandAndEvent { /// <summary> /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. /// /// Step 1a) Using this custom control in a XAML file that exists in the current project. /// Add this XmlNamespace attribute to the root element of the markup file where it is /// to be used: /// /// xmlns:MyNamespace="clr-namespace:CommandAndEvent" /// /// /// Step 1b) Using this custom control in a XAML file that exists in a different project. /// Add this XmlNamespace attribute to the root element of the markup file where it is /// to be used: /// /// xmlns:MyNamespace="clr-namespace:CommandAndEvent;assembly=CommandAndEvent" /// /// You will also need to add a project reference from the project where the XAML file lives /// to this project and Rebuild to avoid compilation errors: /// /// Right click on the target project in the Solution Explorer and /// "Add Reference"->"Projects"->[Browse to and select this project] /// /// /// Step 2) /// Go ahead and use your control in the XAML file. /// /// <MyNamespace:MyButton/> /// /// </summary> public class MyButton : ButtonBase { static MyButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton))); } // 1.要添加一个 RoutedEvent 首先要用EventManager注册一个 RoutedEvent public static readonly RoutedEvent MyClickEvent = EventManager.RegisterRoutedEvent( "MyClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton)); // 2.要定义一个Property 然后增加 对add/remove RoutedEventHandler的处理 public event RoutedEventHandler MyClick { add { base.AddHandler(MyButton.MyClickEvent, value); } remove { base.RemoveHandler(MyButton.MyClickEvent, value); } } // 3. 要通过对底层事件的处理引发自定义的事件。这里重写MouseDoubleClick //protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) //{ // //base.OnMouseLeftButtonDown(e); //base 的函数不起作用 Click 和 Command 都没有处理 // // new一个RoutedEventArgs // RoutedEventArgs re = new RoutedEventArgs(MyButton.MyClickEvent, this); // // Raise it. // base.RaiseEvent(re); // base.OnMouseLeftButtonDown(e); //} // 由于ButtonBase有virtual OnClick 会在OnMouseLeftButtonDown时被调用,可以重写此函数试一试 protected override void OnClick() { // 首先调用base的OnClick try { base.OnClick(); } finally { // new一个RoutedEventArgs RoutedEventArgs re = new RoutedEventArgs(MyButton.MyClickEvent, this); // // Raise it. base.RaiseEvent(re); } }//结果与设想的一样 引发Click,执行命令,并且处理MyClick. } }
/Theme/Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CommandAndEvent"> <Style TargetType="{x:Type local:MyButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MyButton}"> <Border Background="LightBlue" BorderBrush="Black" BorderThickness="1"> <ContentPresenter></ContentPresenter> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
Command binding
实现命令的邦定,需要有命令源,只有实现了ICommandSouce的控件才能作为命令源,还好ButtonBase已经实现了,这里只要创建自己的RoutedCommand就可以了。与创建RoutedEvent类似:
private static RoutedUICommand requery; static ExampleCommands() { InputGestureCollection inputs = new InputGestureCollection(); inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctr+R")); requery = new RoutedUICommand( "Requery text", "Requery name", typeof(ExampleCommands), inputs); } public static RoutedUICommand Requery { get { return requery; } }
然后就是把他们绑在一起就可以了。 详细见
// MainWindow.xaml
<Window x:Class="CommandAndEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CommandAndEvent" Title="MainWindow" Height="350" Width="525"> <WrapPanel> <Button Click="Button_Click" Command="local:ExampleCommands.Requery"> Click Me </Button> <local:MyButton Click="Button_Click" Command="local:ExampleCommands.Requery" MyClick="MyButton_MyClick">My button</local:MyButton> </WrapPanel> <Window.CommandBindings> <CommandBinding Command="local:ExampleCommands.Requery" CanExecute="Requery_CanExecute" Executed="Requery_Executed" /> </Window.CommandBindings> </Window>