0

The problem might look similar to this But I don't have an issue with updating the TreeView if the ObservableCollection is modified in the same window (as in the case of the linked post).

I have a TreeView located in my MainWindow. I use a HierarchicalDataTemplate for the TreeView, basically the TreeView is structured into three levels of hierarchy: Group, Report, Node

For each level of the TreeView, I have created simple Model classes, each class will have a Name property and an ObservableCollection of its children class as second property (e.g., Group class will have a Name and a ReportList property, whilst Report class will have a Name and NodeList property)

In the ViewModel of my MainWindow, I have an ObservableCollection of the Group objects (I named it Groups) that is bound to the TreeView. I also have a RelayCommand method that will add a new Group object to the Groups collection (Let's call it the ImportReport Method). If I bind the RelayCommand to the button inside the MainWindow, everything worked fine, the TreeView is updated immediately with new Group Object each time I press the button.

However, this becomes an issue when I have another window, let's call it ImportWindow. In ImpportWindow, I have a button. Probably this is the cause of the issue, but I use the same ViewModel for both the ImportWindow and the MainWindow so that they can share the Groups property that is bound to the TreeView in the MainWindow. My goal is to perform addition to the TreeView from the second window by pressing a button from there. Now, If I move the binding of the ImportReport command to the button in ImportWindow instead, then try to run the code, pressing the button on ImportWindowdoesn't update the TreeView at all. I have debugged the code and the Groups ObservableCollection in the ViewModel indeed recorded a new addition but the TreeView doesn't display the new Group object.

Any explanation to this? Thanks!

MainWindow

<Window x:Class="StressReportWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:StressReportWPF"
    xmlns:ribbon="clr-namespace:System.Windows.Controls.Ribbon;assembly=System.Windows.Controls.Ribbon"
    xmlns:viewModel="clr-namespace:StressReportWPF.ViewModel"
    xmlns:data="clr-namespace:StressReportWPF.DataElements"
    mc:Ignorable="d"
    Title="StressReportApp" 
    Height="750" 
    Width="1000">

<Window.DataContext>
    <viewModel:MainViewModel/>
</Window.DataContext>

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type data:DisplayGroup}"
                              ItemsSource="{Binding ReportList}">
        <TextBlock Text="{Binding DisplayGroupName}"/>
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate DataType="{x:Type data:DisplayReport}"
                              ItemsSource="{Binding NodeList}">
        <TextBlock Text="{Binding DisplayReportName}"/>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type data:DisplayNode}">
        <TextBlock Text="{Binding DisplayNodeName}"/>
    </DataTemplate>
</Window.Resources>

<Border Background="#e7f4fe">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="75"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        ...
        
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="250"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Border Grid.Column="0"
                    Margin="0,20,20,20"
                    Background="#dde6ed"
                    BorderBrush="Black"
                    BorderThickness="1">
                <ScrollViewer>
                    <TreeView Background="Transparent"
                              ItemsSource="{Binding Groups, UpdateSourceTrigger=PropertyChanged}">
                        
                    </TreeView>
                </ScrollViewer>
            </Border>

            <Border Grid.Column="1"
                    Margin="20,20,0,20"
                    Background="White"
                    BorderBrush="Black"
                    BorderThickness="1">

                <ContentControl Margin="0"
                                Content="{Binding CurrentView}">
                    
                </ContentControl>
            </Border>
            
        </Grid>
    </Grid>
    
    
</Border>

ImportWindow

<Window x:Class="StressReportWPF.ImportWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:StressReportWPF"
    xmlns:viewModel="clr-namespace:StressReportWPF.ViewModel"
    xmlns:data="clr-namespace:StressReportWPF.DataElements"
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    mc:Ignorable="d"
    Title="ImportWindow" Height="450" Width="800"
    Background="#dde6ed">

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
    
    <DataTemplate x:Key="GroupComboBoxTemplate">
        <TextBlock Text="{Binding DisplayGroupName}"/>
    </DataTemplate>
    
</Window.Resources>

<Window.DataContext>
    <viewModel:MainViewModel/>
</Window.DataContext>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="500"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    ...

    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="150"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        
        ...

        <Button Grid.Row="2"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Height="40"
                Width="100"
                Content="Import"
                Style="{StaticResource StandardButtonStyle}"
                Command="{Binding ImportCommand}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <i:CallMethodAction MethodName="Close" 
                                        TargetObject="{Binding RelativeSource={RelativeSource
                                              Mode=FindAncestor,
                                              AncestorType=Window}}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>

        </Button>
    </Grid>
    
</Grid>

ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StressReportWPF.Core;
using StressReportWPF.DataElements;
using System.Collections.ObjectModel;
using System.Windows.Input;
using StressReportWPF.Views;

namespace StressReportWPF.ViewModel
{
    public class MainViewModel: ObservableObject
    {

    #region ViewModel-related Properties
    public RelayCommand HomePageViewCommand { get; set; }
    public RelayCommand LoadCaseAssignmentViewCommand { get; set; }
    public RelayCommand JointNumberAssignmentViewCommand { get; set; }
    public HomePageViewModel HomePageVM { get; set; }
    public LoadCaseAssignmentViewModel LoadCaseAssignmentVM { get; set; }
    public JointNumberAssignmentViewModel JointNumberAssignmentVM { get; set; }

    private object _currentView;

    public object CurrentView
    {
        get { return _currentView; }
        set
        {
            _currentView = value;
            OnPropertyChanged();
        }
    }
    #endregion

