Is there a way to pass treeViewItem name as command parameter? - c#

I am creating a simple treeview with binding to a collection. What I want is to display full info about the collection element in a listview when treeview element is selected. Similar to windows file explorer. The problem is I cannot bind selected treeviewitem to a Command.
Tried binding command to SelectedItemChanged event with a parameter of treeviewitem name - didn`t work.
Tried it with a simple event - gets cumbersome and breaks MVVM pattern. There must be an elegant way to solve this since what I am trying to do is really widespread in different apps.
<TreeView Grid.Row="1" Background="AliceBlue" ItemsSource="{Binding TopPanelNodes}" Name="TopTreeView" SourceUpdated="TopTreeView_SourceUpdated" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" IsEnabled="False">
<TextBlock Text="{Binding Name}" >
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding UpdateInspectorCommand }" CommandParameter=??? />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
I would like to pass treeviewitem name as command parameter instead of ??? in my code so selected treeview item will be identified by name so I can get respective info and bind it to listview.

Solved it using RelativeSource.
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}}"
Works fine.

Related

How to bind to another function, inside a container with ItemsSource [duplicate]

I have an ItemsControl whose for the ItemTemplate DataTemplate contains a Button. I want the Command on the button to bind to a Command on the DataContext of the ItemsControl, not the ItemTemplate. I think the solution has to do with using RelativeSource, but my attempts so far have failed:
<ItemsControl ItemsSource="{Binding Games}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Path=GameSelectedCommand, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}"
Style="{StaticResource MenuButtonStyle}"
Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I get the Button to bind to the GameSelectedCommand of the ItemsControl's DataContext object?
You're setting the source of the binding to the ItemsControl itself. Therefore, you'll need to dereference the DataContext of the ItemsControl:
Command="{Binding DataContext.GameSelectedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
How would you have known this? Take a look at your debug output window when running the app. You'll see a message along the lines of "Cannot resolve property 'GameSelectedCommand' on type 'ItemsControl'".

Getting ContextMenu target from Item in ItemsControl

I have an ItemsControl bound to a collection on my ViewModel. This ItemsControl presents several "Messages". I need a ContextMenu that, when clicked, provides an option to Copy that particular message to the clipboard.
The Command should activate on the ViewModel and the CommandParameter I want to pass is the Message itself.
The problem I'm having is getting the actual message that the ContextMenu has been opened OVER.
I've tried mutliple different approaches, but I cannot seem to figure out a way to pass the message as the Command's parameter.
Should I look for a different approach to accomplish this? Is the issue using an ItemsControl with an ItemsPresenter?
<ScrollViewer CanContentScroll="False"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Grid.RowSpan="1">
<ItemsControl ItemsSource="{Binding MyActiveConversation.OrderedMessages, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding MyActiveConversation.IsOptOut, Converter={StaticResource BoolToEnabledInverter}}"
ItemTemplateSelector="{StaticResource tSelector}"
VirtualizingPanel.IsVirtualizing="False"
SourceUpdated="SourceUpdatedHandler" MinWidth="450">
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Template="{DynamicResource MenuItemControlTemplate}"
CommandParameter="{Binding}" Command="{Binding Path=Data.CopyTextMessageCommand, Source={StaticResource ContextProxy}}">
</MenuItem>
</ContextMenu>
</ItemsControl.ContextMenu>
<ItemsControl.Template>
<ControlTemplate>
<Grid>
<ItemsPresenter VirtualizingPanel.IsVirtualizing="False"/>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
CommandParameter="{Binding}" works good for me, same for CommandParameter="{Binding .}"

How to parse Children of TreeviewItem correctly in ViewModel?

I have a TreeView control in my WPF program,which gets data from MySQL server and show databases and tables:
Server
...Databases
...Tables
What I want is when I click an item in the Treeview,I can get the Server/Databases/Tables' name.However,after hours testing,I still could not get the name.
HierarchicalDataTemplate of server
<HierarchicalDataTemplate
DataType="{x:Type local:ServerViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Figures/server.png" />
<TextBlock Text="{Binding ServerName}" />
</StackPanel>
</HierarchicalDataTemplate>
The other two are similar, except that HierarchicalDataTemplate of table does not have ItemsSource.
As I'm using MvvmLight pattern,I pass the SelectedItem as CommandParameterto ViewModel:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<cmd:EventToCommand
Command="{Binding tableSelected}"
CommandParameter="{Binding SelectedItem,ElementName=MyTreeview}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In the ViewModel,I delared a command to handle SelectedItemChanged event.I implemented the Execute() method as below,yet it did not work.Whenever I click a TreeviewItem,an unhandled exception System.Reflection.TargetInvocationException would be thrown.
private void GetSelectedItem(object parameter)
{
var item = parameter as TreeViewItem;
StackPanel stackpanel = (StackPanel)VisualTreeHelper.GetChild(item, 0);
TextBlock textblock = (TextBlock)VisualTreeHelper.GetChild(stackpanel, 1);
MessageBox.Show(textblock.Text);
}
Update
Command
public ICommand tableSelected { get; private set; }//In the ViewModel
tableSelected = new RelayCommand<object>((obj) => GetSelectedItem(obj), (obj) => true);
//Implemented in the ViewModel's constructor
I've read several related posts on Stackoverflow,like How I can get content of TreeViewItem in WPF?,WPF: Getting TreeViewItem's constituent controls,yet I still can not get it right.Please help,thanks.
I realized that what I was doing previously is totally wrong.The SelectedItem is not a collections of UI control,but purely an instance the same type as that of DataType.
<HierarchicalDataTemplate
DataType="{x:Type local:DatabaseViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Figures/database.png" />
<TextBlock Text="{Binding DatabaseName}" />
</StackPanel>
</HierarchicalDataTemplate>
The code above is part of my treeview.If I click a treeviewitem which uses this template,what I get is a DatabaseViewModel instance,rather than a StackPanel or its children.This is really misleading for WPF beginners.

WPF ListBox selection

I have a problem with extended selection in ListBox. Let's say I have a ListBox with 10 items and I'm selecting first 5 of them using Shift button. The SelectionChanged event is fired with 5 items. After that I want to select 3 items from those 5, again with Shift button pressed, but SelectionChanged event is not fired. How can I react to the second selection of items, when I'm selecting 3 of those 5 previously selected ones?
can you show the xaml code as well I would like to see what your binding looks like
it should looks something like this.. but I can't be certain unless I see your code
In Command binding you have used binding which has relative source binding...
consider making these changes in binding
1) using list box as Ancestortype
2) While binding use Path=DataContext.SelectionChangedCommand otherwise it will take list box as datacontext.
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
here is an example of what the XAML would look like for ListBoxItem Template
<Grid>
<StackPanel Orientation="Horizontal">
<Label Width="180">Field1</Label>
<ListBox Height="200"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding List1, Mode=OneWay}"
Name="listBox1"
SelectionMode="Single"
Width="300">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="290">
<TextBlock Width="90" Text="{Binding}"></TextBlock>
<ComboBox Width="180" ItemsSource="{Binding DataContext.List2, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" DisplayMemberPath="Field1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>

ComboBox in a ListBox does not fire SelectionChanged event

I have a wpf, mvvm app, using the catel (http://catel.codeplex.com) framework\toolkit, C# .Net 4.0. The app has a ListBox with a TextBlock and ComboBox. The ListBox and ComboBox are populated from 2 different ObservableCollection from the ViewModel. I need to save (to a db), when the user clicks a button, each row in the ListBox where the user has selected an item from the ComboBox. The SelectionChanged event does not fire for any of the ComboBoxes in the ListBox. The idea being that I add to a list (ArrayList or IList?), in the ViewModel, each time the user selects an item in a ComboBox and for what row the item has been selected.
Or am I going about this the wrong way by trying to use the ComboBoxe SelectionChanged event? I also tried iterating thru the ListBox.Items but this seems like a hak and I want to avoid ui element logic in the ViewModel if possible.
The xaml:
<Grid>
<StackPanel Orientation="Horizontal">
<Label Width="180">Field1</Label>
<ListBox Height="200"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding List1, Mode=OneWay}"
Name="listBox1"
SelectionMode="Single"
Width="300">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="290">
<TextBlock Width="90" Text="{Binding}"></TextBlock>
<ComboBox Width="180" ItemsSource="{Binding DataContext.List2, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" DisplayMemberPath="Field1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<catel:EventToCommand Command="{Binding SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
ViewModel code:
//in the ViewModel constructor
SelectionChangedCommand = new Command<SelectionChangedEventArgs>(OnSelectionChangedCommandExecute, OnSelectionChangedCommandCanExecute);
public Command<SelectionChangedEventArgs> SelectionChangedCommand { get; private set; }
private bool OnSelectionChangedCommandCanExecute()
{
return true;
}
private void OnSelectionChangedCommandExecute(SelectionChangedEventArgs e)
{
// add or update list....
}
In Command binding you have used binding which has relative source binding...
consider making these changes in binding
1) using list box as Ancestortype
2) While binding use Path=DataContext.SelectionChangedCommand otherwise it will take list box as datacontext.
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />

Categories