Prism2 Region适配器用于AvalonDock的DocumentPane和DockingPane?

时间:2009-05-29 01:56:01

标签: prism avalondock

有没有人有关于如何为AvalonDock的DocumentPane和DockingPane创建Region Adapter的示例代码?

4 个答案:

答案 0 :(得分:4)

Markus Raufer在CompositeWpfContrib项目的CodePlex项目中添加了两个区域适配器,支持DocumentPaneDockingPane

答案 1 :(得分:3)

我使用Raffaeu Bermuda代码段来支持Avalon标签区域适配器,但发现有些问题没有解决:

1-它不支持激活某个视图(又名 - 制表符 - DockableContent),因此代码Region.Activate(object view)将不起作用。

2-所有选项卡默认在区域中处于活动状态。因此默认情况下Region.ActiveViews集合具有所有视图,这不是理想的,因为有时我需要验证视图是否处于活动状态(您可以想象工具栏区域上的保存按钮仅在执行SaveCommand时在我们的例子中当前的活动视图=选项卡)

3-封闭视图实际上并未关闭,只是隐藏。即使您在添加newDockableContent时设置HideOnClose = true,它仍然不会从Region.Views集合中删除。这可能会导致内存泄漏问题。

4-如果您之前在窗格中添加了DockableContent,它们将无法同步并添加到Region.Views集合中。

以下是我现在使用的代码,它只是来自PRISM源代码中的Selector Adapter和Selector Sync Behavior的一个小调整:

AvalonRegionAdapter类:

public class AvalonRegionAdapter : RegionAdapterBase<DocumentPane>
{
    public AvalonRegionAdapter(IRegionBehaviorFactory factory) : base(factory) {}

    protected override void AttachBehaviors(IRegion region, DocumentPane regionTarget)
    {
        if (region == null) throw new System.ArgumentNullException("region");
        //Add the behavior that syncs the items source items with the rest of the items
        region.Behaviors.Add(AvalonDocumentSyncBehavior.BehaviorKey,
                             new AvalonDocumentSyncBehavior()
        {
            HostControl = regionTarget
        });
        base.AttachBehaviors(region, regionTarget);
    }

    protected override void Adapt(IRegion region, DocumentPane regionTarget){ }

    protected override IRegion CreateRegion()
    {
        return new Region();
    }
}

AvalonDocumentSyncBehavior行为代码:

public class AvalonDocumentSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
{
    /// <summary>
    /// Name that identifies the SelectorItemsSourceSyncBehavior behavior in a collection of RegionsBehaviors. 
    /// </summary>
    public static readonly string BehaviorKey = "AvalonDocumentSyncBehavior";
    private bool updatingActiveViewsInHostControlSelectionChanged;
    private Selector hostControl;

    /// <summary>
    /// Gets or sets the <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
    /// </summary>
    /// <value>
    /// A <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
    /// </value>
    /// <remarks>For this behavior, the host control must always be a <see cref="Selector"/> or an inherited class.</remarks>
    public DependencyObject HostControl
    {
        get
        {
            return this.hostControl;
        }

        set
        {
            this.hostControl = value as Selector;
        }
    }

    /// <summary>
    /// Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.
    /// </summary>
    protected override void OnAttach()
    {
        bool itemsSourceIsSet = this.hostControl.ItemsSource != null;
        if (itemsSourceIsSet)
        {
            //throw new InvalidOperationException(Resources.ItemsControlHasItemsSourceException);
        }

        this.SynchronizeItems();

        this.hostControl.SelectionChanged += this.HostControlSelectionChanged;
        this.Region.ActiveViews.CollectionChanged += this.ActiveViews_CollectionChanged;
        this.Region.Views.CollectionChanged += this.Views_CollectionChanged;
    }

