Partially Bound WPF TreeView - wpf

Edit: It now looks like this is a bug with the Telerik controls I'm using. After re-implementing my XAML using the .Net TreeView I get the results I would expect. I've reported this to Telerik and will answer this posting after hearing back from them.
I'm attempting (with limited success) to create a TreeView that contains a mixture of dynamic and static data.
You'll find my current best attempt at this below, however since I'm nesting TreeViews there is a side effect wherein it is possible to have multiple items within the parent TreeView selected. When I've tried nesting TreeViewItems the results were not positive, i.e. only the parent level TreeViewItem in the ItemTemplate is displayed and space for it's children is created but nothing is output.
I would really like to know how to acheive my goal without resorting to specifying the entire menu in collections which I bind to the TreeView.
Thank you
<telerik:RadTreeView Width="225" HorizontalAlignment="Left" Background="Transparent" FontWeight="SemiBold">
<telerik:RadTreeViewItem Header="Customer" Command="{Binding OpenCustomerCommand}"/>
<telerik:RadTreeViewItem Header="Sites" Command="{Binding OpenSiteBrowserCommand}" ItemsSource="{Binding Sites}">
<telerik:RadTreeViewItem.ItemTemplate>
<DataTemplate>
<telerik:RadTreeView>
<telerik:RadTreeViewItem Header="{Binding Key}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenSiteCommand}"
CommandParameter="{Binding Value}">
<telerik:RadTreeViewItem Header="Material Profiles"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenMaterialProfileBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="Prices"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenPriceBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="Orders"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenOrderBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="Activity">
<telerik:RadTreeViewItem Header="Collection"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenActivityCollectionsBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="Call Outs"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenActivityCallOutBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="Gate"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenActivityGateBrowserCommand}"
CommandParameter="{Binding Value}"/>
<telerik:RadTreeViewItem Header="One Off Charges"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=view:WindowViewBase},
Path=DataContext.OpenActivityOneOffChargeBrowserCommand}"
CommandParameter="{Binding Value}"/>
</telerik:RadTreeViewItem>
</telerik:RadTreeViewItem>
</telerik:RadTreeView>
</DataTemplate>
</telerik:RadTreeViewItem.ItemTemplate>
</telerik:RadTreeViewItem>

Telerik have confirmed that this isn't possible with their RadTreeView control and they've added it to their TODO list.
Thanks for the comments anyway.

Related

WPF launch Datagrid Interaction.Triggers on specific column

I have recently started using Interaction.Triggers and I was wondering if it would be possible to activate the trigger only when clicking on a certain column. In this case, I want it to launch when the user clicks on elements of the Nom Produit column (in blue).
The binding is already working great between the trigger and the datagrid. I just want to modify the launch condition of the trigger because now wherever the user clicks in the row (even Action, Impression/Édition buttons), it will launch.
Here is a minified version of my ProductsListView.xaml.
<DataGrid x:Name="myDataGrid" ItemsSource="{Binding ProductsList}" x:FieldModifier="public" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="No MAT" Binding="{Binding MATProductNumber}" IsReadOnly="True" Width="0.1*"/>
<DataGridTextColumn Header="Format" Binding="{Binding tblFormat.FormatName}" IsReadOnly="True" Width="0.1*"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ProductNavCommand}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
Thanks in advance!
You could define a CellTemplate for the "Nom Produit" column and place your Interaction.Triggers there:
<DataGridTemplateColumn Header="Nom Produit" MinWidth="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Propriete2, Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=DataContext.ProductNavCommand}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
This way it will only trigger when the click is on the "Nom Produit" column.
(As shown above, you will have to change the Binding for the Command to bind to your ViewModel).

How to bind lost focus to a command in viewmodel

<ComboBox x:Name="PrimaryCountyFIPS" Grid.Row="3" Grid.Column="3" Margin="3"
IsSynchronizedWithCurrentItem="False"
IsEditable="True"
LostFocus="{Binding LostFocusCommand }"
ItemContainerStyle ="{StaticResource ComboBoxItemStyle}"
IsEnabled="{Binding IsChecked, ElementName=IncludePrimZipCodeCheckBox}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type viewmodel:GeocoderDataCleanerViewModel}, AncestorLevel=1}, Path=DestinationColsDictionary}" DisplayMemberPath="Value" SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type viewmodel:GeocoderDataCleanerViewModel}, AncestorLevel=1}, Path=PrimaryCountyFIPSField, Mode=OneWay}">
</ComboBox>
I have a ComboBox that I am wanting to Bind it's lost focus event to a command that I have in it's ViewModel. When I try to bind it I get an error that says "LostFocus is not a method" How would I go about binding it to that Command or is that even possible?
I guess this is what you are looking for:
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding Path=DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>

Triggering WPF MVVM Button in ContextMenu on Return/Enter

