当从另一个窗口修改 ObservableCollection 时,绑定到 ObservableCollection 的 MainWindow 中的 MVVM WPF TreeView 不会更新
该问题可能类似于此 但是,如果在同一窗口中修改 ObservableCollection (如链接帖子的情况),则更新 TreeView 不会有问题。
我的MainWindow 中有一个TreeView。我对 TreeView 使用 HierarchicalDataTemplate,基本上 TreeView 分为三个层次结构:组、报表、节点
对于 TreeView 的每个级别,我创建了简单的模型类,每个类都有一个Name 属性及其子类的 ObservableCollection 作为第二个属性(例如,Group 类将具有 Name 和 ReportList 属性,而 Report 类将具有 Name 和 NodeList 属性)
在我的 ViewModel 中MainWindow,我有一个绑定到 TreeView 的 Group 对象的 ObservableCollection(我将其命名为 Groups)。我还有一个 RelayCommand 方法,它将向 Groups 集合添加一个新的 Group 对象(我们将其称为 ImportReport 方法)。如果我将 RelayCommand 绑定到 MainWindow 内的按钮,则一切正常,每次按下按钮时,TreeView 都会立即更新为新的组对象。
但是,当我有另一个窗口(我们将其称为 ImportWindow)时,这就会成为问题。在 ImportWindow 中,我有一个按钮。可能这就是问题的原因,但是我对 ImportWindow 和 MainWindow 使用相同的 ViewModel,以便它们可以共享组绑定到 MainWindow 中 TreeView 的属性。我的目标是通过按第二个窗口中的按钮来执行对 TreeView 的添加。现在,如果我将 ImportReport 命令的绑定移动到 ImportWindow 中的按钮,然后尝试运行代码,按 ImportWindow 上的按钮根本不会更新 TreeView。我已经调试了代码,ViewModel 中的 Groups ObservableCollection 确实记录了新的添加,但 TreeView 不显示新的 Group 对象。
对此有何解释?谢谢!
主窗口
<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>
导入窗口
<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
{
}
}
}
}
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
{
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论