WPF的自定义控件怎样添加Click事件

WPF的自定义控件怎样添加Click事件,第1张

首先要给控件添加属性,然后为其添加事件,最后添加命令,具体步骤如下:

1,为控件添加属性(依赖属性,DependencyProperty)

正如下面的代码所示:

public static readonly DependencyProperty TimeProperty = 

            DependencyProperty.Register("Time", typeof(DateTime), typeof(ClockUserCtrl), 

            new FrameworkPropertyMetadata(DateTime.Now,new PropertyChangedCallback(TimePropertyChangedCallback)))

我们为控件(或者任何一个WPF类)添加的依赖属性都是"公开的","静态的","只读的",其命名方式是"属性名+Property",这是依赖属性一成不变的书写方式.对于依赖属性的注册可以在声明该属性时就调用DependencyProperty.Register()方法注册,也可以在其静态构造方法中注册.上面的DependencyProperty.Register方法的几个参数分别是:属性名(该属性名与声明的依赖属性名称"XXXProperty"相比仅仅是少了"Property"后缀,其它完全一样,否则在运行时会报异常),属性的数据类型,属性的拥有者的类型,元数据.

关于参数中传递的元数据:如果是普通的类则应该传递PropertyMetadata,如果是FrameworkElement则可以传递FrameworkPropertyMetadata,其中FrameworkPropertyMetadata中可以制定一些标记表明该属性发生变化时控件应该做出什么反应,比如某属性的变化会影响到该控件的绘制,那么就应该像这样书写该属性的元数据: new

FrameworkPropertyMetadata(defauleValue,

FrameworkPropertyMetadataOptions.AffectsRender)这样当该属性发生变化时系统会考虑重绘该控件.另外元数据中还保护很多内容,比如默认值,数据验证,数据变化时的回调函数,是否参与属性"继承"等.

然后,我们将该依赖属性包装成普通属性:

[Description("获取或设置当前日期和时间")]

        [Category("Common Properties")]

        public DateTime Time

        {

            get

            {

                return (DateTime)this.GetValue(TimeProperty)

            }

            set

            {

                this.SetValue(TimeProperty, value)

            }

        }

GetValue和SetValue方法来自于DependencyObject类,其用于获取或设置类的某属性值.

注意:在将依赖属性包装成普通属性时,在get和set块中除了按部就班的调用GetValue和SetValue方法外,不要进行任何其它的 *** 作.下面的代码是不恰当的:

   [Description("获取或设置当前日期和时间")]

        [Category("Common Properties")]

        public DateTime Time

        {

            get

            {

                return (DateTime)this.GetValue(TimeProperty)

            }

            set

            {

                this.SetValue(TimeProperty, value)

                this.OnTimeUpdated(value)//Error

            }

        }

在以前这或许是很多人的惯用写法,但在WPF中,这样的写法存在潜在的错误,原因如下:我们知道继承于DependencyObject的类拥有GetValue和SetValue方法来获取或设置属性值,那为什么我们不直接使用该方法来获取或设置属性值,而要将其包装成普通的.NET属性呢,事实上在这里两种方式都是可以的,只不过包装成普通的.NET属性更符合.NET开发人员的习惯,使用GetValue和SetValue更像JAVA开发人员的习惯,但XAML在执行时似乎于JAVA开发人员一样,其不会调用.NET属性而是直接使用GetValue或SetValue方法,这样一来,我们写在get块和set块中的其它代码根本不会被XAML执行到.所以说,就上面的Time属性而言,C#(或其它)对该属性的调用不会出现任何问题,但该属性被用在XAML中时(比如在XAML对该属性进行数据绑定等),其set块中的this.OnTimeUpdated(value)语句不会被执行到.

那么,当Time属性发生变化时的确需要调用this.OnTimeUpdated(value)语句(因为该语句会引发时间被更新了的事件),还是在传递的依赖属性元数据做文章:

