2011-05-10 8 views
3

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

<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

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

+0

KeyUp не имеет значения. – Gunter

+2

Реальная проблема, похоже, связана с OnLoaded и что элементы элементов управления вкладками получают несколько событий OnLoaded. – Gunter

+1

Ок, я вижу. Итак, 'KeyTrigger' является общедоступным классом, поэтому вы можете его расширять и переопределять' OnEvent', чтобы предотвратить добавление обработчика нескольких событий? – dain

2

KeyTrigger регистрирует события 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 событие снова. Это вызывает повторную регистрацию событий включения/выключения.

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

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

Мое предложение - использовать CallMethodAction с событием KeyDown.

Смежные вопросы