PRISM 5 MEF AvalonDock 2.0 DataAdapter注册视图和父选择

时间:2014-08-19 22:14:27

标签: c# mvvm prism mef avalondock

我正在尝试使用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。

在我的View的ViewModel中,我有一个属性,我使用另一个UI元素的交互成功设置。但是我无法从ViewModel(Property) - &gt;建立连接。 ContentContro(查看) - &gt; LayoutAnchorable(View的父级).IsSelected或,IsActive。

我知道如何绑定到父对象,但是这会占用属性,并且不允许我将它绑定到ViewModel属性。我也没有问题绑定到ViewModel属性,但除非我可以设置父属性,否则这是没用的。我也尝试过基于视图的事件。问题在于,一旦视图加载,它就不再喜欢调用自己的事件,除非它是由用户直接与该视图交互引起的。

简而言之,我只想根据我程序另一部分的交互情况,在需要时显示相应的窗口。也许我让它变得比它需要的更复杂。对此的任何帮助将不胜感激。

由于 詹姆斯

1 个答案:

答案 0 :(得分:2)

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

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;
        }

完成了一个障碍,接下来的一个障碍。对于本文中所有信息的基础,PRISM 5.0 download中包含的ViewSwitchingNavigation.sln将帮助您入门。如果您想知道适配器注册中引用的ConfigureDefaultRegionBehaviors(),我可以从sample downloads中的StockTraderRI_Desktop.sln获取。

我希望这篇文章可以帮助其他人发现自己处于技术三明治所提供的同一个泡菜中。

此致 詹姆斯