为何无法用EventTrigger实现WPF窗口Activated事件的MVVM触发?
现有可行实现(Behavior方式)
我已经通过自定义Behavior实现了登录窗口的激活逻辑,XAML代码如下:
<Window ... xmlns:behaviors="clr-namespace:"..." xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors" . .> <interactivity:Interaction.Behaviors> <behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}"/> </interactivity:Interaction.Behaviors> . . </Window>
对应的Behavior类通过手动订阅Window的Activated事件实现逻辑:
public class ActivateBehavior : Behavior<Window> { . . (此处包含Activated依赖属性等其他代码) . protected override void OnAttached() { AssociatedObject.Activated += OnActivated; AssociatedObject.Deactivated += OnDeactivated; } protected override void OnDetaching() { AssociatedObject.Activated -= OnActivated; AssociatedObject.Deactivated -= OnDeactivated; } void OnActivated(object sender, EventArgs eventArgs) { _isActivated = true; Activated = true; if (string.IsNullOrEmpty(App.UserId)) { LoginView loginView = new LoginView(); loginView.ShowDialog(); } } void OnDeactivated(object sender, EventArgs eventArgs) { _isActivated = false; Activated = false; }
后台代码方案(不符合MVVM)
也可以通过重写Window的OnActivated方法实现,但由于遵循MVVM模式,该方案不可行:
protected override void OnActivated(EventArgs e) { base.OnActivated(e); if (string.IsNullOrEmpty(App.UserId)) { LoginView loginView = new LoginView(); loginView.ShowDialog(); } }
尝试的EventTrigger方案(未生效)
我尝试改用EventTrigger+InvokeCommandAction实现,XAML代码如下:
<Window ... xmlns:behaviors="clr-namespace:"..." xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors" . .> <interactivity:Interaction.Triggers> <interactivity:EventTrigger EventName="Activated"> <interactivity:InvokeCommandAction Command="{Binding OnActivatedCommand}" /> </interactivity:EventTrigger> </interactivity:Interaction.Triggers> . . </Window>
在ViewModel中定义并初始化了对应命令:
public RelayCommand OnActivatedCommand { get; set; } // 构造函数中初始化 OnActivatedCommand = new RelayCommand(o => { if (string.IsNullOrEmpty(App.UserId)) { LoginView loginView = new LoginView(); loginView.ShowDialog(); } });
但该方案中命令从未被触发,说明EventTrigger未捕获到Activated事件。我清楚ViewModel引用View的争议,仅关注为何EventTrigger无法触发该事件的原因。
原因分析
事件类型差异
Window.Activated是CLR事件(非路由事件),而EventTrigger(来自Microsoft.Xaml.Behaviors库)的设计初衷主要是针对路由事件。虽然部分版本的库支持CLR事件,但在事件订阅的底层逻辑上,与直接通过C#代码手动订阅CLR事件存在差异,可能导致无法正确捕获Activated事件。初始化时机冲突
Window的Activated事件会在窗口首次显示时立即触发,而Interaction.Triggers的初始化(包括EventTrigger完成事件订阅)可能晚于这个时机,导致第一次激活事件被错过。即使后续窗口从后台切换回前台时EventTrigger已初始化完成,也会让用户误以为事件完全未触发。
而自定义Behavior<Window>的OnAttached方法会在Behavior附加到Window时立即执行,此时会手动完成Activated事件的订阅,无论事件触发时机早或晚,都能捕获到事件(包括首次激活),因此可以正常工作。
内容的提问来源于stack exchange,提问作者Peter Centellini

