I am using a xaml UserControl as part of a WPF application. i have created a list box which i have populated with data from a text search. this data appears on buttons which are used to select the users desired option from the search.
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Margin="2">
<ListBox ItemsSource="{Binding Path=CrewOptions}"
HorizontalContentAlignment="Stretch"
BorderThickness="0"
SelectionMode="Multiple"
Name="CrewOptionsListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="irrelevant"
Height="28px"
Background="#F4F3E9"
Margin="2,2,2,2"
Content="{Binding irrelevant1, TargetNullValue={x:Static sys:String.Empty}}"
Command="{Binding irrelevant2}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</ScrollViewer>
This works fine, however when i tab from the textbox into the list box and then use the arrow keys to select an option, the enter key press does not select the button. Instead, i have to tab once more to focus on the button before pressing enter to select it.
Is there any way to avoid having to hit the last tab key to focus on the button?
Open to both Xaml and C# solutions (preferably MVVM)
Hi you can add below code in ListBox this will resolve focus issue on ListBoxItem.
<ListView.ItemContainerStyle>
<Style TargetType="ContentControl">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
The reason for this is that you have cascading controls. When you use the arrow keys the List box is the active control, therefore all events will be fired based on the list box, not the button.
One way forward is to assign a keypressed event on the selected item, then initiate the function that will be triggered by the button.
something like:
listBox_keyPressed()
{
if(selecteditem)
{
DoSomethingFor(selectedItem);
}
}
Related
I have a ListBox defined as follows:
<ListBox
DisplayMember="Name"
ItemsSource="{Binding Persons}"
ItemTemplate="{StaticResource PeopleDataTemplate}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
Name="listOfPeople">
</ListBox>
The Person class contains two properties:
public string FirstName { get; set; }
public string LastName { get; set; }
XAML:
<UserControl.Resources>
<DataTemplate x:Key="PeopleDataTemplate">
<StackPanel>
<Image
Width="75"
Height="75"
Source="{Binding Picture}">
</Image>
<TextBlock FontSize="14" TextWrapping="Wrap" Text="{Binding Name}" />
<Button Command="{Binding DataContext.AddCommand , ElementName=main}" Content="Add" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
If I click the Image, the ListBoxItem gets selected but if I click the Button, the ListBoxItem does not get selected. Why?
The simplest solution is to add corresponding triggers to the ListBox.ItemContainerStyle to delegate focus:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Why doesn't the item get selected?
TL;DR: because the mouse down event is handled by the Button and never reaches the ListBoxItem.
What triggers the IsSelected property on each ListBoxItem is the MouseRightButtonDownEvent routed event declared on UIElement whose handling is overriden by ListBoxItem here. However, this MouseDownEvent is a bubbling routed event, meaning that it climbs up the visual tree until marked as handled by something on its way:
If you click your Image: Image receives MouseDown but does not handle it. It bubbles up to StackPanel, same story. Bubbles up to ListBoxItem. Bingo, it is handled there and calls a method on the parent ListBox that marks the item as selected.
If you click your Button however: the Button receives the MouseDown event and immediately marks it as Handled here. The event propagation is stopped and the event never bubbles up to the ListBoxItem which never gets selected.
How to get around this?
The MVVM way
In your AddCommand implementation, add some code to change the IsSelected property to true. Since you bind to IsSelected, this will select your ListBoxItem.
The XAML way
Add a Click event handler to the Button to manually mark its parent ListBoxItem as Selected.
As pointed out by #Sinatr, you could also react to focus events to select any item that gets the focus. Subscribe to the GotFocus event of each of your StackPanel for example and find the parent ListBox (on which you call UnselectAll()) and find the parent ListBoxItem (on which you call Focus()).
I have a MVVM WPF app in Visual Studio 2008 and NET Framework 3.5 SP1. Among other controls, this app have a datagrid and a button.
Button:
<Button Grid.Column="1" Command="{Binding CalculateCommand}" FocusManager.FocusedElement="{Binding ElementName=myDataGrid}" HorizontalAlignment="Right">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myDataGrid, Path=SelectedItem}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image VerticalAlignment="Center" Source="/MyWPFApp;component/Images/Calculate.png"></Image>
<TextBlock VerticalAlignment="Center">Calculate</TextBlock>
</StackPanel>
</Button>
What I am trying to do is to set focus on the current selected item in the datagrid once button is clicked so when I click on up/down arrow key in the keyboard I can move to any other item in the datagrid.
So I have tried to set FocusManager.FocusedElement property in the button but it is not working. I have also tried to set as is without specifying the Path:
FocusManager.FocusedElement="{Binding ElementName=myDataGrid}"
In the first attempt, without setting the Path in the property:
FocusManager.FocusedElement="{Binding ElementName=myDataGrid}"
when I click on down of up arrow key in the keyboard (after clicking button), it change the focus to another control in the UI that is not the current selected item in the datagrid.
In the second attempt, setting the Path in the property:
FocusManager.FocusedElement="{Binding ElementName=myDataGrid, Path=SelectedItem}"
it simply does nothing, neither no focus in the current selected item in the datagrid nor in any other control.
Also I have tried an attached behaviour as said here but it is not working:
<Button Grid.Column="1" Command="{Binding CalculateCommand}" classes:EventFocusAttachment.ElementToFocus="{Binding ElementName=myDataGrid}" HorizontalAlignment="Right">
Another attempt:
It works on second key click, first click is ignored.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
if (myDataGrid.SelectedIndex > -1)
{
var selectedRow = (Microsoft.Windows.Controls.DataGridRow)myDataGrid.ItemContainerGenerator.ContainerFromIndex(myDataGrid.SelectedIndex);
FocusManager.SetIsFocusScope(selectedRow, true);
FocusManager.SetFocusedElement(selectedRow, selectedRow);
}
}
#Evk Solution works perfectly.
The following should work:
if (myDataGrid.SelectedIndex > -1) {
var container = (DataGridRow) myDataGrid.ItemContainerGenerator.ContainerFromIndex(myDataGrid.SelectedIndex);
if (container != null) {
container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
You can put that code into Button.Click event (nothing wrong here, what we are doing is completely view-only thing) or if you don't like code-behind you can create attached property\behavior from that.
I have a list
<ListBox Name="PageNumberListItemsControl" VirtualizingPanel.IsVirtualizing="True"
ItemsPanel="{StaticResource MyPanel}"
ItemTemplate="{StaticResource CountTemplate}" Grid.Column="1">
</ListBox>
A template to make a list of buttons
<DataTemplate x:Key="CountTemplate">
<Button Content="{Binding}" Click="Button_Click">
<Button.Style>
<Style>
<Setter Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}">
</Setter>
</Style>
</Button.Style>
</Button>
</DataTemplate>
code to update the position in the list
PageNumberListItemsControl.SelectedIndex = CurrentPageNumber - 1;
I want the code selected button to be highlighted. How can I fix this?
Looks like you want that when the item is selected the button inside should be focused. So you can try adding some trigger like this:
<DataTemplate x:Key="CountTemplate">
<Button Content="{Binding}" Click="Button_Click" Name="bt">
</Button>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="true">
<Setter TargetName="bt" Property="FocusManager.FocusedElement"
Value="{Binding ElementName=bt}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Then when you click some item to select it or use code, the button inside will be automatically focused. However when you use arrow keys (up and down) to navigate through the listboxitems, the selected item still has focus. That behavior is in fact what we should keep, because only when the selected item still has focus, we can still use the up/down arrow key to continue navigating. Otherwise if the button inside has focus, we cannot continue navigating using up/down arrow keys (unless you use some codebehind handling some key event).
Hi I could not find any similar problem so I posted new question. In code below I create ListBox control with ListBoxItems that each contains radio button inside. When I click on the radio button it gets selects but parent ListBoxItem does not (ListBoxItem is not highlighted). How can I solve this issue?
<ListBox Margin="0, 5, 0, 0" ItemsSource="{Binding mySource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" SelectionMode="Single">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- Rabio template -->
<RadioButton GroupName="radiosGroup"
Margin="10, 2, 5, 2"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.SelectedSetting}"
CommandParameter="{Binding SomeId, Mode=OneWay}"
Content="{Binding FileNameWithoutExtensions, Mode=OneWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can achieve this by applying the following ItemContainerStyle to your ListBox which uses Trigger on property IsKeyboardFocusWithin to select it.
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I have a listbox that displays ListBoxItems vertically, horizontally, and has all sorts of child buttons contained within each ListBoxItem.
The problem I ran into (like others) is when you click on a child button contained in the ListBoxItem, the ListBoxItem is not selected and you can not get the ListBoxItem.SelectedIndex value (because clicking on the button does not select the ListBoxItem).
I had some problems implementing the above xaml code because clicking on my GroupBox header would cause my selected ListBoxItem to lose focus.
The best solution I found on the web for this problem was to add a couple lines of code to the button's mouse click event to determine the parent control and then set the ListBoxItem.IsSelected = true.
After this is done, the ListBoxItem.SelectedIndex will contain the correct index value for the item selected. In my code, DataContext is set on the Listbox like this: DataContext="{StaticResource VM_Programs}"
Here's the VB code behind for the button event:
Private Sub YourButton_Click(sender As Object, e As RoutedEventArgs)
Dim clicked As Object = (TryCast(e.OriginalSource, FrameworkElement)).DataContext
Dim lbitem As ListBoxItem
lbitem = YourListboxName.ItemContainerGenerator.ContainerFromItem(clicked)
lbitem.IsSelected = True
MsgBox("The listbox item (" + YourListboxName.SelectedIndex.ToString + ") is now selected")
End Sub
Everything starts pretty simple: i have a ListBoxSelector (app is for wp7). Items consists of picture and some text. Items style is stored inside of the StyleTemplate. Image can be tapped, its event is like
<Style x:Key="ProductSearchListViewerItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border BorderThickness="0,0,0,0.5">
<Grid>
<Border Grid.Column="0" Background="Transparent">
<Grid> !!! Image is here
</Grid>
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Tap">
<command:EventToCommand
Command="{Binding Source={StaticResource ViewModelLocator}, Path=NavigationViewModel.OnProductSearchListViewerTapImageCommand, Mode=OneWay}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</Border>
...and so on...
Event itself:
private void OnProductSearchListViewerTapImage(ProductItem item)
{
if (item == null) return;
MessengerInstance.Send<ProductItem, ShoppingCartViewModel>(item);
}
So far so good. When running, everything works fine... in 95% cases. I got a list of items with proper items, when i'm pressing on some item's image, event is risen, command is sent, correct item is added to the shopping cart.
BUT. In some cases (problems starts, when tapping 24-25-th item), wrong item is added to the shopping cart (list is rendered properly).
First what i did - checked command: It holds wrong item. Say, i'm tapping on 24-th item, but inside of command, "item" is first from the list.
Second what i did - i grabbed XamlSpy and checked, what is the DataContext of the item. It holds 24-th item, as it should be (because item's image and description is rendered correctly).
That means, the problem is between Event and Command. So, something is wrong inside of EventToCommand.
Anybody have same experience or any ideas?
Actually, i end up moving ControlTemplate to the separate DataTemplate, without using style.
<DataTemplate x:Key="ProductsListViewerItemTemplate">
<Grid>
****
At least, now that bug is not reproducing.