I have a ContextMenu inside a TreeView, which contains a TextBox and a Button.
<TreeView ItemsSource="{Binding Folders}">
<TreeView.ContextMenu>
<ContextMenu IsOpen="{Binding Path=PlacementTarget.Tag.DataContext.ContextOpen, Mode=TwoWay, RelativeSource={RelativeSource Self}}">
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Path=PlacementTarget.Tag.DataContext.AddFolderName, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></TextBox>
<Button Content="Create here" IsDefault="True"
Command="{Binding Path=PlacementTarget.Tag.DataContext.AddFolderCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}">
</Button>
</StackPanel>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView>
In terms of mouse operation, this works as expected. Right-click brings up the menu, and the user can then fill in the text box and left-click on the button to execute the AddFolderCommand.
However, the users would also like the button to trigger on pressing Enter/Return, so they can just stick with the keyboard after entering the text.
At the moment, pressing Enter/Return causes the ContextMenu to close, and the focus to switch to the TreeView. But the underlying command does not execute.
I have tried setting IsDefault="True" on the button, but its behaviour does not change. There can only be one ContextMenu on the screen open at once. We're using MVVM, so I'd prefer to avoid a code-behind solution if possible. How can I trigger the command on the keypress?
<TreeView ItemsSource="{Binding Folders}">
<TreeView.ContextMenu>
<ContextMenu IsOpen="{Binding Path=PlacementTarget.Tag.DataContext.ContextOpen, Mode=TwoWay, RelativeSource={RelativeSource Self}}">
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Path=PlacementTarget.Tag.DataContext.AddFolderName, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></TextBox>
<Button Content="Create here" IsDefault="True"
Command="{Binding Path=PlacementTarget.Tag.DataContext.AddFolderCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}">
</Button>
<StackPanel.InputBindings>
<KeyBinding Gesture="Enter" Command ="{Binding Path=PlacementTarget.Tag.DataContext.AddFolderCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</StackPanel.InputBindings>
</StackPanel>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView>
This should do the trick without having to even leave the xaml. Note the <StackPanel.InputBindings> part

WPF contextmenu binding

Cant seem to bind a menuitem in my contextmenu to a command in my viewmodel. I know the contextmenu does not live in the visual tree. For testing purposes i have bound to the same command twice in a button. The first binding works but i can get the second binding in the contextmenu to bind. I can see the binding error in output. Someone has any idea?
<HierarchicalDataTemplate DataType="{x:Type inf:OSiteEquipment}" ItemsSource="{Binding Path=SubSystems, Converter={StaticResource subsystemConverter}}" >
<Button HorizontalContentAlignment="Left"
Command="{Binding DataContext.CommandOpenSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding Path=PartData.Name}" TextTrimming="CharacterEllipsis" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Category" Command="{Binding Path=Parent.PlacementTarget.Tag.CommandOpenSelected, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</HierarchicalDataTemplate>
Just remove the "Parent" in the Databinding path :
<HierarchicalDataTemplate DataType="{x:Type inf:OSiteEquipment}" ItemsSource="{Binding Path=SubSystems, Converter={StaticResource subsystemConverter}}" >
<Button HorizontalContentAlignment="Left"
Command="{Binding DataContext.CommandOpenSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding Path=PartData.Name}" TextTrimming="CharacterEllipsis" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Category" Command="{Binding Path=PlacementTarget.Command, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</HierarchicalDataTemplate>
You already find the Parent via the relative source -> Omit the Parent in the path
You did not even set the Tag on the PlacementTarget (Button) -> Set it respectively

Silverlight: Adding ChangePropertyAction to DataTemplate

I was playing around, experimenting and googling this for the last hour and cannot figure out the problem. Basically, I have a listbox, each item has textblock and checkbox. I need to change text of textblock to green next to the checked checkbox. The code below is the best that I could come up with, but it doesn't work. Any help is appreciated.
<DataTemplate x:Key="ListBoxTemplate">
<Grid
Width="80">
<TextBlock
x:Name="textBlock"
HorizontalAlignment="Left"
Text="{Binding Data, Converter={StaticResource DataConverter}}"
Foreground="#FF859FAF"
FontSize="12"
TextWrapping="Wrap"
Width="50"
Margin="0"
Grid.Row="10" />
<CheckBox
x:Name="Btn6"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
Cursor="Hand"
Padding="0"
HorizontalAlignment="Right"
Height="32"
VerticalAlignment="Bottom"
RenderTransformOrigin="0.5,0.5"
Width="26"
d:LayoutOverrides="GridBox">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="textBlock">
<ei:ChangePropertyAction
TargetName="textBlock"
TargetObject="{Binding ElementName=textBlock}"
PropertyName="Width"
Value="100">
</ei:ChangePropertyAction>
<i:InvokeCommandAction
Command="{Binding SelectDataCommand, Source={StaticResource ControlViewModel}}"
CommandName="SelectDateRangeCommand"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
(sorry for my bad english)
This do not exactly answer your question but an easy alternative would be to bind IsSelected with the TextBox.Foreground and create a converter that return a SolidColorBrush green when the value is true

Resources