Blend KeyTrigger срабатывает несколько раз - PullRequest
3 голосов
/ 10 мая 2011

Я использую Blend SDK KeyTrigger в проекте WPF, и у меня проблема с тем, что событие запускается несколько раз каждый раз, когда я нажимаю назначенную клавишу.

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

Этот триггер находится в коллекции триггеров 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);
  }
}

Метод OnEvent вызывается один раз за каждый раз, когда связанный элемент триггера получает событие OnLoaded. Но элементы TabControl получают событие OnLoaded каждый раз, когда вы активируете вкладку. Это означает, что вы каждый раз добавляете один и тот же обработчик событий в KeyDown / KeyUp.

Для меня это действительно выглядит большим упущением в реализации Blend SDK KeyTrigger.

У кого-нибудь есть идея предотвратить это или, возможно, как правильно реализовать KeyTrigger?

Ответы [ 2 ]

2 голосов
/ 12 января 2012

KeyTrigger регистрирует события KeyDown / Up для события Loaded.

    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 снова.

Это похоже на ошибку Microsoft, так как я бы сказал, что они должны отменить регистрацию событий при выгрузке !!!

Мы виделидо этого KeyTrigger вызывал утечки памяти, поскольку в главном окне могла быть ссылка на загруженную вкладку, и даже когда она закрывалась / удалялась из элемента управления вкладками, на нее все еще ссылаются.

Я предлагаюиспользовать CallMethodAction с событием KeyDown.

2 голосов
/ 10 мая 2011

Вы пробовали использовать FiredOn="KeyUp" вместо этого?KeyDown также может быть повторен ОС, и я полагаю, вы не хотите повторного удаления в любом случае?

...