AFAIK bubbling means that if an event is triggered on the children, the same event will be triggered on the parents.
I have this piece of code:
<ListView Name="lvFiles" SelectedItem="{Binding SelectedTabFilePath, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SelectedItem.Files,ElementName=trvFiles, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Path=Name}"
Command="{Binding DataContext.OpenFileFromTreeView,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
CommandParameter="{Binding DataContext,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
Background="Transparent"
BorderBrush="Transparent"
>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The problem is that if I click on the button, the property SelectedTabFilePath from SelectedItem will not be filled with data (because I didn't click on the item of the ListView).
So, my question is:
Is there any way to trigger the Click event of the button and that of the ListView too?
Or if not, what would be the way to set the SelectedTabFilePath from the SelectedItem attribute when I click on that button, too?
The Click event is indeed bubbling, which means it will be routed up the elements of visual tree until the root element is reached. However, the Button itself handles the event, because its Command property was bound. That means its parent elements, including ListViewItem (the item container) will ignore the event, therfore not select the item.
There is a workaround using the fact that the Button receives keyboard focus upon clicking. You add an item container style with a trigger that sets the IsSelected property of the ListViewItem once the keyboard focus is within, denoted by the IsKeyboardFocusWithin property.
<ListView ...>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<!-- ...your other markup. -->
</ListView>
how to handle WPF listbox selectionchanged event using MVVM
I think you could use interaction to trigger the same command(OpenFileFromTreeView) when the listbox selection changes.
Instead of:
AncestorType={x:Type ListView}
What if you try:
AncestorType={x:Type ListViewItem}
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 nested context menu like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Thing Count" ItemsSource="{Binding ThingsProvider}">
NB the Thing Count parent MenuItem with children from ThingsProvider.
ThingsProvider provides a List of ThingViewModel which contains properties Thing and IsChecked. I want to be able to control IsChecked from my main view model and from the user via the MenuItem but I have hit a problem; If I use an ItemContainerStyle like this.
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Thing, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</MenuItem.ItemContainerStyle>
then (predictably I suppose as it's a style) it will observe the ViewModel's IsChecked but will not set it.
If I use an ItemTemplate like so
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, Mode=OneWay}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
I can't get to the MenuItem's IsChecked property as I've only got the TextBlock. If I put a MenuItem in the ItemTemplate then I end up with nested MenuItems in my UI and it looks bad.
There must be a way to do this but I'm stumped at the moment, can anyone help?
Cheers
Rich
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
I have a control that has a DataContext being loaded and based on it I create a bunch of Buttons. I would like to have the Button react on a change to the bound objects change
<Button ToolTip="{Binding Tip}"
ib:ButtonProperties.Image="{Binding EnabledSource}"
ib:ButtonDProperties.Image="{Binding DisabledSource}"
Content="{Binding Text}"
IsEnabled="{Binding DefaultEnabled}"
Tag="{Binding .}"
Click="ToolBarButtonButton_Click"
Style="{StaticResource ImageButton}">
</Button>
For simplification purposes, Lets say, I databind a collections of Custom objects which Have a Property called "IsPerfect" of type bool. The Object assigned to the button is set as the tag as well.
I would like to have the button reacting on the property "IsPerfect" that belongs to the databound object and then execute a eventhandler or enable/disable the button.
<Button IsEnabled="{Binding SomeBoolValue}"/>
You can use a style to fulfill your goal.
<Button>
<Button.Style>
<Style>
<DataTrigger Binding="{Binding IsPerfect}" Value="True">
<Setter Property="Button.IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style>
<Button.Style>
</Button>