I'm implementing something like an autosuggestion control: I have a user control that contains a TextBox
and a ListBox
. When the user enters text I'm handing it with System.Windows.Interactivity
behaviors and filling the ListBox
with some values...
Everything works fine... but I want to enable the user to select items in the ListBox
(i.e. to set Focus
on the ListBox
) when the down arrow key is pressed.
I know that it is possible to handle the KeyPressDown
event of the 开发者_Python百科TextBox
in the code-behind .cs file but how can I avoid this?
If you already use Interactivity that should not be much of an issue, just implement your own TriggerAction
that has the properties Key
& TargetName
to indentify when and what to focus. Set it in an EventTrigger
for PreviewKeyDown
.
Sample implementation & use:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<t:KeyDownFocusAction Key="Down"
Target="{Binding ElementName=lbx}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox Name="lbx" ItemsSource="{Binding Data}" />
class KeyDownFocusAction : TriggerAction<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyDownFocusAction));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(UIElement), typeof(KeyDownFocusAction), new UIPropertyMetadata(null));
public UIElement Target
{
get { return (UIElement)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
protected override void Invoke(object parameter)
{
if (Keyboard.IsKeyDown(Key))
{
Target.Focus();
}
}
}
Tested it and it works, note that KeyDown
does not because the arrow keys are intercepted and marked as handled by the TextBox.
I don't think you can avoid it
What's wrong with capturing the KeyDown
event of the TextBox and if it's an Up or Down arrow key, just trigger the ListBox.KeyDown
event in the code behind?
I see no reason not to use code-behind in MVVM if it is to provide view-specific functionality such as focus
This answer is based on the one from H.B.
, and adds support for checking to see if the Ctrl key is pressed. This means it can handle keycombinations such as Ctrl-F for find.
XAML
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<t:KeyDownFocusAction Key="Down" Ctrl="True"
Target="{Binding ElementName=lbx}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox Name="lbx" ItemsSource="{Binding Data}" />
Namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
See help on adding System.Windows.Interactivity
.
DependencyProperty
public class KeyDownFocusAction : TriggerAction<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyDownFocusAction));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
public static readonly DependencyProperty CtrlProperty =
DependencyProperty.Register("Ctrl", typeof(bool), typeof(KeyDownFocusAction));
public bool Ctrl
{
get { return (bool)GetValue(CtrlProperty); }
set { SetValue(CtrlProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(UIElement), typeof(KeyDownFocusAction), new UIPropertyMetadata(null));
public UIElement Target
{
get { return (UIElement)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
protected override void Invoke(object parameter)
{
if (Keyboard.IsKeyDown(Key))
{
if (Ctrl == true)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
Target.Focus();
}
}
}
}
}
精彩评论