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
{
}
}
}
}