自定义传递事件

书名:WPF专业编程指南
作者:李应保
出版社:电子工业出版社
出版时间:2010-01
ISBN:9787121100116


一、自定义传递事件

自定义传递事件步骤

声明一个传递事件

  • 声明一个传递事件的语法为:
  public static readonly RoutedEvent   YclickEvent;
  public static readonly RoutedEvent   PreviewYclickEvent;
  • 在这里定义了YclickEvent和PreviewYclickEvent两个传递事件,其类型为RoutedEvent,它们都被说明为static和readonly的,这是必需的。

在WPF传递事件系统中注册传递事件

  • 与相关属性需要在相关属性系统中注册一样,也需要向传递系统注册传递事件。
  YclickEvent=EventManager.RegisterRoutedEvent("Yclick",
  RoutingStrategy.Bubble, typeof(RoutedEventHandler),
  typeof( Ybutton)  );
  PreviewYclickEvent =EventManager.RegisterRoutedEvent("Yclick",
  RoutingStrategy.Tunnel, typeof(RoutedEventHandler),
  typeof( Ybutton)  );
  • WPF用RoutingStrategy来定义传递事件的传递方式,它取Tunnel、Bubble和Direct三个值,分别对应于潜入传递、冒泡传递和直接传递(只传递到自身类)三种传递方式。
    在这里,我把YclickEvent的传递方式设为冒泡方式,把PreviewYclickEvent设为潜入方式。
    第三个参数为传递事件处理函数的类型,第四个参数说明传递事件是在哪个类中定义的。

定义传递事件的接入属性

  public event RoutedEventHandler  Yclick
  {
      add {AddHandler(YclickEvent,value);}
      remove{RemoveHandler(YclickEvent,value);}
  }
  public event RoutedEventHandler  PreviewYclick
  {
      add {AddHandler(PreviewYclickEvent,value);}
      remove{RemoveHandler(PreviewYclickEvent,value);}
  }
  • AddHandler和RemoveHandler是DependencyObject中的两个方法,所以为了使用自定义传递事件,Ybutton的继承树上需要有DependencyObject类。

产生传递事件

  • 要在WPF传递事件系统中产生传递事件,需要调用UIElement类中的RaiseEvent方法:
  RoutedEventArgs argsEvent = new RoutedEventArgs();
  argsEvent.RoutedEvent = YButton.PreviewYclickEvent;
  argsEvent.Source = this;
  RaiseEvent(argsEvent);

好了,在完成了上面四个步骤之后,就可以使用自己定义的事项了。

