通知父ViewModel子视图模型

时间:2015-07-22 17:10:56

标签: wpf mvvm treeview

我在这里阅读了一些文章,描述了如何收听所提出的通知。但是:我仍然无法将这些应用到我的应用程序中。

我目前有一个包含多个"页面的应用程序"。

其中一个页面包含一个WPF Treeview控件以及几个ViewModel和数据模型。

public class FoldersSearchViewModel
{
    private ReadOnlyCollection<DriveTreeViewItemViewModel> _drives;

    public FoldersSearchViewModel(string[] logicalDrives)
    {
        _drives = new ReadOnlyCollection<DriveTreeViewItemViewModel>(
            Environment.GetLogicalDrives()
            .Select(s => new DriveInfo(s))
            .Where(di => di.IsReady)
            .Select(di => new DriveTreeViewItemViewModel(di))
            .ToList()
        );
    }

    public ReadOnlyCollection<DriveTreeViewItemViewModel> Drives
    {
        get { return _drives; }
    }
}

此ViewModel包含DriveTreeViewItemViewModels,并通过DataContext绑定到UserControl(&#34; page&#34;)。

Drive-和DirectoryTreeViewItemViewModel类包含一些属性,但基于TreeViewItemViewModel,您可以在此处看到:

public class TreeViewItemViewModel : INotifyPropertyChanged
{
    #region Data

    static readonly protected TreeViewItemViewModel DummyChild = new TreeViewItemViewModel();

    readonly ObservableCollection<TreeViewItemViewModel> _children;
    readonly TreeViewItemViewModel _parent;

    bool _isExpanded;
    bool _isSelected;

    #endregion // Data

    #region Constructors

    protected TreeViewItemViewModel(TreeViewItemViewModel parent, bool lazyLoadChildren)
    {
        _parent = parent;

        _children = new ObservableCollection<TreeViewItemViewModel>();

        if (lazyLoadChildren)
            _children.Add(DummyChild);
    }

    // This is used to create the DummyChild instance.
    private TreeViewItemViewModel()
    {
    }

    #endregion // Constructors

    #region Presentation Members

    #region Children

    /// <summary>
    /// Returns the logical child items of this object.
    /// </summary>
    public ObservableCollection<TreeViewItemViewModel> Children
    {
        get { return _children; }
    }

    #endregion // Children

    #region HasLoadedChildren

    /// <summary>
    /// Returns true if this object's Children have not yet been populated.
    /// </summary>
    public bool HasDummyChild
    {
        get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
    }

    #endregion // HasLoadedChildren

    #region IsExpanded

    /// <summary>
    /// Gets/sets whether the TreeViewItem 
    /// associated with this object is expanded.
    /// </summary>
    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;

            // Lazy load the child items, if necessary.
            if (this.HasDummyChild)
            {
                this.Children.Remove(DummyChild);
                this.LoadChildren();
            }
        }
    }

    #endregion // IsExpanded

    #region IsSelected

    /// <summary>
    /// Gets/sets whether the TreeViewItem 
    /// associated with this object is selected.
    /// </summary>
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                this.OnPropertyChanged("IsSelected");
            }
        }
    }

    #endregion // IsSelected

    #region LoadChildren

    /// <summary>
    /// Invoked when the child items need to be loaded on demand.
    /// Subclasses can override this to populate the Children collection.
    /// </summary>
    protected virtual void LoadChildren()
    {
    }

    #endregion // LoadChildren

    #region Parent

    public TreeViewItemViewModel 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
}

我已经按照http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode中描述的教程和想法进行了操作,到目前为止一切都很顺利。

我的问题是:我想添加一个字符串&#34; select&#34;作为FoldersSearchViewModel的一个属性,它包含所选子ViewModel的路径。 DriveTreeViewItemViewModel和DirectoryTreeViewItemViewModel都有一个&#34; Path&#34;属性,包含孩子的完整路径。

所以:一旦OnPropertyChanged(&#34; IsSelected&#34;)被调用,我想通知FoldersSearchViewModel,并让方法将Path-property从选定的TreeViewItemViewModel复制到新的&#34;选择&# 34;(字符串)属性。

我可以通过将FoldersSearchViewModel对象传递给构造函数中的子对象和子对象等来实现这一点 - 但是没有更好的方法吗?我想我应该将FoldersSearchViewModel挂钩到每个节点和子节点的PropertyChanged事件,但我想知道在这种情况下有MVVM经验的人会做什么。

顺便说一句:我可以使用WPF Treeview.SelectedItem来获取当前选择的TreeViewItemViewModel,但这听起来不对,因为我想保持视图,模型和视图模型分开。

P.s。:我试过阅读并使用MVVM in WPF - How to alert ViewModel of changes in Model... or should I?,但遗憾的是它似乎无法解决我的问题。

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:0)

MVVM的方法是使用信使/事件聚合器模式并广播事件。

答案 1 :(得分:0)

我的工作方式是,Messenger服务是一个单独的讨论。我也使用DI,因此需要使用它的VM会将IMessengerService实例注入其中。

IMessengerService看起来像:

public interface IMessengerService : IServiceBase
{
    Message<T> GetMessage<T>() where T : IMessageBase;
}

消息&#34; param&#34;类可以在应用程序范围内使用,因此您可能会有类似的内容:

public class FolderOpened : IMessageBase
{
}

因此,FolderOpened类在整个应用程序中都可用,它在编译时明确定义。

任何关心此消息的客户端都将在其VM构造函数中订阅该消息:

_messenger.GetMessage()。处理程序+ = ...

如果发件人已经注册了&#34;那么这并不重要。它,信使只是基于消息类类型。

任何人都可以随时发送消息:

_messenger.GetMessage()的SendMessage(...);

YMMV,但我的信使会自动断开已处置/不存在的订阅者,但实际上,正确的方法是让VM取消订阅其终结者或处置方法。

这清楚了吗?