PRISM 5 MEF AvalonDock 2.0数据适配器注册视图和父视图被选中

本文关键字:视图 注册 适配器 MEF AvalonDock 数据 PRISM | 更新日期: 2023-09-27 18:18:40

我正在尝试使用PRISM 5构建一个MVVM Windows应用程序,我已经用AvalonDock(下面的包装代码)包装了我的主要内容窗口。

    using Microsoft.Practices.Prism.Regions;
    using Xceed.Wpf.AvalonDock;
    using System.Collections.Specialized;
    using System.Windows;
    using Xceed.Wpf.AvalonDock.Layout;
namespace Central.Adapters
{
    using System.Linq;
    public class AvalonDockRegionAdapter : RegionAdapterBase<DockingManager>
    {
        /// <summary>
        /// This ties the adapter into the base region factory.
        /// </summary>
        /// <param name="factory">The factory that determines where the modules will go.</param>
        public AvalonDockRegionAdapter(IRegionBehaviorFactory factory)
            : base(factory)
        {
        }
        /// <summary>
        /// Since PRISM does not support the Avalon DockingManager natively this adapter provides the needed support. 
        /// </summary>
        /// <param name="region">This is the region that resides in the DockingManager.</param>
        /// <param name="regionTarget">The DockingManager that needs the window added.</param>
        protected override void Adapt(IRegion region, DockingManager regionTarget)
        {
            region.Views.CollectionChanged += (sender, e) =>
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        AddAnchorableDocument(regionTarget, e);
                        break;
                    case NotifyCollectionChangedAction.Remove:                    
                        break;
                }
            };
        }
        /// <summary>
        /// This adds the window as an anchorable document to the Avalon DockingManager.
        /// </summary>
        /// <param name="regionTarget">The DockingManager instance.</param>
        /// <param name="e">The new window to be added.</param>
        private static void AddAnchorableDocument(DockingManager regionTarget, NotifyCollectionChangedEventArgs e)
        {
            foreach (FrameworkElement element in e.NewItems)
            {
                var view = element as UIElement;
                var documentPane = regionTarget.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
                if ((view == null) || (documentPane == null))
                {
                    continue;
                }
                var newContentPane = new LayoutAnchorable
                                         {
                                             Content = view,
                                             Title = element.ToolTip.ToString(),
                                             CanHide = true,
                                             CanClose = false
                                         };
                documentPane.Children.Add(newContentPane);
            }
        }
        /// <summary>
        /// This returns the region instance populated with all of its contents.
        /// </summary>
        /// <returns>DockingManager formatted region.</returns>
        protected override IRegion CreateRegion()
        {
            return new AllActiveRegion();
        }
    }
}

然后我以这种方式在引导程序中注册这个适配器:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            var mappings = base.ConfigureRegionAdapterMappings();
            if (mappings == null)
            {
                return null;
            }
            mappings.RegisterMapping(typeof(DockingManager), new AvalonDockRegionAdapter(ConfigureDefaultRegionBehaviors()));
            return mappings;
        }

我面临的问题是,其他区域UI元素将需要某些LayoutAnchorable窗口成为活动和选择的窗口。我提供给LayoutAnchorable对象的内容是一个ContentControl。

在我的视图的ViewModel我有一个属性,我成功地设置使用另一个UI元素的交互。然而,我无法使连接从ViewModel(属性)-> ContentContro(视图)-> LayoutAnchorable(视图的父)。IsSelected或IsActive.

我知道如何绑定到父对象,但这消耗了属性,不允许我将它绑定到ViewModel属性。我也没有问题绑定到ViewModel属性,但这是无用的,除非我能让它设置父属性。我也尝试过基于视图的事件。这样做的问题是,一旦视图加载,它就不喜欢调用自己的事件了,除非它是由用户直接与该视图交互引起的。

简而言之,我只是想在需要时根据程序另一部分的交互显示适当的窗口。也许我把它弄得太复杂了。如有任何帮助,我将不胜感激。

谢谢詹姆斯。

PRISM 5 MEF AvalonDock 2.0数据适配器注册视图和父视图被选中

当我从问题中休息时,我从另一个角度来看待它。为了解决这个问题,我决定将包含视图的内容窗格的实例存储到一个单例字典类中:

using System;
using System.Collections.Generic;
using Xceed.Wpf.AvalonDock.Layout;
namespace Central.Services
{
    public class DockinWindowChildObjectDictionary
    {
        private static Dictionary<string, LayoutAnchorable> _contentPane = new Dictionary<string, LayoutAnchorable>();
        private static readonly Lazy<DockinWindowChildObjectDictionary> _instance = 
            new Lazy<DockinWindowChildObjectDictionary>(()=> new DockinWindowChildObjectDictionary(), true);
        public static DockinWindowChildObjectDictionary Instance 
        { 
            get
            {
                return _instance.Value;
            } 
        }
        /// <summary>
        /// Causes the constructor to be private allowing for proper use of the Singleton pattern.
        /// </summary>
        private DockinWindowChildObjectDictionary()
        {
        }
        /// <summary>
        /// Adds a Content Pane instance to the dictionary.
        /// </summary>
        /// <param name="title">The title given to the Pane during instantiation.</param>
        /// <param name="contentPane">The object instance.</param>
        public static void Add(string title, LayoutAnchorable contentPane)
        {
            _contentPane.Add(title, contentPane);
        }
        /// <summary>
        /// If a window needs to be removed from the dock this should be used 
        /// to also remove it from the dictionary.
        /// </summary>
        /// <param name="title">The title given to the Pane during instantiation.</param>
        public static void Remove(string title)
        {
            _contentPane.Remove(title);
        }
        /// <summary>
        /// This will return the instance of the content pane that holds the view.
        /// </summary>
        /// <param name="title">The title given to the Pane during instantiation.</param>
        /// <returns>The views Parent Instance.</returns>
        public static LayoutAnchorable GetInstance(string title)
        {
            return _contentPane[title];
        }
    }
}

在适配器中,我修改了以下代码:

private static void AddAnchorableDocument(DockingManager regionTarget, NotifyCollectionChangedEventArgs e)
        {
            foreach (FrameworkElement element in e.NewItems)
            {
                var view = element as UIElement;
                var documentPane = regionTarget.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
                if ((view == null) || (documentPane == null))
                {
                    continue;
                }
                var newContentPane = new LayoutAnchorable
                                         {
                                             Content = view,
                                             Title = element.ToolTip.ToString(),
                                             CanHide = true,
                                             CanClose = false
                                         };
                DockinWindowChildObjectDictionary.Add(element.ToolTip.ToString(),** newContentPane);
                documentPane.Children.Add(newContentPane);
            }
        }

然后我添加以下内容到ViewModel以获得我想要的效果:

public void OnNavigatedTo(NavigationContext navigationContext)
        {
            var viewParentInstance = DockinWindowChildObjectDictionary.GetInstance("Belt Plan");
            viewParentInstance.IsSelected = true;
        }

跨过一个障碍,进入下一个。这篇文章中所有信息的基础是ViewSwitchingNavigation。PRISM 5.0下载中包含的sln将帮助您入门。如果你想知道在适配器注册中引用的ConfigureDefaultRegionBehaviors(),我从StockTraderRI_Desktop中得到了它。示例下载中的SLN。

我希望这篇文章能帮助到那些发现自己和这个技术三明治一样的人。

真诚詹姆斯。