WPF: How to programmatically give visual feedback that the keyboard focus is on the list?

I am writing an application with a list that allows you to use multiple selection (SelectionMode = Multiple); items in lisbox are ingredients for the recipe.

Unfortunately, clicking on a list item allows you to select this item, which may be undesirable. I need the following script:

  • the user clicks on the list to select the list (the list itself, not the item)
  • user scrolls the desired item and selects it

What I did was ListBoxItem style including checkbox and ContentPresenter (like this blog post ). However, clicking on an ingredient name selects it. Thus, I delay the MouseDown event in the text block containing the ingredient name, find the base ListBoxItem, call Focus () on it and set the Handled property of the event to true.

The Listbox item now has focus but is not selected. Using the up and down keys indicates that the focus was on the desired item. My problem is that the user does not see that he clicked on the correct item. A dashed rectangle is not displayed on this element. Here is the result:

alt text

And here is what I would like:

alt text

I tried calling private WPF methods, such as KeyboardNavigation.ShowFocusVisual, I tried to send keystrokes to the list (when a person did it by pressing the right cursor key or Alt key, a dotted box will appear).

Any idea?

+3
source share
2 answers

SendInput is the only way I found that passes this one. From this link.

PInvoke to SendInput is the official way to simulate input. This pushes input through all the expected code paths and is indistinguishable from real input.

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

By adding a link 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 may not be ideal for many reasons (e.g. Shift + Tab for ListBoxItem)

The best idea is probably to remove the FocusVisualStyle for the ListBoxItem and add your own to the ControlTemplate like this. (Copied from Blend and added "FocusVisualStyle" from the 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> 
+4
source

I found Meleak's answer very useful, but using GotFocus does not work for me. Instead, I bind even a handler to PreviewMouseLeftButtonDown. Now you do not need a boolean property to hold state, and the code is very simple:

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

This makes the job very good for me.

PS I use this style - it has a wonderful animation of a moving dotted rectangle

0
source

Source: https://habr.com/ru/post/1344867/


All Articles