WPF: Как программно дать визуальный отклик, что фокус клавиатуры находится в списке? - PullRequest
3 голосов
/ 21 ноября 2010

Я пишу приложение со списком, позволяющим множественный выбор (SelectionMode = Multiple); элементы в списке являются ингредиентами для рецепта.

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

  • пользователь нажимает на список, чтобы выбрать список (сам список, а не элемент)
  • пользователь прокручивает до нужного элемента и выбирает его

Я сделал стиль для ListBoxItem, включив в него флажок и ContentPresenter (например, в этом блоге ). Тем не менее, нажатие на название ингредиента выбирает его. Поэтому я перехватываю событие MouseDown в текстовом блоке, содержащем имя ингредиента, нахожу базовый ListBoxItem, вызываю для него Focus () и устанавливаю для свойства Handled события значение true.

Теперь элемент списка имеет фокус, но не выбран. Использование клавиш вверх и вниз показывает, что фокус был на правильном элементе. Моя проблема в том, что пользователь не может видеть, что он нажал на нужный элемент. Пунктирный прямоугольник не отображается на этом элементе. Вот результат:

alt text

А вот что я хотел бы:

alt text

Я пытался вызывать частные методы WPF, такие как KeyboardNavigation.ShowFocusVisual, я пытался отправлять нажатия клавиш в список (когда это делается человеком, нажатие правой клавиши курсора или клавиши Alt приводит к появлению пунктирного прямоугольника).

Есть идеи?

Ответы [ 2 ]

4 голосов
/ 22 ноября 2010

SendInput - единственный способ, который я нашел, который проходит через это.От этой ссылки.

PInvoke до SendInput - это официальный способ имитации ввода.Он проталкивает ввод через все ожидаемые пути кода и неотличим от реального ввода.

Простой способ использовать это с InputSimulator из CodePlex.

Добавив ссылку на InputSimulator.dll, мы можем сделать что-то вроде этого

private bool m_waitingForFocusVisualStyle = false;
private void ListBoxItem_GotFocus(object sender, RoutedEventArgs e)
{
    if (m_waitingForFocusVisualStyle == false)
    {
        m_waitingForFocusVisualStyle = true;
        InputSimulator.SimulateKeyDown(VirtualKeyCode.TAB);
        InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.SHIFT, VirtualKeyCode.TAB);
    }
    else
    {
        m_waitingForFocusVisualStyle = false;
    }
}

Но это может быть не идеальным по многим причинам (например, Shift + Tab для ListBoxItem)

Лучшей идеей, вероятно, является удаление FocusVisualStyle для ListBoxItem и добавление своего собственного в ControlTemplate, как это.(Скопировано из Blend и добавлено «FocusVisualStyle» из стандартного FocusVisualStyle)

<ListBox ...>
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template" Value="{StaticResource ListBoxItemTemplate}" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

<ControlTemplate x:Key="ListBoxItemTemplate" TargetType="{x:Type ListBoxItem}">
    <Grid>
        <Rectangle Grid.ZIndex="1"
                    Name="focusVisualStyle"
                    StrokeThickness="1"
                    Stroke="Black"
                    StrokeDashArray="1 2"
                    SnapsToDevicePixels="true"
                    Visibility="Hidden"/>
        <Border x:Name="Bd"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Background="{TemplateBinding Background}"
                Padding="{TemplateBinding Padding}"
                SnapsToDevicePixels="true">
            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Border>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsFocused" Value="True">
            <Setter TargetName="focusVisualStyle" Property="Visibility" Value="Visible"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="true">
            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsSelected" Value="true"/>
                <Condition Property="Selector.IsSelectionActive" Value="false"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        </MultiTrigger>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
0 голосов
/ 13 апреля 2012

Я нашел ответ Мелеака очень полезным, но использование GotFocus не работает для меня. Вместо этого я привязал чётный обработчик к PreviewMouseLeftButtonDown even. Теперь вам не нужно логическое свойство для хранения состояния, а код очень прост:

    void SlideCanvasPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
            InputSimulator.SimulateKeyDown(VirtualKeyCode.TAB);
            InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.SHIFT, VirtualKeyCode.TAB);  
    }

Это делает работу для меня очень хорошей.

P.S. Я использую этот стиль - в нем есть прекрасная анимация перемещения пунктирной прямоугольника

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...