将 EventArgs 作为命令参数传递

我正在使用 Microsoft Expression Blend 4
我有一个浏览器,

[ XAML ] ConnectionView“空代码背后”

        <WebBrowser local:AttachedProperties.BrowserSource="{Binding Source}">
<i:Interaction.Triggers>
<i:EventTrigger>
<i:InvokeCommandAction Command="{Binding LoadedEvent}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Navigated">
<i:InvokeCommandAction Command="{Binding NavigatedEvent}" CommandParameter="??????"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</WebBrowser>

[ C # ] AttachedProperties 类

public static class AttachedProperties
{
public static readonly DependencyProperty BrowserSourceProperty = DependencyProperty . RegisterAttached ( "BrowserSource" , typeof ( string ) , typeof ( AttachedProperties ) , new UIPropertyMetadata ( null , BrowserSourcePropertyChanged ) );


public static string GetBrowserSource ( DependencyObject _DependencyObject )
{
return ( string ) _DependencyObject . GetValue ( BrowserSourceProperty );
}


public static void SetBrowserSource ( DependencyObject _DependencyObject , string Value )
{
_DependencyObject . SetValue ( BrowserSourceProperty , Value );
}


public static void BrowserSourcePropertyChanged ( DependencyObject _DependencyObject , DependencyPropertyChangedEventArgs _DependencyPropertyChangedEventArgs )
{
WebBrowser _WebBrowser = _DependencyObject as WebBrowser;
if ( _WebBrowser != null )
{
string URL = _DependencyPropertyChangedEventArgs . NewValue as string;
_WebBrowser . Source = URL != null ? new Uri ( URL ) : null;
}
}
}

[ C # ] ConnectionViewModel 类

public class ConnectionViewModel : ViewModelBase
{
public string Source
{
get { return Get<string> ( "Source" ); }
set { Set ( "Source" , value ); }
}


public void Execute_ExitCommand ( )
{
Application . Current . Shutdown ( );
}


public void Execute_LoadedEvent ( )
{
MessageBox . Show ( "___Execute_LoadedEvent___" );
Source = ...... ;
}


public void Execute_NavigatedEvent ( )
{
MessageBox . Show ( "___Execute_NavigatedEvent___" );
}
}

[ C # ] ViewModelBase 类 < a href = “ http://viewmodel Support.codebx.com/”> Here

最后:
使用命令绑定可以很好地工作,并显示了 MessageBoxes


我的问题是:
当导航事件发生时,如何将 NavigationEventArgs作为命令参数传递?

121062 次浏览

这并不容易支持,下面是 一篇文章,它提供了如何将 EventArgs 作为命令参数传递的说明。

你可能需要考虑使用 MVVMLight-它直接支持 EventArgs 命令; 你的情况应该是这样的:

 <i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding NavigatedEvent}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>

我不认为你可以做到这一点很容易与 InvokeCommandAction-我会采取看看 EventToCommand从 MVVMLight 或类似。

我所做的是使用 InvokeCommandAction 将加载的控件事件绑定到视图模型中的一个命令,在 Xaml 为控件赋予 x: Name 并传递为 CommandParameter,然后在上述加载的命令钩子视图模型处理程序中直到我需要获取事件 args 的事件。

我试图将依赖性保持在最低限度,所以我自己实现了这一点,而不是使用 MVVMLight 的 EventToCommand。目前为止对我有用,但是欢迎反馈。

答:

<i:Interaction.Behaviors>
<beh:EventToCommandBehavior Command="{Binding DropCommand}" Event="Drop" PassArguments="True" />
</i:Interaction.Behaviors>

视图模型:

public ActionCommand<DragEventArgs> DropCommand { get; private set; }


this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);


private void OnDrop(DragEventArgs e)
{
// ...
}

行为:

/// <summary>
/// Behavior that will connect an UI event to a viewmodel Command,
/// allowing the event arguments to be passed as the CommandParameter.
/// </summary>
public class EventToCommandBehavior : Behavior<FrameworkElement>
{
private Delegate _handler;
private EventInfo _oldEvent;


// Event
public string Event { get { return (string)GetValue(EventProperty); } set { SetValue(EventProperty, value); } }
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));


