Сделайте WPF TextBox или другие фокусируемые элементы управления прозрачными для определенных нажатий клавиш (т. Е. Сквозных, а не глотательных) - PullRequest
2 голосов
/ 17 октября 2010

У меня есть ListBox с пользовательским шаблоном данных, который содержит CheckBox, TextBlock и TextBox. Обычно, когда вы выбираете элемент в ListBox, базовый ListBoxItem фактически является тем, что имеет фокус, и, как таковой, он реагирует на клавиши вверх и вниз. Кроме того, если у CheckBox есть фокус, так как он сам ничего не делает с клавишами вверх и вниз, он просто счастливо игнорирует их, и они также обрабатываются базовым ListBoxItem. Пока все хорошо.

Однако TextBox имеет свои собственные правила обработки для клавиш «вверх» и «вниз», именуя перемещение курсора вверх или вниз на строку в тексте, что здесь не применимо, потому что в данном случае это одна строка (на самом деле это число) . Таким образом, если TextBox имеет фокус, клавиши «вверх» и «вниз» нарушают навигацию выбора ListBox, а также не помогают при редактировании.

Теперь, хотя я мог бы обрабатывать PreviewKeyDownEvent (что я делаю ниже, но по разным причинам) и вручную обрабатывать поведение в зависимости от нажатых клавиш, это очень специфическое решение и требует, чтобы элемент управления знал о поведении своего контейнера.

В идеальном мире (и в псевдокоде) я хотел бы просто сказать MyTextBox.KeysToIgnore (Up, Down) или что-то подобное и заставить его делать это просто ... игнорировать эти клавиши, как будто это даже не там. (Опять же, не глотайте, но игнорируйте, чтобы они проходили.)

Но до тех пор вот то, что я придумал, и это, кажется, работает, но выглядит для меня просто так "хакерски" ...

private void PreviewKeyDownHandler(object sender, KeyEventArgs e) {

    switch (e.Key){
        case Key.Up:
        case Key.Down:
        case Key.OtherKeyToIgnore
        case Key.AndAnother

            e.Handled = true;

            FrameworkElement target = VisualTreeHelper.GetParent(
                e.Source as DependencyObject) as FrameworkElement;

            target.RaiseEvent(
                new KeyEventArgs(
                    e.KeyboardDevice,
                    PresentationSource.FromVisual(target),
                    0,
                    e.Key
                ){
                    RoutedEvent=Keyboard.KeyDownEvent
                }
            );
            break;

    }

}

Это также имеет добавленный минус: не отправлять событие PreviewKeyDown цели. Теперь я мог бы обойти это и подделать, отправив сначала это событие, а затем посмотрев на e.Handled перед отправкой фактического сообщения KeyDown, что имеет смысл, но затем я ударил другую стену событиями PreviewKeyUp и KeyUp, поскольку благодаря установке Обращенный выше, я никогда не получаю настоящие события 'key up', чтобы знать, когда отправлять фальшивые. Кроме того, я почти уверен, что нарушу направление сообщений PreviewKeyxxx, поскольку они всплывают в противоположном направлении от обычных версий без предварительного просмотра. (Может быть, это решается внутренне, но я так не думаю.)

Как я уже сказал ... хаки, хаки, хаки!

Но это работает, так что есть это. И я могу реализовать это через Attached Behaviors, поэтому я даже пошел по этому пути. (В реализации присоединенного поведения это не оператор case, а скорее проверка набора ключей, который я указываю в XAML.) Мне просто не нравится идея потерять все другие поведения, которые я хочу.

Опять же, я просто хочу сказать: «Привет, TextBox ... когда вы видите нажатие клавиш« Вверх »или« Вниз », STFU ya b * stard !! ' а в противном случае сделайте его прозрачным для ключа.

Кто-нибудь думает?

Ответы [ 2 ]

1 голос
/ 30 ноября 2010

Да, обработка событий в TextBox может вызвать головную боль.Проблема в том, что невозможно определить порядок вызовов обратных вызовов, зарегистрированных через EventManager.RegisterClassEventHandler.Т.е. ваш обратный вызов вызывается при необработанном событии, затем обрабатывается событие, и вот оно ... Я нашел способ «отменить» ключевые события, создав подкласс TextBox и вызвав «AddEventHandler (KeyDownEvent, callback, true)» в конструкторе.Затем установите e.Handled = false по обстоятельствам.Похоже, что обратный вызов вызывается после обработки события TextBox.Очень нехорошо иметь экземпляр делегата для каждого экземпляра TextBox, а не иметь один экземпляр делегата для класса, но я не вижу другого способа обойти это.

1 голос
/ 10 ноября 2010

Человек ... ни одного комментария, не говоря уже о ответе почти через месяц! О, хорошо ... думаю, мое "хакерское" решение выше - способ сделать это, поэтому я отмечаю это как ответ.

...