снова Мне нужна ваша помощь.
Я создаю приложение WPF, где у меня есть MainViewModel для страницы, которая содержит древовидное представление. Это древовидное представление привязано к ProjectTreeViewModel, который создается MainViewModel.
Теперь моя ProjectTreeViewModel улавливает событие щелчка (с помощью команды реле), которое сообщает ему, какой узел был нажат.
Мне нужна эта информация внутри моей модели MainView. Как мне передать его туда?
РЕДАКТИРОВАТЬ ... работоспособный пример
некоторые данные для отображения в дереве:
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):
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; }
}
}
A 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
}
}
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>
... и его codebehind :
using System.Windows;
using WpfApp1.ViewModels;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
... после нескольких часов экспериментов я решил. Решение находится в приведенных выше фрагментах, и я не знаю, лучший ли это способ. Я сделал это следующим образом:
Командный метод ExecuteTreeNodeSelected
изменяет свойство publi c SelectedNode
. Это вызывает уведомление OnPropertyChanged("SelectedNode");
.
При создании модели TreeView в MainViewModel я добавил прослушиватель событий к событию PropertyChanged в TreeViewModel _projectTree.PropertyChanged += ProjectTreePropertyChanged;
. Это событие изменяет SelectedNode
-свойство MainViewModel, которое уведомляет пользовательский интерфейс.
Спасибо за терпение