    private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            int startIndex = e.NewStartingIndex;
            foreach (object newItem in e.NewItems)
            {
                UIElement view = newItem as UIElement;
                TabViewModel viewModel = ((UserControl)view).DataContext as TabViewModel;
                if (view != null)
                {
                    DockableContent newDockableContent = new DockableContent();
                    newDockableContent.Content = newItem;
                    //if associated view has metadata then apply it.
                    newDockableContent.Title = view.GetType().ToString();
                    if (viewModel != null)
                    {
                        //Image img = new Image();
                        //img.Source = new BitmapImage(new Uri(@"Resources/Alerts.png", UriKind.Relative));
                        newDockableContent.Title = viewModel.TabModel.Title;
                        newDockableContent.IsCloseable = viewModel.TabModel.CanClose;
                        //newContentPane.Icon = img.Source;
                    }

                    //When contentPane is closed remove from the associated region
                    newDockableContent.Closed += new EventHandler(newDockableContent_Closed);

                    newDockableContent.HideOnClose = false; 
                    this.hostControl.Items.Add(newDockableContent);
                    newDockableContent.Activate();
                }
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (object oldItem in e.OldItems)
            {
                this.hostControl.Items.Remove(oldItem);
            }
        }
    }

    void newDockableContent_Closed(object sender, EventArgs e)
    {
        var dockableContent = sender as DockableContent;
        if(dockableContent != null)
            if (this.Region.Views.Contains(dockableContent.Content))
            {
                this.Region.Remove(dockableContent.Content);
            }
    }

    private void SynchronizeItems()
    {
        List<object> existingItems = new List<object>();

        // Control must be empty before "Binding" to a region
        foreach (object childItem in this.hostControl.Items)
        {
            existingItems.Add(childItem);
        }

        foreach (object view in this.Region.Views)
        {
            this.hostControl.Items.Add(view);
        }

        foreach (object existingItem in existingItems)
        {
            this.Region.Add(existingItem);
        }
    }


    private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (this.updatingActiveViewsInHostControlSelectionChanged)
        {
            // If we are updating the ActiveViews collection in the HostControlSelectionChanged, that 
            // means the user has set the SelectedItem or SelectedItems himself and we don't need to do that here now
            return;
        }

        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            var selectedDockableContent = this.hostControl.SelectedItem as DockableContent;
            if (selectedDockableContent != null
                && selectedDockableContent.Content != null
                && selectedDockableContent.Content != e.NewItems[0]
                && this.Region.ActiveViews.Contains(selectedDockableContent.Content))
            {
                this.Region.Deactivate(selectedDockableContent.Content);
            }

            var _UIElement = e.NewItems[0] as FrameworkElement;
            this.hostControl.SelectedItem = _UIElement.Parent;
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove &&
                 e.OldItems.Contains(this.hostControl.SelectedItem))
        {
            this.hostControl.SelectedItem = null;
        }
    }

    private void HostControlSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        try
        {
            // Record the fact that we are now updating active views in the HostControlSelectionChanged method. 
            // This is needed to prevent the ActiveViews_CollectionChanged() method from firing. 
            this.updatingActiveViewsInHostControlSelectionChanged = true;

            object source;
            source = e.OriginalSource;

            if (source == sender)
            {
                foreach (object item in e.RemovedItems)
                {
                    // check if the view is in both Views and ActiveViews collections (there may be out of sync)
                    var dockableContent = item as DockableContent;
                    if (this.Region.Views.Contains(dockableContent.Content) && this.Region.ActiveViews.Contains(dockableContent.Content))
                    {
                        this.Region.Deactivate(dockableContent.Content);
                    }
                }

                foreach (object item in e.AddedItems)
                {
                    var dockableContent = item as DockableContent;
                    if (this.Region.Views.Contains(dockableContent.Content) && 
                         !this.Region.ActiveViews.Contains(dockableContent.Content))
                    {
                        this.Region.Activate(dockableContent.Content);
                    }
                }
            }
        }
        finally
        {
            this.updatingActiveViewsInHostControlSelectionChanged = false;
        }
    }
}

