I am trying to select the first item in the ListView
when I tab into it.
If I type text in the TextBox
and then I tab to the ListView
it selects the first item with a dotted border only and the select item is null
and selected index is 0. If I press down on the keyboard I start getting selected item. How can I get it to work on item 0 directly from tabbing from the TextBox
?
XAML:
<Grid>
<StackPanel>
<TextBox> </TextBox>
<ListView Margin="10" Name="lvDataBinding">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline" Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<User> items = new List<User>();
items.Add(new User() { Name = "John Doe", Age = 42 });
items.Add(new User() { Name = "Jane Doe", Age = 39 });
items.Add(new User() { Name = "Sammy Doe", Age = 13 });
lvDataBinding.ItemsSource = items;
}
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return this.Name ", " this.Age " years old";
}
}
CodePudding user response:
I am trying to select the first item in the listview when I tab on to it.
From what it seems there is not a method on ListView
that selects a particular item (through index for example, although there's a SelectAll
method). What you have though is the list of the users where there indices maps to the elements of the ListView
(User
in items
at position 0 is rendered by the element of the lvDataBinding
at the same position). You can use an indirect way, through data-binding, to select an item. In the example that I'll give the first item will be selected.
Create User.IsSelected
That will be the data-binding source.
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsSelected { get; set; }
public override string ToString()
{
return this.Name ", " this.Age " years old";
}
}
Bind to element's IsSelected
The target is the ListView
element's IsSelected
property.
ListView Margin="10" Name="lvDataBinding">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline" Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Trigger the selection
Now where the data-binding is applied you need to trigger a user selection. Assumingly that you want this when the TextBox
loses focus then you could subscribe to LostFocus
event.
<TextBox
LostFocus="TextBox_LostFocus"/>
and select your user (first in this example)
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
items[0].IsSelected = true;
}
That will result in the selection of the first element.
CodePudding user response:
In your specific case, you want the behavior only to apply if the TextBox
was focused first. You can achieve it by naming the TextBox
and add a handler in code-behind for the GotKeyboardFocus
event.
<Grid>
<StackPanel>
<TextBox x:Name="MyTextBox"/>
<ListView Margin="10" Name="lvDataBinding" GotKeyboardFocus="lvDataBinding_OnGotKeyboardFocus">
<!-- ...your other markup. -->
</ListView>
</StackPanel>
</Grid>
In there you check if the TextBox
was focused before using e.OldFocus
and set SelectedIndex
to zero.
private void lvDataBinding_OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!MyTextBox.Equals(e.OldFocus))
return;
((ListView)sender).SelectedIndex = 0;
}
A more elegant approach is using a custom behavior. For this you need to install the Microsoft.Xaml.Behaviors.Wpf NuGet package and derive a class from Behavior<T>
, where T
is the control it will be attached to, here the ListView
. We do the same as in code-behind, but this time we expose a dependency property to bind the previously focused element. If nothing is bound, the behavior will work regardless of the element that was focused before.
public class SelectFirstItemOnKeyboardFocusBehavior : Behavior<ListView>
{
public UIElement PreviouslyFocusedElement
{
get => (UIElement)GetValue(PreviouslyFocusedElementProperty);
set => SetValue(PreviouslyFocusedElementProperty, value);
}
public static readonly DependencyProperty PreviouslyFocusedElementProperty = DependencyProperty.Register(
nameof(PreviouslyFocusedElement), typeof(UIElement), typeof(SelectFirstItemOnKeyboardFocusBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotKeyboardFocus = OnGotKeyboardFocus;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotKeyboardFocus -= OnGotKeyboardFocus;
}
private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (PreviouslyFocusedElement is null || PreviouslyFocusedElement.Equals(e.OldFocus))
((ListView)sender).SelectedIndex = 0;
}
}
Attach the behavior to the ListView
and bind the TextBox
as previously focused element.
<Grid>
<StackPanel>
<TextBox Name="MyTextBox"/>
<ListView Margin="10" Name="lvDataBinding">
<b:Interaction.Behaviors>
<local:SelectFirstItemOnKeyboardFocusBehavior PreviouslyFocusedElement="{Binding ElementName=MyTextBox}"/>
</b:Interaction.Behaviors>
<!-- ...your other markup. -->
</ListView>
</StackPanel>
</Grid>
You need to import the following XML namespace for behaviors.
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"