new FrameworkPropertyMetadata(DateTime.Now,new PropertyChangedCallback(TimePropertyChangedCallback)),我们为属性的变化指定了一个回调函数,当该属性变化时该回调函数就会被执行:

  private static void TimePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs arg)

        {

            if (sender != null && sender is ClockUserCtrl)

            {

                ClockUserCtrl clock = sender as ClockUserCtrl

                clock.OnTimeUpdated((DateTime)arg.OldValue, (DateTime)arg.NewValue)

                

            }

        }

2,为控件添加事件(传阅事件,RoutedEvent)

添加传阅事件的方法与添加依赖属性的方法很类似:

  public static readonly RoutedEvent TimeUpdatedEvent = 

            EventManager.RegisterRoutedEvent("TimeUpdated",

             RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime>), typeof(ClockUserCtrl))

其支持方法EventManager.RegisterRoutedEvent()对应的几个参数分别为:事件名称,事件传阅的方式(向上传阅,向下传阅或不传阅),事件对应的EventHandler的类型,事件拥有者的类型)

然后将事件包装成普通的.NET事件:

 [Description("日期或时间被更新后发生")]

        public event RoutedPropertyChangedEventHandler<DateTime> TimeUpdated

        {

            add

            {

                this.AddHandler(TimeUpdatedEvent, value)

            }

            remove

            {

                this.RemoveHandler(TimeUpdatedEvent, value)

            }

        }

注意,与依赖属性一样,不要在add与remove块中添加除AddHandler与RemoveHandler以外的代码.

题外话,事件参数中的e.Handled=true并不是终止事件的传阅,这只是为事件做一个标记而已,以便在默认情况下的让那些事件处理函数在该标记为true的情况下不被调用,要为该标记为true的事件注册处理方法并让该方法得到执行,请使用AddHandler方法,并把最后一个参数handlerEventsToo设置为true,如下:

this.myInkCanvas.AddHandler(

      InkCanvas.MouseLeftButtonDownEvent,

      new MouseButtonEventHandler(

          myInkCanvas_MouseLeftButtonDown),

      true)

private void myInkCanvas_MouseLeftButtonDown(

       object sender, MouseButtonEventArgs e)

{

       //do something

}

然后编写惯用的OnXXX方法:

  protected virtual void OnTimeUpdated(DateTime oldValue, DateTime newValue)

        {

            RoutedPropertyChangedEventArgs<DateTime> arg = 

                new RoutedPropertyChangedEventArgs<DateTime>(oldValue, newValue,TimeUpdatedEvent)

            this.RaiseEvent(arg)

            

        }

3,为控件添加命令(Commands)

能为自定义控件添加如WPF内置控件一样的命令是一件很不错的事情(事实上这也是在CustomControl中降低界面和后台逻辑耦合度的一种方法,本系列随笔中的下一篇中将会具体谈谈).

WPF中内置的命令有两大类型:RoutedCommand以及RoutedUICommand,后者比前者多了一个Text属性用于在界面上自动本地化地显示该命令对应的文本,更多的可以参考WPF中的命令与命令绑定(一)以及WPF中的命令与命令绑定(二).

这里我们来定义一个命令,其功能是控件的语音报时.首先我们定义一个命令:

public static readonly RoutedUICommand SpeakCommand = new RoutedUICommand("Speak", "Speak", typeof(ClockUserCtrl))

参数分别为命名的显示名称,命令的名称,命令的拥有者类型.

然后在控件的静态函数中定义一个命令绑定,该命令绑定定义了命令的具体细节:对应的命令是什么?其完成什么样的功能,当前环境下其能执行吗?

     CommandBinding commandBinding = new CommandBinding(SpeakCommand, new ExecutedRoutedEventHandler(ExecuteSpeak),                

     new CanExecuteRoutedEventHandler(CanExecuteSpeak))

        private static void ExecuteSpeak(object sender, ExecutedRoutedEventArgs arg)        

        {            

            ClockUserCtrl clock = sender as ClockUserCtrl            

            if (clock != null)            

            {                

            clock.SpeakTheTime()            

            }        

        }        

        private static void CanExecuteSpeak(object sender, CanExecuteRoutedEventArgs arg)        

        {            

        ClockUserCtrl clock = sender as ClockUserCtrl            

        arg.CanExecute = (clock != null)        

        }

