Как правильно реализовать пользовательский элемент управления, который обрабатывает мнемонику? - PullRequest
2 голосов
/ 13 октября 2010

Я реализую пользовательский элемент управления в WinForms. Этот элемент управления должен реагировать на мнемонический символ, а мнемоника должна фокусировать следующий элемент управления в порядке табуляции, как это делает элемент управления Label.

Метод ProcessMnemonic явно тот, который нужно переопределить. Однако, глядя на то, как это делает элемент управления Label, для справки видно, что он сильно зависит от внутренних компонентов WinForms:

  • Чтобы проверить, нормально ли реагировать на мнемонику, Label использует внутренний метод CanProcessMnemonic.
  • Метод запрашивает UIPermission, а затем использует группу внутренних классов и вызовов для проверки того, что фокус может быть изменен.
  • Фокус изменяется с использованием внутреннего поля «ParentInternal».

Я не ожидал, что реакция на мнемонику может быть настолько сложной. Кажется, это работает, если я опускаю все эти внутренние вызовы и просто использую не внутренний Parent, но это заставляет меня задуматься: конечно, этот код не существует даром, поэтому моей наивной реализации должно быть что-то не хватает?

Вот реальный код Лейбла, слегка подправленный мной для удобства чтения:

[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected internal override bool ProcessMnemonic(char charCode)
{
    if ((!UseMnemonic || !IsMnemonic(charCode, Text)) || !CanProcessMnemonic() /*internal*/)
        return false;

    if (ParentInternal != null) /* internal */
    {
        IntSecurity.ModifyFocus.Assert(); /* internal */
        try
        {
            if (ParentInternal.SelectNextControl(this, true, false, true, false) && !ParentInternal.ContainsFocus)
                ParentInternal.Focus();
        }
        finally
        {
            CodeAccessPermission.RevertAssert();
        }
    }
    return true;
}

Вот мое "наивное" переписывание этого:

protected override bool ProcessMnemonic(char charCode)
{
    if ((!UseMnemonic || !IsMnemonic(charCode, Text)) || !Enabled || !Visible)
        return false;

    if (Parent != null)
        if (Parent.SelectNextControl(this, true, false, true, false) && !Parent.ContainsFocus)
            Parent.Focus();

    return true;
}

Одно очевидное отличие состоит в том, что CanProcessMnemonic рекурсивно проверяет родительский элемент, а мой код этого не делает, потому что этот метод, необъяснимо, является внутренним ...

...