0

I'm trying to figure out how to work with AvalonDock in Prism. My MainApplciationView Xaml code:

<Window x:Class="WPFTestLab.Views.ApplicationWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:fa="http://schemas.fontawesome.com/icons/"
    xmlns:prism="http://prismlibrary.com/"
    prism:ViewModelLocator.AutoWireViewModel="True"
    xmlns:core="clr-namespace:WPFTestLab.Core;assembly=WPFTestLab.Core"  
    xmlns:avalonDock="https://github.com/Dirkster99/AvalonDock"
    Title="{Binding Title}" Width="1280" Height="720" WindowStartupLocation="CenterScreen">
<Grid>
    <avalonDock:DockingManager prism:RegionManager.RegionName="{x:Static core:RegionNames.MainRegion}">
        <avalonDock:LayoutRoot>
            <avalonDock:LayoutPanel Orientation="Horizontal">
                <avalonDock:LayoutDocumentPaneGroup DockWidth="100" Orientation="Vertical">
                    <avalonDock:LayoutDocumentPane>

                    </avalonDock:LayoutDocumentPane>
                </avalonDock:LayoutDocumentPaneGroup>
            </avalonDock:LayoutPanel>
        </avalonDock:LayoutRoot>
    </avalonDock:DockingManager>
</Grid>

And I create docking manager region adapter:

public class AvalonDockingRegionAdapter : RegionAdapterBase<DockingManager>
{
    private bool _updatingActiveViewsInManagerActiveContentChanged;
    #region Constructor

    public AvalonDockingRegionAdapter(IRegionBehaviorFactory factory)
        : base(factory)
    {
        _updatingActiveViewsInManagerActiveContentChanged = false;
    }

    #endregion  //Constructor


    #region Overrides

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

    protected override void Adapt(IRegion region, DockingManager regionTarget)
    {
        regionTarget.ActiveContentChanged += delegate (
            object sender, EventArgs e)
        {
            this.ManagerActiveContentChanged(sender, e, region, regionTarget);
        };
        region.Views.CollectionChanged += delegate (
            Object sender, NotifyCollectionChangedEventArgs e)
        {
            this.OnViewsCollectionChanged(sender, e, region, regionTarget);
        };
        region.Views.CollectionChanged += delegate (
            Object sender, NotifyCollectionChangedEventArgs e)
        {
            this.OnActiveViewsCollectionChanged(sender, e, region, regionTarget);
        };
        regionTarget.DocumentClosed += delegate (
                        Object sender, DocumentClosedEventArgs e)
        {
            this.OnDocumentClosedEventArgs(sender, e, region);
        };
    }

