Сохранение фокусировки клавиатуры на одном элементе управления, в то же время возможность использования ListBox - PullRequest
3 голосов
/ 03 марта 2010

Работая над приложением TouchScreen, к которому также подключена клавиатура, у меня возникает следующая проблема:

Окно WPF имеет TextBox, который должен получать ВСЕ ввод с клавиатуры. Есть также кнопки и ListBox, которые используются только сенсорным экраном (= мышь).

Очень простой пример выглядит так:

<Window x:Class="KeyboardFocusTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">
    <StackPanel>
        <TextBox Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                 PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus"/>
        <Button Click="Button_Click">Add</Button>
        <ListBox ItemsSource="{Binding Strings}" />
    </StackPanel>
</Window>

Чтобы держать TextBox всегда сфокусированным, я просто делаю:

private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
    e.Handled = true;
}

Пока все хорошо - проблема в том, что я больше не могу выбирать элементы из списка. Кажется, это работает, только если ListBox имеет фокус клавиатуры. Но если я потеряю фокус клавиатуры на TextBox, я больше не смогу вводить текст, не щелкнув его сначала.

Любые идеи, комментарии, предложения приветствуются!

Ответы [ 2 ]

3 голосов
/ 04 марта 2010

Основываясь на предложении Николаса (спасибо!), Вот расширение разметки, которое используется как:

<TextBox Helpers:KeyboardFocusAttractor.IsAttracted="true" />

Кажется, это работает, и ANTS не показал никаких утечек памяти. Но когда дело доходит до WPF и особенно событий и привязок, вы никогда не знаете, так что используйте с осторожностью!

public static class KeyboardFocusAttractor
{
    public static readonly DependencyProperty IsAttracted = DependencyProperty.RegisterAttached("IsAttracted",
        typeof (bool), typeof (KeyboardFocusAttractor), new PropertyMetadata(false, OnIsAttracted));

    private static void OnIsAttracted(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var isAttracted = (bool) e.NewValue;
        var controlWithInputFocus = d as Control;

        if (controlWithInputFocus != null)
        {
            if (isAttracted)
            {
                new KeyboardInputFocusEventManager(controlWithInputFocus);
            }
        }
    }

    public static void SetIsAttracted(DependencyObject dp, bool value)
    {
        dp.SetValue(IsAttracted, value);
    }

    public static bool GetIsAttracted(DependencyObject dp)
    {
        return (bool) dp.GetValue(IsAttracted);
    }

    private class KeyboardInputFocusEventManager
    {
        private readonly Control _control;
        private Window _window;

        public KeyboardInputFocusEventManager(Control control)
        {
            _control = control;
            _control.Loaded += ControlLoaded;
            _control.IsVisibleChanged += ControlIsVisibleChanged;
            _control.Unloaded += ControlUnloaded;
        }

        private void ControlLoaded(object sender, RoutedEventArgs e)
        {
            _window = Window.GetWindow(_control);
            if (_window != null)
            {
                _control.Unloaded += ControlUnloaded;
                _control.IsVisibleChanged += ControlIsVisibleChanged;
                if (_control.IsVisible)
                {
                    _window.PreviewKeyDown += ParentWindowPreviewKeyDown;
                }
            }
        }

        private void ControlUnloaded(object sender, RoutedEventArgs e)
        {
            _control.Unloaded -= ControlUnloaded;
            _control.IsVisibleChanged -= ControlIsVisibleChanged;
        }

        private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (_window != null)
            {
                _window.PreviewKeyDown -= ParentWindowPreviewKeyDown;
            }

            if (_control.IsVisible)
            {
                _window = Window.GetWindow(_control);
                if (_window != null)
                {
                    _window.PreviewKeyDown += ParentWindowPreviewKeyDown;
                }
            }
        }

        private void ParentWindowPreviewKeyDown(object sender, KeyEventArgs e)
        {
            Keyboard.Focus(_control);
        }
    }
}
2 голосов
/ 03 марта 2010

Для этого может быть более элегантное решение, но вы всегда можете обработать событие PreviewKeyDown на уровне окна и передать фокус TextBox, если у него его еще нет, вместо того, чтобы не потерять фокус в первое место. Таким образом, ListBox может использовать фокус как обычно, но как только клавиша нажата, фокус переходит прямо к TextBox. Кроме того, вы можете отфильтровать клавиши, которые вы не хотите переключать, - вам на ум приходят клавиши со стрелками, которые затем можно использовать для перемещения вверх и вниз в ListBox.

Добавление обработчика событий, как показано ниже, должно помочь:

private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (!textBox.IsFocused)
    {
        textBox.Focus();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...