    private ICommand _importCommand;

    public ICommand ImportCommand
    {
        get
        {
            if (_importCommand == null)
            {
                _importCommand = new RelayCommand(
                    param => ImportReport()
                );
            }
            return _importCommand;
        }
    }

    private ICommand _importWindowCommand;

    public ICommand ImportWindowCommand
    {
        get
        {
            if (_importWindowCommand == null)
            {
                _importWindowCommand = new RelayCommand(
                    param => ShowImportWindow()
                );
            }
            return _importWindowCommand;
        }
    }

    #region Data Properties
    private readonly ObservableCollection<DisplayGroup> _groups = new ObservableCollection<DisplayGroup>();

    public ObservableCollection<DisplayGroup> Groups
    {
        get { return _groups; }
    }

    public bool IsNewGroupCreated { get; set; }
    public object SelectedGroup { get; set; }

    private DisplayGroup _newGroup = new DisplayGroup();

    public DisplayGroup NewGroup
    {
        get { return _newGroup; }
        set
        {
            _newGroup = value;
            OnPropertyChanged();
        }
    }

    #endregion

    public MainViewModel()
    {
        HomePageVM = new HomePageViewModel();
        LoadCaseAssignmentVM = new LoadCaseAssignmentViewModel();
        JointNumberAssignmentVM = new JointNumberAssignmentViewModel();

        CurrentView = HomePageVM;

        HomePageViewCommand = new RelayCommand(o =>
        {
            CurrentView = HomePageVM;
        });
        LoadCaseAssignmentViewCommand = new RelayCommand(o =>
        {
            CurrentView = LoadCaseAssignmentVM;
        });
        JointNumberAssignmentViewCommand = new RelayCommand(o =>
        {
            CurrentView = JointNumberAssignmentVM;
        });

        // Set a temporary Group list
        List<DisplayGroup> testGroupList = new List<DisplayGroup>();
        List<DisplayNode> testNodeList = new List<DisplayNode>();
        List<DisplayReport> testReportList = new List<DisplayReport>();

        DisplayNode nodeA = new DisplayNode();
        nodeA.DisplayNodeName = "Node A";
        DisplayNode nodeB = new DisplayNode();
        nodeB.DisplayNodeName = "Node B";
        DisplayNode nodeC = new DisplayNode();
        nodeC.DisplayNodeName = "Node C";

        testNodeList.AddRange(new List<DisplayNode> { nodeA, nodeB, nodeC }) ;
        ObservableCollection<DisplayNode> testNodeObsCol = new ObservableCollection<DisplayNode>(testNodeList);

        DisplayReport reportA = new DisplayReport();
        reportA.DisplayReportName = "Report A";
        reportA.NodeList = testNodeObsCol;

        DisplayReport reportB = new DisplayReport();
        reportB.DisplayReportName = "Report B";
        reportB.NodeList = testNodeObsCol;

        DisplayReport reportC = new DisplayReport();
        reportC.DisplayReportName = "Report C";
        reportC.NodeList = testNodeObsCol;

        testReportList.AddRange(new List<DisplayReport> { reportA, reportB, reportC });
        ObservableCollection<DisplayReport> testReportObsCol = new ObservableCollection<DisplayReport>(testReportList);

        DisplayGroup groupA = new DisplayGroup();
        groupA.DisplayGroupName = "Group A";
        groupA.ReportList = testReportObsCol;

        DisplayGroup groupB = new DisplayGroup();
        groupB.DisplayGroupName = "Group B";
        groupB.ReportList = testReportObsCol;

        DisplayGroup groupC = new DisplayGroup();
        groupC.DisplayGroupName = "Group C";

        testGroupList.AddRange(new List<DisplayGroup> { groupA, groupB, groupC });

        foreach (var item in testGroupList)
        {
            Groups.Add(item);
        }
    }

    public void ShowImportWindow()
    {
        //ImportViewModel importVM = new ImportViewModel();
        //importVM.Groups = this.Groups;
        ImportWindow importWindow = new ImportWindow();
        //importWindow.DataContext = importVM;
        importWindow.Show();
    }
    public void ImportReport()
    {
        if (IsNewGroupCreated)
        {
            DisplayGroup newGroup = new DisplayGroup();
            newGroup.DisplayGroupName = "Group D";
            Groups.Add(newGroup);
        }
        else
        {

        }
    }
}
}
  • You have **two** instances of `MainViewModel` like we can both have a Ford Mustang in Bullit green. When you start your engine mine will not, because even of the same kind they are independent like two instances – Sir Rufo Mar 03 '22 at 01:49
  • @SirRufo Ah so you mean that even though both Views use the same `MainViewModel` as `DataContext`, each of them ended up with their own version of `MainViewModel`. Thus, a change in the property for one instance of `MainViewModel` does not happen in the other `MainViewModel`? – Kevin Anggrek Mar 03 '22 at 02:08
  • 1
    @KevinAnggrek You instantiated different instances of MainViewModel both in MainWindow and ImportWindow by – emoacht Mar 03 '22 at 05:15
  • In order to share a single view model instance between both Windows, and assuming that ImportWindow is shown from the MainWindow code, do something like `var importWindow = new ImportWindow { DataContext = DataContext }; importWindow.Show();` – Clemens Mar 03 '22 at 09:54
  • @KevinAnggrek: The following XAML markup creates a new instance of the `MainViewModel` class which apparently isn't what you want here: `` – mm8 Mar 03 '22 at 13:46

0 Answers0