    private void OnActiveViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
    {
        if (_updatingActiveViewsInManagerActiveContentChanged) return;

        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            if (regionTarget.ActiveContent != null && regionTarget.ActiveContent != e.NewItems[0] &&
                region.ActiveViews.Contains(regionTarget.ActiveContent))
                region.Deactivate(regionTarget.ActiveContent);

            regionTarget.ActiveContent = e.NewItems[0];
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove &&
                 e.OldItems.Contains(regionTarget.ActiveContent))
        {
            regionTarget.ActiveContent = null;
        }
    }

    private void ManagerActiveContentChanged(object sender, EventArgs e, IRegion region, DockingManager regionTarget)
    {
        try
        {
            _updatingActiveViewsInManagerActiveContentChanged = true;

            if (regionTarget == sender)
            {
                var activeContent = regionTarget.ActiveContent;
                if (activeContent != null)
                {
                    foreach (var item in region.ActiveViews.Where(it => it != activeContent))
                        if (region.Views.Contains(item))
                            region.Deactivate(item);

                    if (region.Views.Contains(activeContent) && !region.ActiveViews.Contains(activeContent))
                        region.Activate(activeContent);
                }
            }
        }
        finally
        {
            _updatingActiveViewsInManagerActiveContentChanged = false;
        }

    }

    #endregion  //Overrides


    #region Event Handlers

    /// <summary>
    /// Handles the NotifyCollectionChangedEventArgs event.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event.</param>
    /// <param name="region">The region.</param>
    /// <param name="regionTarget">The region target.</param>
    void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            foreach (FrameworkElement item in e.NewItems)
            {
                UIElement view = item as UIElement;

                if (view != null)
                {
                    //Create a new layout document to be included in the LayoutDocuemntPane (defined in xaml)
                    LayoutDocument newLayoutDocument = new LayoutDocument();
                    newLayoutDocument.Content = item;

                    PaneViewModel viewModel = (PaneViewModel)item.DataContext;

                    if (viewModel != null)
                        newLayoutDocument.Title = viewModel.Title;

                    //Store all LayoutDocuments already pertaining to the LayoutDocumentPane (defined in xaml)
                    List<LayoutDocument> oldLayoutDocuments = new List<LayoutDocument>();
                    //Get the current ILayoutDocumentPane ... Depending on the arrangement of the views this can be either 
                    //a simple LayoutDocumentPane or a LayoutDocumentPaneGroup
                    ILayoutDocumentPane currentILayoutDocumentPane = (ILayoutDocumentPane)regionTarget.Layout.RootPanel.Children[0];

                    if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
                    {
                        //If the current ILayoutDocumentPane turns out to be a group
                        //Get the children (LayoutDocuments) of the first pane
                        LayoutDocumentPane oldLayoutDocumentPane = (LayoutDocumentPane)currentILayoutDocumentPane.Children.ToList()[0];
                        foreach (LayoutDocument child in oldLayoutDocumentPane.Children)
                        {
                            oldLayoutDocuments.Insert(0, child);
                        }
                    }
                    else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
                    {
                        //If the current ILayoutDocumentPane turns out to be a simple pane
                        //Get the children (LayoutDocuments) of the single existing pane.
                        foreach (LayoutDocument child in currentILayoutDocumentPane.Children)
                        {
                            oldLayoutDocuments.Insert(0, child);
                        }
                    }

                    //Create a new LayoutDocumentPane and inserts your new LayoutDocument
                    LayoutDocumentPane newLayoutDocumentPane = new LayoutDocumentPane();
                    newLayoutDocumentPane.InsertChildAt(0, newLayoutDocument);

                    //Append to the new LayoutDocumentPane the old LayoutDocuments
                    foreach (LayoutDocument doc in oldLayoutDocuments)
                    {
                        newLayoutDocumentPane.InsertChildAt(0, doc);
                    }

                    //Traverse the visual tree of the xaml and replace the LayoutDocumentPane (or LayoutDocumentPaneGroup) in xaml
                    //with your new LayoutDocumentPane (or LayoutDocumentPaneGroup)
                    if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
                        regionTarget.Layout.RootPanel.ReplaceChildAt(0, newLayoutDocumentPane);
                    else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
                    {
                        currentILayoutDocumentPane.ReplaceChild(currentILayoutDocumentPane.Children.ToList()[0], newLayoutDocumentPane);
                        regionTarget.Layout.RootPanel.ReplaceChildAt(0, currentILayoutDocumentPane);
                    }
                    newLayoutDocument.IsActive = true;
                }
            }
        }
    }

    /// <summary>
    /// Handles the DocumentClosedEventArgs event raised by the DockingNanager when
    /// one of the LayoutContent it hosts is closed.
    /// </summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The event.</param>
    /// <param name="region">The region.</param>
    void OnDocumentClosedEventArgs(object sender, DocumentClosedEventArgs e, IRegion region)
    {
        region.Remove(e.Document.Content);
    }

    #endregion
}

My question is how I can manipulate docking manager from MainApplicationViewModel. For example, I want to catch the active document content change and disable button in MainApplicationView. Or clicking the button in MainApplicationView should change the text in current active document view.

1 Answers1

0

Hopefully, the DockingManager has properties and commands that you can bind to stuff on your ApplicationWindowViewModel.

If not, you have to resort to EventToCommand and attached behaviours.

From there on, everything's standard. Document get's active, document view model's IsActive property becomes true, a service is notified, another view model listening to the service becomes aware of the change, the text is changed...

Haukinger
  • 10,420
  • 2
  • 15
  • 28