CanExecuteRoutedEventArgs的CanExecute属性用于指示当前命令是否可用,也就是说系统会不断地检视该命令与该命令的作用对象,并根据你所提供的条件来判断当前命令是否可用,比如文本框状态变为"只读"后,其"粘贴"命令将不可用,作用于该文本框的粘贴按钮会自动被禁用,反之则启用.

new ExecutedRoutedEventHandler(ExecuteSpeak)委托指定了当该命令被执行时所要完成的任务,这通过回调ExcuteSpeak函数来实现.

private static void ExecuteSpeak(object sender, ExecutedRoutedEventArgs arg)        

{            

                ClockUserCtrl clock = sender as ClockUserCtrl      

                if (clock != null)            

                {                

                clock.SpeakTheTime()            

                }        

  }

 private void SpeakTheTime()        

  {            

        DateTime localTime = this.Time.ToLocalTime()

        string textToSpeak = "现在时刻," + localTime.ToShortDateString() +","+ localTime.ToShortTimeString() +",星期" + (int)localTime.DayOfWeek            

        this.speecher.SpeakAsync(textToSpeak)        

   }

我们也可以为命令添加快捷键,这是通过InputBinding来实现的,其将命令与命令的快捷键关联起来,比如:

InputBinding inputBinding = new InputBinding(SpeakCommand, new MouseGesture(MouseAction.LeftClick))

CommandManager.RegisterClassInputBinding(typeof(ClockUserCtrl), inputBinding)

这样,当我们鼠标点击控件时就会引发控件的Speak命令,从而调用SpeakTheTime函数进行语音播报.

快捷键可以通过MouseGesture或KeyGesture来定义.

双击按钮。

在代码编辑器中,将随即创建 Click 事件处理程序,并且光标放置在该事件处理程序中。

将以下代码添加到事件处理程序中:C# code

MessageBox.Show("Event handler was created by " + "double-clicking the button.")

从“工具箱”中将第二个“Button”控件拖到 WPF 设计图面上,然后选择该按钮。

向 XAML 编辑器中的 Button 元素添加一个名为 Click 的属性,并将属性值设置为 ButtonOKClicked。此名称便是要指定给代码中的事件处理程序的名称。例如,该属性可以编写为:Click="ButtonOKClicked"。

右击设计器,然后单击“查看代码”。

向 Window1 类添加以下事件处理程序。单击按钮时,此代码将显示一条消息。

private void ButtonOKClicked(object sender, RoutedEventArgs e){MessageBox.Show("Event handler was created manually.")}

按 F5 运行程序。

当窗口出现时,单击按钮。

验证在单击每个按钮时消息框中显示的文本是否正确,然后关闭该应用程序。

触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行 *** 作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。 触发器可以从 DBA_TRIGGERS ,USER_TRIGGERS 数据字典中查到。SQL3的触发器是一个能由系统自动执行对数据库修改的语句。

触发器可以查询其他表,而且可以包含复杂的SQL语句。它们主要用于强制服从复杂的业务规则或要求。例如:您可以根据客户当前的帐户状态,控制是否允许插入新订单。

触发器也可用于强制引用完整性,以便在多个表中添加、更新或删除行时,保留在这些表之间所定义的关系。然而,强制引用完整性的最好方法是在相关表中定义主键和外键约束。如果使用数据库关系图,则可以在表之间创建关系以自动创建外键约束。

触发器与存储过程的唯一区别是触发器不能执行EXECUTE语句调用,而是在用户执行Transact-SQL语句时自动触发执行。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/bake/11889479.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存