[Version 2.0] Multiple anchorable regions

Apr 2, 2012 at 10:53 AM

Hi, I suspect this is quite a simple question, but I'm trying to create multiple anchorable regions within the same avalondocklayout; using vs2010 as a guide, say a solution explorer on the right (with say two tabs) and an immediate window down the bottom with another two tabs, and then a document pane to display my designer.

At the moment, I've got the document pane with multiple tabs, and an anchorable on the left hand side with two tabs (where the former is bound to an observablecollection of document view models, and the anchorables are bound to an observablecollection of anchorable view models).

The thing is I can't seem to figure out how to get different anchorable regions to get their view models from different sources, or how to get the bound collection to distribute their models to the different regions, as at the moment, my one anchorable region gets populated with all the view models in the collection, and the remaining anchorables get left empty.

[I tried having two dockingmanagers but can't seem to drag and drop tabs between the two managers].

Cheers.

Apr 4, 2012 at 8:11 PM

Same problem, but after few hours, i've just found a part of the solution...   try something like that.... hope it'll help

 

 

 

<avalonDock:DockingManager x:Name="dockManager" Grid.Row="1"
			DocumentsSource="{Binding Path=MainListCV, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
			ActiveContent="{Binding Path=MainListCV.CurrentItem, UpdateSourceTrigger=PropertyChanged}"
	 >

	<avalonDock:DockingManager.Theme>
		<avalonDockVS2010Theme:VS2010Theme/>
	</avalonDock:DockingManager.Theme>


	<avalonDockLayout:LayoutRoot>
		<avalonDockLayout:LayoutRoot.RootPanel>
			
			<avalonDockLayout:LayoutPanel Orientation="Horizontal">
				<avalonDockLayout:LayoutAnchorablePane>
					<avalonDockLayout:LayoutAnchorable>
						<ItemsControl DataContext="{Binding Path=PlayListCV, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
								Template="{StaticResource PlayListSqlTemplate}" />
					</avalonDockLayout:LayoutAnchorable>
				</avalonDockLayout:LayoutAnchorablePane>

				<avalonDockLayout:LayoutPanel Orientation="Vertical">
					<avalonDockLayout:LayoutAnchorablePane>
						<avalonDockLayout:LayoutAnchorable>
							<ItemsControl DataContext="{Binding Path=FilterCV, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
									  Template="{StaticResource FilterListTemplate}"/>
						</avalonDockLayout:LayoutAnchorable>
					</avalonDockLayout:LayoutAnchorablePane>
					
					<avalonDockLayout:LayoutDocumentPane >

					</avalonDockLayout:LayoutDocumentPane>
				</avalonDockLayout:LayoutPanel>

			</avalonDockLayout:LayoutPanel>
			
		</avalonDockLayout:LayoutRoot.RootPanel>
	</avalonDockLayout:LayoutRoot>


 

 

 

Jun 14, 2012 at 1:26 PM

I've got same problem... It would be great to see any comments from AD developers

Jun 14, 2012 at 5:19 PM
Edited Jun 14, 2012 at 8:12 PM

I ran into the same problem and still haven't herad from AD team. Posted a very similar question. Here is the temporary(ugly) workaround that I found out based on the sample project in 2.0:

  1. Create a ViewModel for each or your new panels inheriting from ToolViewModel and create their corresponding properties into the Workspace class which I renamed to MainWindowViewModel.cs
    Make sure to specify a ContentId to each ViewModel. 
  2. Add each new ViewModel property to your IEnumerable<ToolViewModel> collection in the Workspace class
  3. Modify the MainWindow.xaml.cs.OnLoadLayout() Method to set the Content of your new ViewModels
  4. Run the application, all your new panels will appear tabbed in the left-side panel. Drag each tab and dock it wherever you want it to be
  5. Save the Layout to file (AvalonDock.Layout.config)
  6. Next time you run the app load the layout file.

Here is my new OnLoadLayout:

 

public void OnLoadLayout()
        {
            var layoutSerializer = new XmlLayoutSerializer(dockManager);
            layoutSerializer.LayoutSerializationCallback += (s, e) =>
                                                                {
                                                                    if (e.Model.ContentId ==
                                                                        FileStatsViewModel.ToolContentId)
                                                                        e.Content = MainWindowViewModel.This.FileStats;
                                                                    if (e.Model.ContentId ==
                                                                        SolutionExplorerViewModel.ToolContentId)
                                                                        e.Content =
                                                                            MainWindowViewModel.This.SolutionExplorer;
                                                                    if (e.Model.ContentId ==
                                                                        ToolboxViewModel.ToolContentId)
                                                                        e.Content = MainWindowViewModel.This.Toolbox;
                                                                    if (e.Model.ContentId ==
                                                                        PropertiesViewModel.ToolContentId)
                                                                        e.Content = MainWindowViewModel.This.Properties;
                                                                    if (e.Model.ContentId ==
                                                                        MessagesViewModel.ToolContentId)
                                                                        e.Content = MainWindowViewModel.This.Messages;
                                                                    else if (
                                                                        !string.IsNullOrWhiteSpace(e.Model.ContentId) &&
                                                                        File.Exists(e.Model.ContentId))
                                                                        e.Content =
                                                                            MainWindowViewModel.This.Open(
                                                                                e.Model.ContentId);
                                                                };
            layoutSerializer.Deserialize(@".\AvalonDock.Layout.config");
        }

 

One of the drawbacks with this solution is that you must have a Layout config file before hand, the other
big problem is that the Deserialization takes about 15 seconds in my environment! A long startup time
won't be well taken by my users... 

I still need to try Enhakiel's solution.

Jun 14, 2012 at 8:27 PM
Edited Jun 14, 2012 at 8:27 PM

You can see the solution in the MVVTestApp distributed with the source code. You can make your layout with the LayoutAnchorablePane or LayoutDocumentPane and identify them for example with the property Name

 

<avalonDock:LayoutRoot>
	<avalonDock:LayoutPanel Orientation="Vertical">
  		<avalonDock:LayoutDocumentPane/>
		<avalonDock:LayoutAnchorablePane Name="ToolsPane1" DockWidth="150"/>
		<avalonDock:LayoutAnchorablePane Name="ToolsPane2" DockWidth="150"/>
	</avalonDock:LayoutPanel>
</avalonDock:LayoutRoot>

 

then you have to implement an object derived from ILayoutUpdateStrategy, so when the AvalonDock load the collection of view model binded to the AnchorablesSource AvalonDock call the methods of the interface ILayoutUpdateStrategy BeforeInsertAnchorable(...) and InsertAnchorable(...) where you can search for the LayoutAnchorablePane where you want to add your ViewModel

 

    class LayoutInitializer : ILayoutUpdateStrategy
    {
        public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
        {
            string destPaneName = string.Empty;
            if (anchorableToShow is MyViewModel1)
                destPaneName = "ToolsPane1";
            else if (anchorableToShow is MyViewModel2)
                destPaneName = "ToolsPane2";
            else
                return false;

            var toolsPane = layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(d => d.Name == destPaneName);
            if (toolsPane != null)
            {
                toolsPane.Children.Add(anchorableToShow);
                return true;
            }

            return false;
        }

        public bool InsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
        {
            return false;
        }
    }

For doing this you must have only one collection of objects as the sums of your view model, and you have to provide the Template for each view model.

Jun 14, 2012 at 9:49 PM

Thanks @mach22 I'm almost getting it working it how I want with what you said.  I had to change the LayoutInitializer.BeforeInsertAnchorable method to
 

string destPaneName = string.Empty;
            if (anchorableToShow.Content is MyViewModel1)
                destPaneName = "ToolsPane1";
            else if (anchorableToShow.Content is MyViewModel2)
                destPaneName = "ToolsPane2";
            else
                return false;

And in my Code-behind I set the LayoutInitializer as follows:
dockManager.LayoutUpdateStrategy = new LayoutInitializer();

Now, I would like to add a third horizontal panel like the Output Window in Visual Studio. 
How can I accomplish that?

Thanks again for your help 

Jun 14, 2012 at 11:27 PM

you could create the LayoutInitializer in the xaml file, leaving the code behind clearer as possible

 <avalonDock:DockingManager.LayoutUpdateStrategy>
   <local:LayoutInitializer/>
  </avalonDock:DockingManager.LayoutUpdateStrategy>

If you want to add other LayoutAnchorablePanes it's just the same, you have to create the layout in your xaml file, adding all the panes that you need and then in the LayoutInitializer you must write your strategy to distribute the content of your viewmodels in the panes.

I suggest to try debugging the MVVM sample provided with the source code, it's clear and it would resolve your problems.

Jun 15, 2012 at 5:40 AM

It works now, thanks!

Jun 16, 2012 at 10:26 PM

netstep, would you mind sharing your solution? I've been working on the same problem for WAY TOO LONG. I've come from Infragistics, and quite confused with AvalonDock.

Jun 18, 2012 at 5:24 AM

To michaeldjackson: Sure, but it's just a compilation of three previous posts. would you like, I post my code here, or send you email with the project attached (need e-mail in this case)?

 

Jul 2, 2012 at 11:24 AM
Edited Jul 2, 2012 at 12:50 PM

how do i get an anchorable to dock as a document? 

and how does this work if the anchorable gets added after the user moved another panel around, because that seems to be removing the empty layout panes.

At start the application has only a document. As the user opens up tools, they show up, but always at the default location, which is currently "docked to the right".
I want to be able to specify in each ViewModel where they should show up by default. 

But by the time the user opens up a tool, the default layout specified in XAML has been garbage collected and the names are lost, so the method above doesn't really work anymore..

Jul 3, 2012 at 8:16 PM

To dock an anchorable as document you must put in on an LayoutDocumentPane in your LayoutUpdateStrategy.

To specify the location of the new viewmodels you have to implement the code for this in your LayoutUpdateStrategy as shown above.

Jul 5, 2012 at 8:50 AM

Well, the code above uses the Name of the panels to decide to put them there or not. But those panels get removed when they not populated. So if later in the application's lifetime new viewmodels arrive, there's no way to place them anymore.

Jul 19, 2012 at 8:46 AM

Hi Guys,

 

I'm clearly really stupid or have a different dll (I'm trying to use the 2.0 beta drop) because my interface looks like this 

 public bool BeforeInsertAnchorable(LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)        