用于配置适配器的bootstrapper上的代码

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    var mappings = base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(AvalonDock.DocumentPane), 
                             this.Container.Resolve<AvalonRegionAdapter>());
    return mappings;
}

然后你需要TabModel和TabViewModel作为Raffaeu Bermuda

public sealed class TabModel : DependencyObject
{
   public string Title
   {
       get { return (string)GetValue(TitleProperty); }
       set { SetValue(TitleProperty, value); }
   }

   // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty TitleProperty =
       DependencyProperty.Register("Title", typeof(string), typeof(TabModel));

   public bool CanClose
   {
       get { return (bool)GetValue(CanCloseProperty); }
       set { SetValue(CanCloseProperty, value); }
   }

   // Using a DependencyProperty as the backing store for CanClose.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty CanCloseProperty =
       DependencyProperty.Register("CanClose", typeof(bool), typeof(TabModel));

   public bool IsModified
   {
       get { return (bool)GetValue(IsModifiedProperty); }
       set { SetValue(IsModifiedProperty, value); }
   }

   // Using a DependencyProperty as the backing store for IsModified.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty IsModifiedProperty =
       DependencyProperty.Register("IsModified", typeof(bool), typeof(TabModel));

}

TabViewModel充当基类:

public class TabViewModel : INotifyPropertyChanged
{
    private TabModel _tabModel;

    public TabModel TabModel
    {
        get { return this._tabModel; }
        set
        {
            this._tabModel = value;
            OnPropertyChanged("TabModel");
        }
    }
    public TabViewModel()
    {
        this.TabModel = new TabModel();
        this.TabModel.CanClose = true;
        this.TabModel.IsModified = false;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

如果您需要进一步的帮助,请告诉我,我会在不久的将来发布一篇博客。

答案 2 :(得分:2)

由于Avalon DocumentPane和DockingPane都基于System.Windows.Controls.Primitives.Selector,因此您可以在Prism中使用默认的SelectorRegionAdapter。

只需将您的控件建立在DockableContent

<ad:DockableContent x:Class="DesignerWPF.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock"
             d:DesignHeight="300" d:DesignWidth="300" Title="dans">
    <Grid>
        <TextBox Text="sdfdf"></TextBox>
    </Grid>
</ad:DockableContent>
主shell.xmal上的

设置了dockablepane中的区域

       <ad:DockingManager x:Name="dockManager" Grid.Row="1" Margin="0,4,0,0">
            <ad:ResizingPanel Orientation="Horizontal">
                <ad:DockablePane cal:RegionManager.RegionName="LeftRegion">

                </ad:DockablePane>
                <ad:DocumentPane cal:RegionManager.RegionName="DocumentRegion">

                </ad:DocumentPane>
            </ad:ResizingPanel>
        </ad:DockingManager>

然后,当您为控件初始化演示者时,它将显示在停靠栏中。

public class UserTestControl : IModule
    {
        public UserTestControl(IUnityContainer container, IRegionManager regionManager)
        {
            Container = container;
            RegionManager = regionManager;
        }
        public void Initialize()
        {
            var addFundView = Container.Resolve<UserControl1>();
            RegionManager.Regions["LeftRegion"].Add(addFundView);
        }

        public IUnityContainer Container { get; private set; }
        public IRegionManager RegionManager { get; private set; }
    }

答案 3 :(得分:0)

我的建议是查看Prism源代码中的Microsoft.Practices.Composite.Presentation.Regions。具体来说,看一下ItemsControlRegionAdapter并将其用作模板。请记住继承RegionAdapterBase&lt;&gt;:

public class ItemsControlRegionAdapter : RegionAdapterBase<ItemsControl>

并覆盖引导程序中的ConfigureRegionAdapterMappings()。这看起来像是:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    RegionAdapterMappings mappings =  base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(Canvas), Container.Resolve<CanvasRegionAdapter>());
    return mappings;
}