// Command
public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } }
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));


// PassArguments (default: false)
public bool PassArguments { get { return (bool)GetValue(PassArgumentsProperty); } set { SetValue(PassArgumentsProperty, value); } }
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));




private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var beh = (EventToCommandBehavior)d;


if (beh.AssociatedObject != null) // is not yet attached at initial load
beh.AttachHandler((string)e.NewValue);
}


protected override void OnAttached()
{
AttachHandler(this.Event); // initial set
}


/// <summary>
/// Attaches the handler to the event
/// </summary>
private void AttachHandler(string eventName)
{
// detach old event
if (_oldEvent != null)
_oldEvent.RemoveEventHandler(this.AssociatedObject, _handler);


// attach new event
if (!string.IsNullOrEmpty(eventName))
{
EventInfo ei = this.AssociatedObject.GetType().GetEvent(eventName);
if (ei != null)
{
MethodInfo mi = this.GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
_handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
ei.AddEventHandler(this.AssociatedObject, _handler);
_oldEvent = ei; // store to detach in case the Event property changes
}
else
throw new ArgumentException(string.Format("The event '{0}' was not found on type '{1}'", eventName, this.AssociatedObject.GetType().Name));
}
}


/// <summary>
/// Executes the Command
/// </summary>
private void ExecuteCommand(object sender, EventArgs e)
{
object parameter = this.PassArguments ? e : null;
if (this.Command != null)
{
if (this.Command.CanExecute(parameter))
this.Command.Execute(parameter);
}
}
}

行动指令:

public class ActionCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<T> _action;


public ActionCommand(Action<T> action)
{
_action = action;
}


public bool CanExecute(object parameter) { return true; }


public void Execute(object parameter)
{
if (_action != null)
{
var castParameter = (T)Convert.ChangeType(parameter, typeof(T));
_action(castParameter);
}
}
}

使用 Blend for VisualStudio2013中的行为和操作,您可以使用 InvokeCommandAction。我在 Drop 事件中尝试了这种方法,尽管在 XAML 中没有指定 CommandParameter,但令我惊讶的是,Execute Action 参数包含 DragEventArgs。我假设这种情况会发生在其他事件上,但还没有对它们进行测试。

我知道这是一个相当古老的问题,但是我今天遇到了同样的问题,我对引用所有的 MVVMLight 不太感兴趣,只是为了使用带有事件参数的事件触发器。我过去使用过 MVVMLight,它是一个很棒的框架,但我只是不想再在我的项目中使用它了。

为了解决这个问题,我创建了一个 ULTRA最小化的、 非常喜欢自适应的自定义触发器操作,它允许我绑定到命令,并提供一个事件 args 转换器来将 args 传递给命令的 CanExecute 和 Execute 函数。您不希望一字不差地传递事件参数,因为那将导致视图层类型被发送到视图模型层(这在 MVVM 中绝不应该发生)。

下面是我想出来的 事件指挥执行器课程:

public class EventCommandExecuter : TriggerAction<DependencyObject>
{
#region Constructors


public EventCommandExecuter()
: this(CultureInfo.CurrentCulture)
{
}


public EventCommandExecuter(CultureInfo culture)
{
Culture = culture;
}


#endregion


#region Properties


#region Command


public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}


public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));


#endregion


#region EventArgsConverterParameter


public object EventArgsConverterParameter
{
get { return (object)GetValue(EventArgsConverterParameterProperty); }
set { SetValue(EventArgsConverterParameterProperty, value); }
}


public static readonly DependencyProperty EventArgsConverterParameterProperty =
DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));


#endregion


public IValueConverter EventArgsConverter { get; set; }


public CultureInfo Culture { get; set; }


#endregion


protected override void Invoke(object parameter)
{
var cmd = Command;


if (cmd != null)
{
var param = parameter;


if (EventArgsConverter != null)
{
param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
}


if (cmd.CanExecute(param))
{
cmd.Execute(param);
}
}
}
}

这个类有两个依赖项属性,一个允许绑定到视图模型的命令,另一个允许在事件参数转换期间需要时绑定事件源。如果需要,还可以提供区域性设置(它们默认为当前 UI 区域性)。

