0
votes

I have a 2 level WPF treeview. When I click on a child item, the correct selectedCommand is triggered and all works well.

But when I click on my details view and update the field of this selected item, it unselect my childItem and fired the parent command because the parent is selected, but I need to just keep my childItem selected.

I have found some topics about the same problem, but I use command for my binding and not just code behind so I don't know how to make this solution work for me.

Trigger of my command :

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectedItemChanged">
        <i:InvokeCommandAction
            Command="{Binding ItemSelectedCommand}"
            CommandParameter="{Binding SelectedItem, ElementName=TreeView}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

My Treeview :

<TreeView x:Name="TreeView"
        ItemsSource="{Binding Modules}">
    <HierarchicalDataTemplate DataType="{x:Type module:ParentViewModel}" ItemsSource="{Binding ChildItems}">
        <TextBlock Text="Parent"/>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate DataType="{x:Type module:ChildViewModel}">
        <TextBlock Text="{Binding Path=childName}"/>
    </HierarchicalDataTemplate>                  
</TreeView>

And here is my command which is in my ViewModel file

public ICommand ItemSelectedCommand
{
   get
   {
        return _itemSelectedCommand ?? (_itemSelectedCommand = new CommandHandler(param => SelectedCommand(param)));
   }
}

public void SelectedCommand(object selectedItem)
{
     //code to activate my details view with prism
    if(selectedItem.GetType().Name == "ParentType")
    { 
        ActivateParentView(); 
    }
    else 
    {
        ActivateDetailsView(.....); //activate child view
    }
}

So, I trigger the same command when selected an item in my treeview but when I select the child item, the parent event is also triggered so my command is triggered 2 times and activate my parent view and don't stay in my child view. How can I stop propagating the event if I have already pass through child command ? How can I make it working with my command and not in code behind ?

EDIT


Here is my function ActivateDetailsView() which is called by my SelectedCommand I have 13 modules and I have one detailsView in each module, so when I click on my selected item, I will search using reflexivity for the view I need to activate and I use PRISM library to activate it.

private void ActivateDetailsView(string nameTypeItem,IRegion tabConfigurationRegion, ModuleItemBaseViewModel selectedItem)
    {
        try
        {
            string viewName = "ModuleItem" + nameTypeItem + "DetailsView";

            string moduleName = "Module" + nameTypeItem;
            string fullViewName = moduleName + ".Views." + viewName + ", " + moduleName;

            var typeOfCurrentView = Type.GetType(fullViewName);
            //var view = Activator.CreateInstance(typeOfCurrentView);
            var view = tabConfigurationRegion.GetView(viewName);
            if (view == null)
            {
                view = _container.Resolve(typeOfCurrentView);

                // Add the view to the main region. This automatically activates the view too.
                tabConfigurationRegion.Add(view, viewName);
            }

            // The view has already been added to the region so just activate it.
            tabConfigurationRegion.Activate(view);

            string viewModelName = "ModuleItem" + nameTypeItem + "DetailsViewModel";
            string fullViewModelName = moduleName + ".ViewModels." + viewModelName + ", " + moduleName;

            var typeOfCurrentViewModel = Type.GetType(fullViewModelName);
            //equivalent to ModuleItemSiemensDetailsViewModel viewModelM = view.DataContext as ModuleItemSiemensDetailsViewModel;
            var propertyInfoDataContext = view.GetType().GetProperty("DataContext");
            var viewModelModuleItem = propertyInfoDataContext.GetValue(view, null);

            if (viewModelModuleItem != null)
            {
                PropertyInfo prop = typeOfCurrentViewModel.GetProperty("CurrentModuleItem");

                //equivalent to viewModelModuleItem.CurrentModuleItem = selectedItem as ModuleItemBaseViewModel;
                prop.SetValue(viewModelModuleItem, selectedItem, null);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            Debug.WriteLine(ex.StackTrace);
        }
    }

To reproduce the problem : Start a new WPF project, Add one main project in your solution with a view with a treeview and a treeviewviewmodel with the list of ParentViewModel which will be display on your treeview. Define an other project with 2 viewmodels : one class ParentVM with an ObservableCollection and a childViewModel, let one or two property like name, and bind it to the treeview. In the second project, define a detailsView to see properties of your ChildViewModel. On your main project, in the treeviewviewmodel add a SelectedCommandFunction which use prism to activate your detailView. (You need to have a DetailsView when you click on the Parent and an other one when you click on the ChildView.

1
How exactly are you doing this: "update the field of this selected item"? As a workaround you can set flag to ignore future calls of this command (I guess with the use of Application.Current.Dispatcher.Invoke to reset it) during update.Sinatr
I have a field directly bind on my ViewModelChild property. It raised an PropertyChangedEvent. I will see your function !Karakayn
You can't bind to a field. Can you show the code? Ideally - make mcve. Problem could be simply in your code, but you don't show it. If it's a complicated issue, it will be much easier for others to help if they don't have to write everything on their own (read - start from reproducing the issue with your mcve).Sinatr
I will not copy useless stuff here Eliminate any issues that aren't relevant to the problem so my bindings are correct like <StackPanel> <TextBlock Text="Name:" /> <TextBox Text="{Binding Path=CurrentModuleItem.Name}" /> </StackPanel>. I will just add my real function which activate the view because it use PRISM for it and maybe it's the way I am using prism which is incorrect ...Karakayn
I have also added a way to reproduce my issue with a new project :)Karakayn

1 Answers

1
votes

Every TreeViewItem is in the visual tree of its parent, and the event will bubble down, so if you clicked the tree leaf, and it has two parents, then the event will be called 3 times.

Ways to prevent this:

  1. Bind the IsSelected of the TreeItem directly to IsSelected of your VM, and do your thing OnPropertyChanged of IsSelected property of your VM.
  2. In a roundabout way: in codebehind, when you handle the event, check if the OriginalSource is the same as the sender (similar to here)
  3. Implement your own EventTrigger that marks events as Handled