and not like 

public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
and also there is no Name property in my LayoutAnchorablePane...(?) xaml editor marks it as error as does this line
 var toolsPane = layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(d => d.Name == destPaneName);

Also sadly I have tried putting the dockManager.LayoutUpdateStrategy = new LayoutInitializer();
in page behind and in xaml but not going into the method any time.
@nestep - would love you to post your example! 

 

Any help as always much appreciated!

Jul 19, 2012 at 12:50 PM

OK, I have the correct version of the dll now :-) BUT it is not going into the class LayoutInitializer... :-(

 

It does perfectly when i try with the example app. 

I am using prism with regions, But i am still expecting it to go into the layoutinitializer...I have 

 

 <avalonDock:DockingManager.LayoutUpdateStrategy>   

            <local:LayoutInitializer/>     

      </avalonDock:DockingManager.LayoutUpdateStrategy>

 

exactly as in the example and have also tried in class behind. Nothing doing. :-(

 

As always any ideas welcome...! 

Jul 19, 2012 at 2:07 PM

OK I have solved this. 

<avalonDock:DockingManager.LayoutUpdateStrategy>   

            <local:LayoutInitializer/>     

      </avalonDock:DockingManager.LayoutUpdateStrategy>

only works for the anchorable. Is there any way to do it with documentpanes?

 

Oct 24, 2012 at 2:51 PM

Has anyone figured this out yet?

The question:

How to assign a LayoutDocument to a certain LayoutDocumentPane?

Example:
======

<ad:DockingManager DocumentsSource="{Binding Path=Docs}">
<!--
DataTemplates for each ViewModel are defined here
-->
<ad:LayoutRoot>
   <ad:LayoutPanel Orientation="Horizontal">
      <ad:LayoutDocumentPaneGroup>
         <ad:LayoutDocumentPane x:Name="Docs1" />
         <ad:LayoutDocumentPane x:Name="Docs2" />
      </ad:LayoutDocumentPaneGroup>
   </ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>

 

I want some elements from the bound "Docs" collection to appear in the pane "Docs1" and the others in "Docs2".

How can I accomplish this?

Oct 24, 2012 at 6:43 PM

you have to implemento your initializer class derived from  ILayoutUpdateStrategy and in this class AvalonDock pass you the ViewModel of the document befor adding and you can manage its location in one Pane or another.

You can see the MVVM sample (AvalonDock.MVVMTestApp) provided with the source code.

Oct 25, 2012 at 10:01 AM

Hello mach22,

thanks, your answers are very helpful.
However, the ILayoutUpdateStrategy interface only deals with LayoutAnchorables.
Also, the LayoutUpdateStrategy property of the DockingManager only accepts ILayoutUpdateStrategy objects.

Or is there a way to implement ILayoutUpdateStrategy so that I am able to deal with LayoutDocuments?

At the moment I really don't see it.

Oct 25, 2012 at 6:41 PM

The ILayoutUpdateStrategy has 4 methods 2 for the Anchorables and 2 for the Documents:

BeforeInsertAnchorable, AfterInsertAnchorable and BeforeInsertDocument, AfterInsertDocument.

the only problem is that the LayoutDocumentPane hasn't the property Name like the LayoutAnchorablePane, for this I'll submit an issue

Oct 26, 2012 at 9:10 AM

Oh great!
I wasn't paying attention to the source code, I was using the original .dll from August 3rd all the time.

Thank you very much.

Oct 30, 2012 at 4:46 PM
Edited Oct 30, 2012 at 4:47 PM

Hello mach22,

sorry to bother you again, but using the latest AvalonDock source code I have now the following problem with the MVVMTestApp:

As soon as I drag the last LayoutDocument from the LayoutDocumentPane into a LayoutDocumentFloatingWindow, the navigation icons no longer appear if I want to put the floating window back into the pane.

This is what I did:

1. Start MVVMTestApp using latest AvalonDock source code
2. Create a new document using the New menu command
3. Drag the document from the pane into a floating window
4. Attempt to drag the floating window back into the pane -> I can't because the navigation icons no longer appear

For now, I didn't do anything specific in the LayoutInitializer class:

public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument documentToShow, ILayoutContainer destinationContainer)
{
return false;
}

public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown)
{
}
Any hints what I am doing wrong?
Oct 30, 2012 at 8:26 PM

It's clearly a bug, you could open an issue reporting all the step to reproduce the problem.

Oct 31, 2012 at 9:33 AM

OK, done.

Maybe I can be more helpful by taking a look at the source code myself at a later time, but at the moment I am still getting started with AvalonDock.

Nov 7, 2012 at 9:49 AM

adospace has fixed this issue in the latest build, and I can confirm that it works :)

Thanks a lot.

Nov 7, 2012 at 11:05 PM

Still remain the problem that the LayoutDocumentPane has non Name property like LayoutAnchorablePane used in the ILayoutUpdateStrategy.

As I have reported in the issue:

http://avalondock.codeplex.com/workitem/15737