【发布时间】:2020-05-29 07:47:30
【问题描述】:
我再次需要你的帮助。
我正在构建一个 WPF 应用程序,其中我有一个用于页面的 MainViewModel,它包含一个树视图。此树视图绑定到由 MainViewModel 创建的 ProjectTreeViewModel。
现在我的 ProjectTreeViewModel 捕获了一个单击事件(使用中继命令),它告诉它单击了哪个节点。
我在 MainViewmodel 中需要这些信息。我如何将其转移到那里?
编辑...一个可运行的示例
要在树中显示的一些数据:
using WpfApp1.Models;
namespace WpfApp1.Dataprovider
{
class PlcAddressData
{
public static PlcAddress GetPlcRootItems(string projectName)
{
if (string.IsNullOrWhiteSpace(projectName))
projectName = "Projekt-Datenpunkte";
return new PlcAddress
{
Name = projectName,
NodeId = 0,
Children =
{
new PlcAddress
{
Name = "Allgemein",
Comment = "allgemeine Datenpunkte",
NodeId = 1,
ParentNodeId = 0
},
new PlcAddress
{
Name = "Infrastruktur",
Comment = "interne Datenpunkte der Infrastruktur",
ParentNodeId = 0,
NodeId = 2
},
new PlcAddress
{
Name = "lokale IOs",
Comment = "Datenpunkte der SPS-Baugruppe",
ParentNodeId = 0,
NodeId = 3,
Children =
{
new PlcAddress
{
Name = "IO 0",
Comment = "first Channel of Plc-IO-Card",
NodeId = 4,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 1",
Comment = "second Channel of Plc-IO-Card",
NodeId = 5,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 2",
Comment = "third Channel of Plc-IO-Card",
NodeId = 6,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 3",
Comment = "forth Channel of Plc-IO-Card",
NodeId = 7,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 4",
Comment = "fifth Channel of Plc-IO-Card",
NodeId = 8,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 5",
Comment = "sixth Channel of Plc-IO-Card",
NodeId = 9,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 6",
Comment = "seventh Channel of Plc-IO-Card",
NodeId = 10,
ParentNodeId = 3
},
new PlcAddress
{
Name = "IO 7",
Comment = "eighth Channel of Plc-IO-Card",
NodeId = 11,
ParentNodeId = 3
}
}
}
}
};
}
}
}
PlcAddress-模型(treeview-item 的数据):
using System.Collections.Generic;
namespace WpfApp1.Models
{
public class PlcAddress
{
private List<PlcAddress> _children = new List<PlcAddress>();
public List<PlcAddress> Children
{
get { return _children; }
set { _children = value; }
}
public int NodeId { get; set; }
public int ParentNodeId { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
}
一个RelayCommand:
using System;
using System.Windows.Input;
namespace WpfApp1.ViewModels.Commands
{
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null){ }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}
}
MainViewModel:
using WpfApp1.Models;
namespace WpfApp1.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
LoadProjectTree();
}
private void LoadProjectTree()
{
PlcAddress RootItem = Dataprovider.PlcAddressData.GetPlcRootItems("Parent Node of Project");
_projectTree = new ProjectTreeviewModel(RootItem);
_projectTree.PropertyChanged += ProjectTreePropertyChanged;
}
private void ProjectTreePropertyChanged(object sender, PropertyChangedEventArgs e)
{
ProjectTreeviewModel selectedNode = (ProjectTreeviewModel)sender;
System.Console.WriteLine("selectedNode changed:" + selectedNode.SelectedNode);
SelectedNode = selectedNode.SelectedNode;
//MessageBox.Show("Some Property changed");
}
#region Properties
private string _selectedNode;
public string SelectedNode {
get { return _selectedNode; }
set
{
_selectedNode = value;
OnPropertyChanged("SelectedNode");
}
}
private ProjectTreeviewModel _projectTree;
public ProjectTreeviewModel ProjectTree
{
get { return _projectTree; }
}
#endregion
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
PlcAddressViewModel 在树中显示为项目
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using WpfApp1.Models;
namespace WpfApp1.ViewModels
{
public class PlcAddressViewModel : INotifyPropertyChanged
{
#region Data
private Collection<PlcAddressViewModel> _children;
readonly PlcAddressViewModel _parent;
readonly PlcAddress _plcAddress;
bool _isExpanded;
bool _isSelected;
#endregion Data
#region Constructors
public PlcAddressViewModel(PlcAddress plcAddress) : this(plcAddress, null)
{
}
private PlcAddressViewModel(PlcAddress plcAddress, PlcAddressViewModel parent)
{
_parent = parent;
_plcAddress = plcAddress;
_children = new Collection<PlcAddressViewModel>(
(from child in _plcAddress.Children
select new PlcAddressViewModel(child, this))
.ToList<PlcAddressViewModel>());
}
#endregion Constructors
#region AddressProperties
public Collection<PlcAddressViewModel> Children
{
get { return _children; }
set { _children = value; }
}
public string Name
{
get { return _plcAddress.Name; }
}
public string Comment
{
get { return _plcAddress.Comment; }
}
#endregion AddressProperties
#region Presentation Members
#region IsExpanded
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value != _isExpanded)
{
_isExpanded = value;
this.OnPropertyChanged("IsExpanded");
}
// Expand all the way up to the root
if (_isExpanded && _parent != null)
_parent.IsExpanded = true;
}
}
#endregion IsExpanded
#region IsSelected
public bool IsSelected
{
get
{
if (_isSelected)
{
//Console.WriteLine("Nodeselected: " + this._plcAddress.Name);
}
return this._isSelected;
}
set
{
if (value != _isSelected)
{
_isSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
}
#endregion IsSelected
#region Parent
public PlcAddressViewModel Parent
{
get { return _parent; }
}
#endregion Parent
#endregion Presentation Members
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged Members
}
}
识别到 selectionchanged 的 ProjectTreeViewModel
using System;
using System.Collections.ObjectModel;
using WpfApp1.Models;
using WpfApp1.ViewModels.Commands;
namespace WpfApp1.ViewModels
{
public class ProjectTreeviewModel : INotifyPropertyChanged
{
#region Data
public RelayCommand TreeNodeSelected { get; private set; }
readonly ReadOnlyCollection<PlcAddressViewModel> _rootNodes;
readonly PlcAddressViewModel _rootAddress;
#endregion Data
#region Constructor
public ProjectTreeviewModel(PlcAddress rootAddress)
{
_rootAddress = new PlcAddressViewModel(rootAddress);
_rootNodes = new ReadOnlyCollection<PlcAddressViewModel>(
new PlcAddressViewModel[]
{
_rootAddress
});
TreeNodeSelected = new RelayCommand(ExecuteTreeNodeSelected, canExecuteMethod);
}
#endregion Constructor
#region Properties
private string _selectedNode;
public string SelectedNode
{
get { return _selectedNode; }
set
{
_selectedNode = value;
OnPropertyChanged("SelectedNode");
}
}
#endregion
#region RootNode
public ReadOnlyCollection<PlcAddressViewModel> ProjectNode
{
get { return _rootNodes; }
}
#endregion RootNode
#region Commands
private bool canExecuteMethod(object parameter)
{
return true;
}
private void ExecuteTreeNodeSelected(object parameter)
{
PlcAddressViewModel selectedNode = (PlcAddressViewModel)parameter;
Console.WriteLine("Found this node: " + selectedNode.Name);
SelectedNode = selectedNode.Name;
}
#endregion Commands
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
最后但同样重要的是,MainWindow.xaml
<Window x:Class="WpfApp1.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:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
xmlns:viewmodels="clr-namespace:WpfApp1.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
>
<DockPanel LastChildFill="True">
<StackPanel Margin="5" Orientation="Horizontal">
<TreeView DataContext="{Binding ProjectTree}" ItemsSource="{Binding ProjectNode}" DockPanel.Dock="Left"
x:Name="ProjectTree" Margin="0 0 2 0" Grid.Column="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding TreeNodeSelected}"
CommandParameter="{Binding ElementName=ProjectTree, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<!-- following Texblock is bound to a MainViewModels property -->
<TextBlock Text="{Binding SelectedNode}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DockPanel>
</Window>
...及其代码隐藏:
using System.Windows;
using WpfApp1.ViewModels;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
...经过几个小时的实验,我解决了它。解决方案在上面的 sn-ps 中,我不知道这是否是最好的方法。我是按照以下方式完成的:
命令方法ExecuteTreeNodeSelected 更改公共属性SelectedNode。这会触发通知OnPropertyChanged("SelectedNode");。
在创建 Treeviewmodel 时,在 MainViewModel 中,我向 TreeViewModel _projectTree.PropertyChanged += ProjectTreePropertyChanged; 的 PropertyChanged-event 添加了一个事件监听器。此事件更改通知 UI 的 MainViewModel 的 SelectedNode-property。
感谢您的耐心等待
【问题讨论】:
-
你可以编写某种事件来传递它的使用事件聚合器
-
对不起,我不知道你想告诉我什么。能否提供链接或示例?