Blend KeyTrigger触发多次 [英] Blend KeyTrigger fires multiple times

查看:66
本文介绍了Blend KeyTrigger触发多次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在WPF项目中使用Blend SDK KeyTrigger,但遇到的问题是,每当我按下分配的键(此处为DeleteCommand)时,都会多次触发该事件。

I am using the Blend SDK KeyTrigger in a WPF project and have the problem that the event is fired multiple times each time I press the assigned key, here the DeleteCommand.

<ei:KeyTrigger FiredOn="KeyDown" ActiveOnFocus="True" SourceName="repositoryPackages" Key="Delete">
  <i:InvokeCommandAction Command="{Binding SelectedItem.DeleteCommand, repositoryPackages}" />
</ei:KeyTrigger>

此触发器位于ListView的触发器集合中,该ListView本身位于用户控件内的网格​​中。

This trigger is in the trigger collection of a ListView which itself is on a grid inside a user control.

然后将用户控件嵌入到应用程序主窗口中的WPF TabControl的选项卡上。

The user control is then embedded on a tab of a WPF TabControl on the application main window.

每个当我离开并返回到带有ListView的选项卡时,触发器又一次无限次调用该命令。

Each time I switch away and back to the tab with my ListView the trigger invokes the command one more time ad infinitum.

我查看了KeyTrigger的来源(在Microsoft中。Expression .Interactions),并注意以下几行:

I looked at the source of KeyTrigger (in Microsoft.Expressions.Interactions) and noticed the following lines:

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

关联的每次调用一次OnEvent方法触发器的元素获取一个OnLoaded事件。但是,每次激活选项卡时,TabControl上的元素都会获取一个OnLoaded事件。这意味着您每次都将相同的事件处理程序添加到KeyDown / KeyUp。

The OnEvent method gets called once for each time the associated element of the trigger gets an OnLoaded event. But elements on a TabControl get an OnLoaded event each time you activate a tab. Which means you add the same event handler to KeyDown/KeyUp each time.

对我来说,这确实像Blend SDK KeyTrigger实施中的一个大疏忽。

For me this really looks like a big oversight in the implementation of Blend SDK KeyTrigger.

是否有人有想法防止这种情况或可能如何实施正确的KeyTrigger?

Does anyone have an idea to prevent this or probably how to implement a correct KeyTrigger?

推荐答案

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

KeyTrigger registers te KeyDown/Up events on the Loaded event.

    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事件。

In tab controls for example, when you switch to another tab, you get unloaded event, and when you get back to your tab you get a Loaded event again. This causes the registeration of the keydown/up events again.

这似乎是Microsoft的错误,因为我认为他们应该在卸载时注销事件!!!

This seem to be a bug of microsoft since I would thing that they should unregister the events on unload!!!

我们在KeyTrigger导致内存泄漏之前就已经看到了它,因为主窗口可能引用了已加载的Tab,即使它已从窗口中关闭/移除了。

We have seen it before that KeyTrigger caused Memory Leaks since the main window might have a reference to the Tab that was loaded, and even when it was closed/removed from the tab control then it is still being referenced.

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

My suggest is to use CallMethodAction with KeyDown event.

这篇关于Blend KeyTrigger触发多次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