这个类允许您调整事件参数,以便它们可以被视图模型的命令逻辑使用。但是,如果只想逐字传递事件参数,那么就不要指定事件参数转换器。

这个触发器操作在 XAML 中的最简单用法如下:

<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<cmd:EventCommandExecuter Command="{Binding Path=Update, Mode=OneTime}" EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

如果需要访问事件源,则将绑定到事件的所有者

<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<cmd:EventCommandExecuter
Command="{Binding Path=Update, Mode=OneTime}"
EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"
EventArgsConverterParameter="{Binding ElementName=SomeEventSource, Mode=OneTime}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

(这里假设将触发器附加到的 XAML 节点已经分配了 x:Name="SomeEventSource"

这个 XAML 依赖于导入一些必需的名称空间

xmlns:cmd="clr-namespace:MyProject.WPF.Commands"
xmlns:c="clr-namespace:MyProject.WPF.Converters"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

并创建一个 IValueConverter(在本例中称为 NameChangedArgsToStringConverter)来处理实际的转换逻辑。对于基本的转换器,我通常创建一个默认的 static readonly转换器实例,然后可以像上面那样直接在 XAML 中引用它。

这种解决方案的好处是,您只需要向任何项目添加一个类,就可以像使用 InvokeCommandAction一样使用交互框架。添加一个单独的类(大约75行)要比添加一个完整的库来完成相同的结果要好得多。

注意

这与@adabyron 的答案有些相似,但它使用的是事件触发器而不是行为。这个解决方案还提供了事件 args 转换能力,并不是说@adabyron 的解决方案不能做到这一点。我真的没有任何好的理由为什么我更喜欢触发器而不是行为,只是个人的选择。IMO 的任何一种策略都是合理的选择。

我总是回到这里寻找答案,所以我想做一个简短的答案。

有多种方法可以做到这一点:

1. 使用 WPF 工具。

添加名称空间:

  • System.Windows.Interactivitiy
  • Microsoft.Expression.Interactions

XAML:

使用 EventName调用您想要的事件,然后在 MethodName中指定您的 Method名称。

<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">


<wi:Interaction.Triggers>
<wi:EventTrigger EventName="SelectionChanged">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="ShowCustomer"/>
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>

密码:

public void ShowCustomer()
{
// Do something.
}

2. 使用 MVVMLight 最困难。

安装 GalaSoft NuGet 软件包。

enter image description here

获取名称空间:

  • System.Windows.Interactivity
  • GalaSoft.MvvmLight.Platform

XAML:

使用 EventName调用所需的事件,然后在绑定中指定 Command名称。如果要传递方法的参数,请将 PassEventArgsToCommand标记为 true。

<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight">


<wi:Interaction.Triggers>
<wi:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding CommandNameHere}"
PassEventArgsToCommand="True" />
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>

代码实现委托: 来源

你必须得到棱镜 MVVM NuGet 包为此。

enter image description here

using Microsoft.Practices.Prism.Commands;


// With params.
public DelegateCommand<string> CommandOne { get; set; }
// Without params.
public DelegateCommand CommandTwo { get; set; }


public MainWindow()
{
InitializeComponent();


// Must initialize the DelegateCommands here.
CommandOne = new DelegateCommand<string>(executeCommandOne);
CommandTwo = new DelegateCommand(executeCommandTwo);
}


private void executeCommandOne(string param)
{
// Do something here.
}


private void executeCommandTwo()
{
// Do something here.
}

没有 DelegateCommand的代码: 来源

using GalaSoft.MvvmLight.CommandWpf


public MainWindow()
{
InitializeComponent();


CommandOne = new RelayCommand<string>(executeCommandOne);
CommandTwo = new RelayCommand(executeCommandTwo);
}


public RelayCommand<string> CommandOne { get; set; }


public RelayCommand CommandTwo { get; set; }


private void executeCommandOne(string param)
{
// Do something here.
}


private void executeCommandTwo()
{
// Do something here.
}

3. 使用 特勒里克事件指挥行为。这是一种选择。

