我在一个WPF项目中使用Blend SDK KeyTrigger,每次我按下指定的键,这里是DeleteCommand,就会多次触发该事件。
<ei:KeyTrigger FiredOn="KeyDown" ActiveOnFocus="True" SourceName="repositoryPackages" Key="Delete">
<i:InvokeCommandAction Command="{Binding SelectedItem.DeleteCommand, repositoryPackages}" />
</ei:KeyTrigger>此触发器位于ListView的trigger集合中,而trigger集合本身位于用户控件内的网格上。
然后,将用户控件嵌入到应用程序主窗口上的WPF TabControl的选项卡上。
每次我使用ListView切换到选项卡时,触发器都会再次无限地调用该命令。
我查看了KeyTrigger的源代码(在Microsoft.Expressions.Interactions中),并注意到以下几行:
protected override void OnEvent(EventArgs eventArgs)
{
if (this.ActiveOnFocus)
{
this.targetElement = base.Source;
}
else
{
this.targetElement = GetRoot(base.Source);
}
if (this.FiredOn == KeyTriggerFiredOn.KeyDown)
{
this.targetElement.KeyDown += new KeyEventHandler(this.OnKeyPress);
}
else
{
this.targetElement.KeyUp += new KeyEventHandler(this.OnKeyPress);
}
}每当触发器的关联元素获取OnLoaded事件时,都会调用一次OnEvent方法。但每次激活选项卡时,TabControl上的元素都会获得一个OnLoaded事件。这意味着您每次都要向KeyDown/KeyUp添加相同的事件处理程序。
对我来说,这看起来真的是Blend SDK KeyTrigger实现中的一个很大的疏忽。
有没有人有办法防止这种情况发生,或者是如何实现正确的KeyTrigger?
发布于 2011-05-10 17:48:53
您是否尝试过使用FiredOn="KeyUp"?KeyDown也可以被操作系统重复,我想你也不想重复删除吧?
发布于 2012-01-12 20:49:05
KeyTrigger在已加载的事件上注册te向下/向上键事件。
public class KeyTrigger : EventTriggerBase<UIElement>
{
// Fields
public static readonly DependencyProperty ActiveOnFocusProperty = DependencyProperty.Register("ActiveOnFocus", typeof(bool), typeof(KeyTrigger));
public static readonly DependencyProperty FiredOnProperty = DependencyProperty.Register("FiredOn", typeof(KeyTriggerFiredOn), typeof(KeyTrigger));
public static readonly DependencyProperty KeyProperty = DependencyProperty.Register("Key", typeof(Key), typeof(KeyTrigger));
public static readonly DependencyProperty ModifiersProperty = DependencyProperty.Register("Modifiers", typeof(ModifierKeys), typeof(KeyTrigger));
private UIElement targetElement;
// Methods
private static ModifierKeys GetActualModifiers(Key key, ModifierKeys modifiers)
{
if ((key == Key.LeftCtrl) || (key == Key.RightCtrl))
{
modifiers |= ModifierKeys.Control;
return modifiers;
}
if (((key == Key.LeftAlt) || (key == Key.RightAlt)) || (key == Key.System))
{
modifiers |= ModifierKeys.Alt;
return modifiers;
}
if ((key == Key.LeftShift) || (key == Key.RightShift))
{
modifiers |= ModifierKeys.Shift;
}
return modifiers;
}
protected override string GetEventName()
{
return "Loaded";
}
private static UIElement GetRoot(DependencyObject current)
{
UIElement element = null;
while (current != null)
{
element = current as UIElement;
current = VisualTreeHelper.GetParent(current);
}
return element;
}
protected override void OnDetaching()
{
if (this.targetElement != null)
{
if (this.FiredOn == KeyTriggerFiredOn.KeyDown)
{
this.targetElement.KeyDown -= new KeyEventHandler(this.OnKeyPress);
}
else
{
this.targetElement.KeyUp -= new KeyEventHandler(this.OnKeyPress);
}
}
base.OnDetaching();
}
protected override void OnEvent(EventArgs eventArgs)
{
if (this.ActiveOnFocus)
{
this.targetElement = base.Source;
}
else
{
this.targetElement = GetRoot(base.Source);
}
if (this.FiredOn == KeyTriggerFiredOn.KeyDown)
{
this.targetElement.KeyDown += new KeyEventHandler(this.OnKeyPress);
}
else
{
this.targetElement.KeyUp += new KeyEventHandler(this.OnKeyPress);
}
}
private void OnKeyPress(object sender, KeyEventArgs e)
{
if ((e.Key == this.Key) && (Keyboard.Modifiers == GetActualModifiers(e.Key, this.Modifiers)))
{
base.InvokeActions(e);
}
}
// Properties
public bool ActiveOnFocus
{
get
{
return (bool)base.GetValue(ActiveOnFocusProperty);
}
set
{
base.SetValue(ActiveOnFocusProperty, value);
}
}
public KeyTriggerFiredOn FiredOn
{
get
{
return (KeyTriggerFiredOn)base.GetValue(FiredOnProperty);
}
set
{
base.SetValue(FiredOnProperty, value);
}
}
public Key Key
{
get
{
return (Key)base.GetValue(KeyProperty);
}
set
{
base.SetValue(KeyProperty, value);
}
}
public ModifierKeys Modifiers
{
get
{
return (ModifierKeys)base.GetValue(ModifiersProperty);
}
set
{
base.SetValue(ModifiersProperty, value);
}
}
}例如,在选项卡控件中,当您切换到另一个选项卡时,您将获得已卸载的事件,当您返回到您的选项卡时,您将再次获得已加载的事件。这会导致再次注册keydown/up事件。
这似乎是微软的一个bug,因为我认为他们应该在卸载时注销事件!
在KeyTrigger导致内存泄漏之前,我们已经看到过这种情况,因为主窗口可能有对加载的选项卡的引用,甚至当它被关闭/从选项卡控件中移除时,它仍然被引用。
我的建议是在KeyDown事件中使用CallMethodAction。
https://stackoverflow.com/questions/5948158
复制相似问题