WPF: How to programmatically give visual feedback that the keyboard focus is in a listbox?

Deadly 提交于 2019-12-05 18:22:40

SendInput is the only way I've found which gets past this. From this link.

PInvoke to SendInput – this is the official way to simulate input. It pushes the input through all of the expected code paths, and is indistinguishable from real input.

An easy way to use this is with InputSimulator from CodePlex.

Adding a reference to InputSimulator.dll we can do something like this

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;
    }
}

But this might not be ideal for a lot of reasons (Shift+Tab to the ListBoxItem for example)

A better idea is probably to remove the FocusVisualStyle for ListBoxItem and add your own in the ControlTemplate like this. (Copied from Blend and added a "FocusVisualStyle" from standard 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>

I have found Meleak's answer very helpful, but using GotFocus does not work for me. Instead I have bind the even handler to PreviewMouseLeftButtonDown even. Now you don't need boolean property to store the state and the code is very simple:

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

This does the job for me very good.

P.S. I use this style - it has some fine animation of moving dashed rectanle

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!