你得下载 NuGet 软件包

返回文章页面

<i:Interaction.Behaviors>
<telerek:EventToCommandBehavior
Command="{Binding DropCommand}"
Event="Drop"
PassArguments="True" />
</i:Interaction.Behaviors>

密码:

public ActionCommand<DragEventArgs> DropCommand { get; private set; }


this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);


private void OnDrop(DragEventArgs e)
{
// Do Something
}

对于刚刚找到这篇文章的人来说,你应该知道在新版本中(不确定确切的版本,因为官方文档在这个主题上很薄) ,如果没有指定 CommandParameter,InvokeCommandAction 的默认行为是将它所附加的事件的 args 作为 CommandParameter 传递。因此,原始海报的 XAML 可以简单地写成:

<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<i:InvokeCommandAction Command="{Binding NavigatedEvent}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

然后在您的命令中,您可以接受 NavigationEventArgs类型的参数(或者任何适当的事件参数类型) ,它将自动提供。

除了 joshb 已经说过的,这对我来说很有用。确保添加对 Microsoft 的引用。表情。Interactions.dll 和 System。窗户。Interactivity.dll 和在 xaml 中执行:

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

我最终使用了类似的东西来满足我的需要,这表明你也可以传递一个自定义参数:

<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">


<i:InvokeCommandAction Command="{Binding Path=DataContext.RowSelectedItem, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>

下面是@adabyron 的解答版本,它可以防止 EventArgs抽象的泄漏。

首先,修改后的 EventToCommandBehavior类(现在是一个通用的抽象类,并且使用 ReSharper 代码清理进行了格式化)。注意新的 GetCommandParameter虚拟方法及其默认实现:

public abstract class EventToCommandBehavior<TEventArgs> : Behavior<FrameworkElement>
where TEventArgs : EventArgs
{
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null, OnEventChanged));
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null));
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(false));
private Delegate _handler;
private EventInfo _oldEvent;


public string Event
{
get { return (string)GetValue(EventProperty); }
set { SetValue(EventProperty, value); }
}


public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}


public bool PassArguments
{
get { return (bool)GetValue(PassArgumentsProperty); }
set { SetValue(PassArgumentsProperty, value); }
}


protected override void OnAttached()
{
AttachHandler(Event);
}


protected virtual object GetCommandParameter(TEventArgs e)
{
return e;
}


private void AttachHandler(string eventName)
{
_oldEvent?.RemoveEventHandler(AssociatedObject, _handler);


if (string.IsNullOrEmpty(eventName))
{
return;
}


EventInfo eventInfo = AssociatedObject.GetType().GetEvent(eventName);


if (eventInfo != null)
{
MethodInfo methodInfo = typeof(EventToCommandBehavior<TEventArgs>).GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);


_handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
eventInfo.AddEventHandler(AssociatedObject, _handler);
_oldEvent = eventInfo;
}
else
{
throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().FullName}'.");
}
}


private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (EventToCommandBehavior<TEventArgs>)d;


if (behavior.AssociatedObject != null)
{
behavior.AttachHandler((string)e.NewValue);
}
}


// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
private void ExecuteCommand(object sender, TEventArgs e)
{
object parameter = PassArguments ? GetCommandParameter(e) : null;


if (Command?.CanExecute(parameter) == true)
{
Command.Execute(parameter);
}
}
}

接下来,一个隐藏 DragCompletedEventArgs的派生类示例。有些人担心将 EventArgs抽象泄露到他们的视图模型组装中。为了防止这种情况,我创建了一个表示我们所关心的值的接口。接口可以存在于视图模型程序集中,在 UI 程序集中使用私有实现:

// UI assembly
public class DragCompletedBehavior : EventToCommandBehavior<DragCompletedEventArgs>
{
protected override object GetCommandParameter(DragCompletedEventArgs e)
{
return new DragCompletedArgs(e);
}


private class DragCompletedArgs : IDragCompletedArgs
{
public DragCompletedArgs(DragCompletedEventArgs e)
{
Canceled = e.Canceled;
HorizontalChange = e.HorizontalChange;
VerticalChange = e.VerticalChange;
}


public bool Canceled { get; }
public double HorizontalChange { get; }
public double VerticalChange { get; }
}
}


