Get SelectedItem of WPF Treeview - c#

I am making a WPF application with MVVM Light, and I have the following TreeView:
<TreeView x:Name="TreeView"
Grid.Column="2"
HorizontalAlignment="Left" Height="463.481" VerticalAlignment="Top" Width="263"
ItemsSource="{Binding PackageView}" Margin="0,5.657,0,0" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding Command}"
CommandParameter="SelectedItemChanged"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
When the selection is changed, I want to send the newly selected item as an argument to the command. Is there any way to do this? I was under the impression that you could do this with EventToCommand, but when I try to use them, it says they are no longer supported in version 4, and I can't find a suitable workaround.
Thanks.

When you specify CommandParameter="SelectedItemChanged" you are specifying the paramater as a string.
If you want to pass the SelectedItem as the parameter, you should do it like this: CommandParameter="{Binding ElementName=TreeView,Path=SelectedItem}".

Related

WPF ListBoxItem detect IsMouseOver inside a custom item

i have a ListBox which I fill with custom items. I want to detect a MouseOver event from a ListBoxItem inside the item in order to change visibility of a button. I have checked most of the answers on StackOverflow, the following solution was what I was looking for, but it doesn't work.
This is a code snippet from my ContactsView:
<ListBox ScrollViewer.CanContentScroll="False" VerticalContentAlignment="Top" ScrollViewer.ScrollChanged="ListBox_OnScrollChanged" BorderThickness="0,0,0,0" Margin="0,0,0,0" Padding="0" BorderBrush="{StaticResource ResourceKey=PrimaryColor}" Name="ListBox" ItemsSource="{Binding ListBoxItemsSource}" HorizontalContentAlignment="Stretch">
<i:Interaction.Triggers>
<events:RoutedEventTrigger RoutedEvent="ScrollViewer.ScrollChanged">
<i:InvokeCommandAction Command="{Binding Path=ListBoxScrollChangedCommand}" />
</events:RoutedEventTrigger>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Path=ListBoxLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderThickness" Value="0"/>
<Style.Triggers>
<Trigger Property="ListBoxItem.IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource PrimaryColor}"/>
</Trigger>
<Trigger Property="ListBoxItem.IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- Custom item -->
<items:ItemCorporateContact Value="{Binding Path=., Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Top" />
<Separator Height="1" Margin="0" Background="#ececec" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And I have been trying to detect the event this way (code from a custom item that I add to a ListBox):
<Button Name="StartCallButton" VerticalAlignment="Center" Background="Red" Margin="10" HorizontalAlignment="Left">
<Button.Content>
<Image Source="{StaticResource PhoneIconBitmap}"></Image>
</Button.Content>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Any help will be greatly appreciated.
I had been searching for the same thing. Although the answer is provided in the question, but to specify the answer more clearly, following is the code that works for me.
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="ViewTypeStackPanel" Orientation="Horizontal">
<Border BorderThickness="2,0,0,0" Visibility="{Binding Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverterInstance}}" BorderBrush="Blue"/>
<Image Height="32" Width="32">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{Binding Path=ImagePath}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsMouseOver}" Value="True">
<Setter Property="Source" Value="{Binding Path=ImagePathHover}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

Hover over a button and highlight multiple controls when condition is met (MVVM)

I have two buttons with commands bound to them.
If the user hovers over a button1 it should highlight (border color changes)
textbox1 and combobox1 only when the button1 is in disabled state.
If the user hovers over a button2 it should highlight (border color changes)
textbox2, textbo3 and combobox1 only when the button2 is in disabled state.
And finally unhighlight the controls on mouseleave.
Is this possible with pure XAML, because the style should be applied to other controls, not to the button itself who triggers the event and only when conditions are met?
And how could this be done?
I found many examples on SO, but they do not apply to this specific case.
I started to do this programmatically:
<Button Name="btnGenerateHash"
IsEnabled="{Binding VM.IsGenerateButtonEnabled}"
Command="{Binding GenerateCommand}"
Content="{Binding VM.GenerateHashButtonLabel}" Width="160"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Margin="10" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,enter'}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,leave'}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Modified version:
<StackPanel Orientation="Vertical">
<CheckBox
x:Name="EnableButton1CheckBox"
Content="Enable Button1"
Margin="4"
/>
<Grid Margin="4">
<Button Click="button1_Click"
Content="Button1"
x:Name="button1"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">
</Button>
<Border
x:Name="Button1MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
</Grid>
<Grid Margin="4">
<Button
Content="Button2"
x:Name="button2"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">
</Button>
<Border
x:Name="Button2MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
</Grid>
<TextBox
Text="tb1"
x:Name="tb1"
Margin="4"
>
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button1MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button1}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button2MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button2}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
This is partial, but illustrates how to
Expose mouseover state of a possibly disabled control to XAML style triggers on other controls, and
How those other XAML triggers can do stuff only when two different conditions are both true.
XAML:
<StackPanel Orientation="Vertical">
<CheckBox
x:Name="EnableButton1CheckBox"
Content="Enable Button1"
Margin="4"
/>
<Grid
Margin="4"
>
<Button
Content="Button1"
x:Name="button1"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}"
>
</Button>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
<Border
x:Name="Button1MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
<Button
Content="Button2"
x:Name="button2"
Margin="4"
>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button1MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button1}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>

