Blend KeyTrigger会多次触发

时间:2011-05-10 09:44:09

标签: wpf triggers expression-blend

我在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的触发器集合中,ListView本身位于用户控件内的网格​​上。

然后将用户控件嵌入到应用程序主窗口上的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?

2 个答案:

答案 0 :(得分:2)

您是否尝试过使用FiredOn="KeyUp"? KeyDown也可以由操作系统重复,我认为你不想反复删除?

答案 1 :(得分:2)

KeyTrigger在Loaded事件上注册了KeyDown / Up事件。

    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);
        }
    }
}

例如,在选项卡控件中,当您切换到另一个选项卡时,您将获得卸载事件,当您返回选项卡时,您将再次获得一个Loaded事件。这会导致再次注册keydown / up事件。

这似乎是微软的一个错误,因为我认为他们应该在卸载时取消注册事件!!!

我们之前已经看到KeyTrigger导致内存泄漏,因为主窗口可能引用了已加载的Tab,即使它已从Tab控件关闭/删除,它仍然被引用。

我的建议是将CallMethodAction与KeyDown事件一起使用。