// View model assembly
public interface IDragCompletedArgs
{
bool Canceled { get; }
double HorizontalChange { get; }
double VerticalChange { get; }
}

将命令参数强制转换为 IDragCompletedArgs,类似于@adabyron 的回答。

作为对@Mike Fuchs 答案的改编,这里有一个更小的解决方案。我正在使用 Fody.AutoDependencyPropertyMarker来减少一些锅炉板。

班级

public class EventCommand : TriggerAction<DependencyObject>
{
[AutoDependencyProperty]
public ICommand Command { get; set; }


protected override void Invoke(object parameter)
{
if (Command != null)
{
if (Command.CanExecute(parameter))
{
Command.Execute(parameter);
}
}
}
}

EventArgs

public class VisibleBoundsArgs : EventArgs
{
public Rect VisibleVounds { get; }


public VisibleBoundsArgs(Rect visibleBounds)
{
VisibleVounds = visibleBounds;
}
}

XAML

<local:ZoomableImage>
<i:Interaction.Triggers>
<i:EventTrigger EventName="VisibleBoundsChanged" >
<local:EventCommand Command="{Binding VisibleBoundsChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:ZoomableImage>

视图模型

public ICommand VisibleBoundsChanged => _visibleBoundsChanged ??
(_visibleBoundsChanged = new RelayCommand(obj => SetVisibleBounds(((VisibleBoundsArgs)obj).VisibleVounds)));

如果没有设置 CommandParameter,Prism 的 InvokeCommandAction将默认传递事件参数。

Https://learn.microsoft.com/en-us/previous-versions/msp-n-p/gg405494(v=pandp.40)#passing-eventargs-parameters-to-the-command

下面是一个例子。请注意使用 prism:InvokeCommandAction而不是 i:InvokeCommandAction

<i:Interaction.Triggers>
<i:EventTrigger EventName="Sorting">
<prism:InvokeCommandAction Command="{Binding SortingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

视图模型

    private DelegateCommand<EventArgs> _sortingCommand;


public DelegateCommand<EventArgs> SortingCommand => _sortingCommand ?? (_sortingCommand = new DelegateCommand<EventArgs>(OnSortingCommand));


private void OnSortingCommand(EventArgs obj)
{
//do stuff
}

有一个新版本的 Prismlibrary 文档

我知道这有点晚了,但是,微软已经做出了他们的 Xaml。行为是开源的,现在只用一个名称空间就可以更容易地使用交互性。

  1. 首先向项目中添加 Microsoft.Xaml.Performors.WpfNuget 包。
    Https://www.nuget.org/packages/microsoft 行为
  2. 将 xmlns: actions = “ http://schemas.microsoft.com/xaml/behaviors" ; 名称空间添加到您的 谢谢。

然后像这样使用它,

<Button Width="150" Style="{DynamicResource MaterialDesignRaisedDarkButton}">
<behaviours:Interaction.Triggers>
<behaviours:EventTrigger EventName="Click">
<behaviours:InvokeCommandAction Command="{Binding OpenCommand}" PassEventArgsToCommand="True"/>
</behaviours:EventTrigger>
</behaviours:Interaction.Triggers>
Open
</Button>

PassEventArgsToCommand = “ True”应该设置为 True,您实现的 RelayCommand 可以采用 RoutedEventArgs 或对象作为模板。如果使用 object 作为参数类型,只需将其强制转换为适当的事件类型。

命令看起来像这样,

OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });

Command 方法看起来像这样,

private void OnOpenClicked(object parameter)
{
Logger.Info(parameter?.GetType().Name);
}

“参数”将是 Routed 事件对象。

如果你好奇的话,

2020-12-1511:40:36.3600 | INFO | MyApplication. ViewModel. MainWindowViewModel. RoutedEventArgs

如您所见,记录的 TypeName 是 RoutedEventArgs

RelayCommand 实现可以在这里找到。

为什么是 RelayCommand

附注: 您可以绑定到任何控件的任何事件。像窗口关闭事件,你会得到相应的事件。