XAML Style Won't Apply Until EventTrigger Command Completes

I have a ListBox which defines a custom ControlTemplate. The selected item has a style that changes the background and foreground, and the style basically works. However, I want to introduce a behaviour that displays a modal message box on selection changed, asking the user if they really want to select a different item. I've implemented an ICommand to do this which in the code below is shown as AreYouSureCommand.
The problem is whilst that modal message box is shown, the background style for the selected item is changed but the foreground is not. As soon as I dismiss the modal message box, the foreground colour changes. I haven't included the code for the ICommand because it's a bit convoluted but hopefully it is sufficient to say that a Window is opened with ShowDialog when it is executed.
Can anybody shed any light on why my background colour changes but not my foreground colour?
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<!-- Setter on MainBorder applies before AreYouSureCommand completes -->
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<!-- Setter on Presenter applies after AreYouSureCommand completes -->
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
I eventually came to a solution for this. Basically I use another setter for the IsSelected property to push the value of IsSelected for the ListViewItem onto the view model. Then I use a DataTrigger instead of a regular Trigger to set the selected styles, and bind the trigger to the IsSelected property on the view model rather than the property of the ListViewItem itself. I don't really know why this works - it shouldn't be any different really, but it does work.
Thanks Juan and Ben for your comments.
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>

How to bind CustomControl DataTrigger to viewmodel DataContext

Using MVVM light, I have a control template for a custom control. There are multiple instances of this control. The style is set in Generic.xaml.
<Style TargetType="{x:Type tgvw:TimeSlotRect}">
<Setter Property="DataContext" Value="{Binding TimeSlotRectViewModel, Mode=OneWay, Source={StaticResource Locator}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type tgvw:TimeSlotRect}">
<Border Background="{TemplateBinding Background}"
Name="TimeSlotBorder"
BorderBrush="#A5BFE1"
BorderThickness="0,0.5,0,0.5" Height="30">
<Rectangle Fill="Beige" Name="Rect">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding MouseDownCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding DataContext.IsSelected}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The idea is that the mouse button click fires a command in the viewmodel that sets it's IsSelected property to true and then the rectangle should pick that up and change it's fill to black - but it doesn't seem to work.
If I bind the DataTrigger to the viewmodel like this:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding TimeSlotRectViewModel.IsSelected, Source={StaticResource Locator}}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
It fires and every control changes to black. Any ideas what I'm doing wrong?

Treeview loses its focus in wpf mvvm

I have a treeview in my WPF MVVM application. Also I have a textbox that displays the value of the treenode. Right now when I expand the treeview and select a node its value is fetched and displayed on the textbox.
Now I included a checkbox that displays the numbers that appear on the textbox as words.(For eg : 11 as one one). Once I click on the checkbox the treeview loses its focus. Both the expansion of treeview and selection is lost. Only when I click the node again the value is shown in the textbox as words. How can I fix that?
<TreeView ItemsSource="{Binding Children}" Grid.Column="0" MinHeight="200" MinWidth="150" SelectedValuePath="0" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TreeViewModel}"
ItemsSource="{Binding Children}" >
<TextBlock Text="Data" />
</HierarchicalDataTemplate>
<TextBox ScrollViewer.HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
FontFamily="Courier New" FontSize="13" TextAlignment="Left"
IsReadOnly="True" Grid.Row="0" Grid.Column="1" Text="{Binding Value}" Margin="5,0" />
<CheckBox Content="View In Words" VerticalContentAlignment="Center">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding InWords}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
Thanks in advance!

Categories

Resources