WPF:如何以编程方式给出键盘焦点在列表框中的视觉反馈? [英] WPF: How to programmatically give visual feedback that the keyboard focus is in a listbox?

查看:90
本文介绍了WPF:如何以编程方式给出键盘焦点在列表框中的视觉反馈?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个带有允许多选的列表框的应用程序(SelectionMode = Multiple);盒中的物品是食谱的原料.

不幸的是,单击列表框项目会选择此项目,这可能是不希望的.我想要以下情形:

  • 用户单击列表框以选择列表框(列表框本身,而不是项目)
  • 用户滚动到右边的项目并选择它

我所做的就是设置ListBoxItem的样式,使其包括一个复选框和一个ContentPresenter(例如

这就是我想要的:

我曾尝试调用私有的WPF方法,例如KeyboardNavigation.ShowFocusVisual,我曾尝试将击键发送到列表框(当由人操作时,按右光标键或Alt键将显示虚线矩形). /p>

有什么主意吗?

解决方案

SendInput是我发现的唯一超越此方法的方法.通过链接.

PInvoke到SendInput –这是 官方模拟输入的方式.它 通过所有的输入 预期的代码路径,并且是 与实际输入无法区分.

一种简单的使用方法是使用CodePlex中的 InputSimulator .

添加对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)

一个更好的主意可能是删除ListBoxItem的FocusVisualStyle并像这样在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>

I am writing an application with a listbox allowing multi-selection (SelectionMode=Multiple); the items in the lisbox are ingredients for a recipe.

Unfortunately, clicking on a listbox item selects this item, which may not be desired. I would like the following scenario:

  • the user clicks on the listbox in order to select the listbox (the listbox itself, not an item)
  • the user scrolls to the right item and selects it

What I did is style the ListBoxItem to include a checkbox and a ContentPresenter (like in this blog). Still, clicking on the ingredient name selects it. So, I trap the MouseDown event on the textblock containing the ingredient name, find the underlying ListBoxItem, call Focus() on it and set the Handled property of the event to true.

Now, the Listbox item has the focus but is not selected. Using the up and down keys shows that the focus was on the right item. My problem is that the user cannot see that he has clicked on the right item. The dotted rectangle is not shown on this item. Here is the result:

And here is what I'd like:

I've tried calling private WPF methods, like KeyboardNavigation.ShowFocusVisual, I've tried sending keystrokes to the listbox (when done by a human, pressing the right cursor key or the Alt key makes the dotted rectangle appear).

Any idea ?

解决方案

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>

这篇关于WPF:如何以编程方式给出键盘焦点在列表框中的视觉反馈?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