二、例子

  • 我们创建的按钮是由一个StackPanel、两个Ellipse和一个TextBlock组成的,样子有点奇怪。
    主要的目的是要展示传递事件如何在元素树中传播。
    为了演示如何定义并使用传递事件,在下面的例子中,
    还是创建一个按钮,不过这个按钮不只是换一下视觉元素,而是做一点内部较深入的工作,让YButton类从Control类中派生出来:
  using System;
  using System.Globalization;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Input;
  using System.Windows.Media;
  namespace Yingbao.Chapter7
  {
    public class YButton : Control
    {
        FormattedText formtxt;
        bool isMouseReallyOver;
        public static readonly DependencyProperty TextProperty;
        public static readonly RoutedEvent YclickEvent;
        public static readonly RoutedEvent PreviewYclickEvent;
        static YButton()
        {
            TextProperty =DependencyProperty.Register("Text",
                typeof(string),typeof(YButton),
                new FrameworkPropertyMetadata(" ",
              FrameworkPropertyMetadataOptions.AffectsMeasure));
            YclickEvent = EventManager.RegisterRoutedEvent(
                        "Yclick", RoutingStrategy.Bubble,
                        typeof(RoutedEventHandler), typeof(YButton));
            PreviewYclickEvent = EventManager.RegisterRoutedEvent(
                        "PreviewYclick",RoutingStrategy.Tunnel,
                        typeof(RoutedEventHandler), typeof(YButton));
        }
        public string Text
        {
            set { SetValue(TextProperty, value == null ? " " :
              value); }
            get { return (string)GetValue(TextProperty); }
        }
        public event RoutedEventHandler Yclick
        {
            add { AddHandler(YclickEvent, value); }
            remove { RemoveHandler(YclickEvent, value); }
        }
        public event RoutedEventHandler PreviewYclick
        {
            add { AddHandler(PreviewYclickEvent, value); }
            remove { RemoveHandler(PreviewYclickEvent, value); }
        }
        protected override Size MeasureOverride(Size sizeAvailable)
        {
            formtxt = new FormattedText(Text,
                    CultureInfo.CurrentCulture, FlowDirection,
                    new Typeface(FontFamily, FontStyle, FontWeight,
                    FontStretch), FontSize, Foreground);
            Size sizeDesired = new Size(Math.Max(48, formtxt.Width)
                    + 4,formtxt.Height + 4);
            sizeDesired.Width += Padding.Left + Padding.Right;
            sizeDesired.Height += Padding.Top + Padding.Bottom;
            return sizeDesired;
        }
        protected override void OnRender(DrawingContext dc)
        {
            Brush brushBackground = SystemColors.ControlBrush;
            if (isMouseReallyOver && IsMouseCaptured)
              brushBackground = SystemColors.ControlDarkBrush;
            Pen pen = new Pen(Foreground, IsMouseOver ? 2 : 1);
            dc.DrawRoundedRectangle(brushBackground, pen,
                    new Rect(new Point(0, 0), RenderSize), 4, 4);
            formtxt.SetForegroundBrush( IsEnabled ? Foreground :
                  SystemColors.ControlDarkBrush);
            Point ptText = new Point(2, 2);
            switch (HorizontalContentAlignment)
            {
              case HorizontalAlignment.Left:
                  ptText.X += Padding.Left;
                  break;
              case HorizontalAlignment.Right:
                  ptText.X += RenderSize.Width - formtxt.Width -
                                Padding.Right;
                  break;
              case HorizontalAlignment.Center:
              case HorizontalAlignment.Stretch:
                  ptText.X += (RenderSize.Width - formtxt.Width -
                            Padding.Left - Padding.Right) / 2;
                  break;
            }
            switch (VerticalContentAlignment)
            {
              case VerticalAlignment.Top:
                  ptText.Y += Padding.Top;
                  break;
              case VerticalAlignment.Bottom:
                  ptText.Y += RenderSize.Height - formtxt.Height -
                                Padding.Bottom;
                  break;
              case VerticalAlignment.Center:
              case VerticalAlignment.Stretch:
                  ptText.Y += (RenderSize.Height - formtxt.Height -
                            Padding.Top - Padding.Bottom) / 2;
                  break;
            }
            dc.DrawText(formtxt, ptText);
        }
        protected override void OnMouseEnter(MouseEventArgs args)
        {
            base.OnMouseEnter(args);
            InvalidateVisual();
        }
        protected override void OnMouseLeave(MouseEventArgs args)
        {
            base.OnMouseLeave(args);
            InvalidateVisual();
        }
        protected override void OnMouseMove(MouseEventArgs args)
        {
            base.OnMouseMove(args);
            Point pt = args.GetPosition(this);
            bool isReallyOverNow = (pt.X >= 0 && pt.X < ActualWidth
                    && pt.Y >= 0 && pt.Y < ActualHeight);
            if (isReallyOverNow != isMouseReallyOver)
            {
              isMouseReallyOver = isReallyOverNow;
              InvalidateVisual();
            }
        }
      protected override void OnMouseLeftButtonDown(
                        MouseButtonEventArgs args)
        {
            base.OnMouseLeftButtonDown(args);
            CaptureMouse();
            InvalidateVisual();
            args.Handled = true;
        }
        protected override void OnMouseLeftButtonUp(
                        MouseButtonEventArgs args)
        {
            base.OnMouseLeftButtonUp(args);
            if (IsMouseCaptured)
            {
              if (isMouseReallyOver)
              {
                  OnPreviewYclick();
                  OnYclick();
              }
              args.Handled = true;
              Mouse.Capture(null);
            }
        }
        protected override void OnLostMouseCapture(
                  MouseEventArgs args)
        {
            base.OnLostMouseCapture(args);
            InvalidateVisual();
        }
        protected override void OnKeyDown(KeyEventArgs args)
        {
            base.OnKeyDown(args);
            if (args.Key == Key.Space || args.Key == Key.Enter)
              args.Handled = true;
        }
        protected override void OnKeyUp(KeyEventArgs args)
        {
            base.OnKeyUp(args);
            if (args.Key == Key.Space || args.Key == Key.Enter)
            {
              OnPreviewYclick();
              OnYclick();
              args.Handled = true;
            }
        }
        protected virtual void OnYclick()
        {
            RoutedEventArgs argsEvent = new RoutedEventArgs();
            argsEvent.RoutedEvent = YButton.YclickEvent;
            argsEvent.Source = this;
            RaiseEvent(argsEvent);
        }
        protected virtual void OnPreviewYclick()
        {
            RoutedEventArgs argsEvent = new RoutedEventArgs();
            argsEvent.RoutedEvent = YButton.PreviewYclickEvent;
            argsEvent.Source = this;
            RaiseEvent(argsEvent);
        }
    }
  }
  • 在YButton中,定义了两个传递事件:一个是Yclick,另一个是PreviewYclick;一个相关属性TextProperty。
    为了产生传递事件,在YButton中截获了按下鼠标左键的传递事件,并把RoutedEventArgs类中的Handled的属性设为true,以阻止传递事件的传播。
    在释放鼠标左键的时候,首先产生的是PreviewYclick事件,再产生Yclick事件。
    类似地,还截获了空格键和回车键,在用户按下或释放这两个键的过程中产生Yclick和PreviewYclick传递事件。

  • 使用YButton的程序如下:

  <Window x:Class="Yingbao.Chapter7.UsingYButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:src="clr-namespace:Yingbao.Chapter7"
    Title="使用自己的按钮" Height="500" Width="700"
    src:YButton.PreviewYclick="OnPreviewYclick"
    src:YButton.Yclick="OnYclick">
  <StackPanel Orientation ="Horizontal"
    src:YButton.PreviewYclick="OnPreviewYclick"
    src:YButton.Yclick="OnYclick">
  <StackPanel  src:YButton.PreviewYclick="OnPreviewYclick"
    src:YButton.Yclick="OnYclick">
    <src:YButton x:Name="myButton1" Text=" 使用自定义按钮1"/>
    <src:YButton x:Name="myButton2" Text=" 使用自定义按钮2"/>
    <src:YButton x:Name="myButton3" Text=" 使用自定义按钮3"/>
  </StackPanel>
    <StackPanel Width ="560"  Margin ="20" Height ="450">
      <Label  Name ="EventOutputHeader" FontSize ="12"> </Label>
      <ScrollViewer Height ="400">
        <StackPanel Name="EventOutput">
        </StackPanel>
      </ScrollViewer>
    </StackPanel>
  </StackPanel>
  </Window>
  • 后台C#程序如下:
  using System;
  using System.Collections.Generic;
  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.Shapes;
  namespace Yingbao.Chapter7
  {
        public partial class UsingYButton : System.Windows.Window
    {
        string outputFormat = "{0,-40}{1,-30}{2,-30}{3,-30}";
        public UsingYButton()
        {
            InitializeComponent();
            EventOutputHeader.Content = string.Format(outputFormat,
        "Routed Event ", "Sender", "Source", "Original Source");
        }
        private void OnPreviewYclick(object sender,
                    RoutedEventArgs rea)
        {
            RoutedEventHandler(sender, rea);
        }
        private void OnYclick(object sender, RoutedEventArgs rea)
        {
            RoutedEventHandler(sender, rea);
        }
        private void RoutedEventHandler(object sender,
                RoutedEventArgs rea)
        {
            TextBlock text = new TextBlock();
            text.Text = string.Format(outputFormat,
                    rea.RoutedEvent.Name, GetTypeName(sender),
                    GetTypeName(rea.Source),
                    GetTypeName(rea.OriginalSource));
            EventOutput.Children.Add(text);
            (EventOutput.Parent as ScrollViewer).ScrollToBottom();
            while (EventOutput.Children.Count > 40)
            {
              EventOutput.Children.RemoveAt(0);
            }
        }
        private string GetTypeName(object obj)
        {
            string[] name = obj.GetType().ToString().Split('.');
            return name[name.Length - 1];
        }
    }
  }
  • 这个例子可以考察自定义的传递事件,也可以按照冒泡和潜入两种方式传播。按下按钮,我们可以看到PreviewYclick和Yclick事件的传递过程。图7-8示出了上面程序的运行结果,在XAML中使用YButton按钮,和使用WPF本身所提供的控件几乎没什么区别,这样,就可以根据自己的需要,很方便地对WPF控件进行自己的扩展。


    图7-8 使用自定义传递事件
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容