Click here to Skip to main content
11,640,445 members (61,407 online)
Click here to Skip to main content

AvalonDock [2.0] Tutorial Part 1 - Adding a Tool Window

, 31 Jan 2014 CPOL 77.8K 5.2K 103
Rate this:
Please Sign up or sign in to vote.
How to create a new tool window in AvalonDock [2.0]

Introduction   

"AvalonDock is a WPF controls library which can be used to create a docking layout system like that is present in Visual Studio. It supports fly-out panes, floating windows, multiple docking manager in same window, styles and themes and it can host WinForms controls."  (citation from http://avalondock.codeplex.com/)

AvalonDock is a very complete and stable open source project for .NET/WPF that offers this functionality and has been in the making for half a decade now. The current solution is a step forward as it was build from scratch, supports MVVM, is themable, and can be localized.

AvalonDock was written by Adolfo Marinucci. I am in no way involved with the development of AvalonDock but felt that it was time to document some essential things. I am not sure whether I got everything completely right (since I am not the primary developer) but I am building on some experience and simply hope people will give me feedback here if I am making claims that are false.

The first two parts of the tutorial are covering initial preparation steps and adding a Most Recent Files (MRU) tool window and a Start Page. There may be more parts on other AvalonDock subjects (theming, localization, AvalonEdit in AvalonDock 2.0) later. Those who cannot wait for later articles shall look at Edi (http://edi.codeplex.com) to see some of the things before I document them here. This article was in fact written with Edi.

Solution Preparation

  • Download AvalonDock sources from CodePlex (I used the end of 2012 build 98289): http://avalondock.codeplex.com/SourceControl/list/changesets.
  • Find the version 2.0 Sub-folder and remove all other sub-folders.
  • Review the solution in the version 2.0 sub-folder and remove all projects except for AvalonDock.MVVMTestApp and AvalonDock.

    I ended up removing the sub-projects:

    • AvalonDock.TestApp.csproj
    • AvalonDock.WinFormsTestApp.csproj
    • AvalonDock.Theme.VS2010 (theming resources library)
    • AvalonDock.Themes.Aero (theming resources library)
    • ... and other theming projects.

    and was left with:

    • AvalonDock (docking layout system library)
    • AvalonDock.MVVMTestApp (Test apllication to be extended in this article)
  • Add a new WPF Application project with the application name that you want to develop based on AvalonDock (mine is Edi) and copy files from AvalonDock.MVVMTestApp into the new project. I also resorted some items into folders and namespaces as you can see if you review Version_01_Edi.zip. Your solution should look like this:

Let's get down to business

Open the solution and set the Edi project as Startup Project in Visual Studio. Hit the F5 key to check that the application runs OK. I have seen the following content in my case:

 

The initial test application framework consists (in my case) of an empty document panel and one tool window at the bottom. Play around with the menu and tool window to get familiar with the functionality that is already there. We are going to extend this functionality with a Start Page and a second Tool Window (both are mainly displaying a recent files list).

Add the Recent Files List Control

I have documented a way of developing (look-less) controls through composition and theming in an earlier article series (http://www.codeproject.com/Articles/332541/WPFControlCompositionPart2of2). An extended version of this library contains a listview which can hold hyperlinks which in turn are customized such that:

The parts that implement the above requirements are:

Now I am not going too much into details on these items because I really want to focus on AvalonDock [2.0]. Please read the referenced articles if you really want to know more about the MRU implementation. And if that does not help, feel free to ask a question below the article.

So back to our AvalonDock integration project. Download and extract the Version_02_Edi_RecentFilesTW.zip file. This should give you a working solution with a Recent Files tool window in it. Just execute File>Open a few times to generate some hyperlink entries in the tool window and take a note of the extra MRU entries in the file sub-menu.

How does this solution work and what is necessary to get it? That is discussed in the remainder of this article.

The Most Recent Files (MRU) Tool Window (ViewModel)

I like to use RoutedUICommands whenever I can so I added the AppCommand class with a static constructor and the following commands:

LoadFile

PinUnpin
AddMruEntry
RemoveMruEntry

The LoadFile command is executed when a user opens a file. It is necessary to open a file when a user clicks on an entry in the recent files list. The PinUnpin command reverses the state of a pin in the MRU. And the other two commands AddMruEntry and RemoveMruEntry are executed to add or remove an entry. You can determine their function by following their references from the AppCommand class into the Workspace class (The Workspace class is similar to what others often call an ApplicationViewModel or MainViewModel. It is the root of all ViewModels in this project):  

public void InitCommandBinding(Window win)
{
  win.CommandBindings.Add(new CommandBinding(AppCommand.LoadFile,
  (s, e) =>
  {
    if (e == null)
      return;

    string filename = e.Parameter as string;

    if (filename == null)
      return;

    this.Open(filename);
  }));

  win.CommandBindings.Add(new CommandBinding(AppCommand.PinUnpin,
  (s, e) =>
  {
    this.PinCommand_Executed(e.Parameter, e);
  }));

  win.CommandBindings.Add(new CommandBinding(AppCommand.RemoveMruEntry,
  (s, e) =>
  {
    this.RemoveMRUEntry_Executed(e.Parameter, e);
  }));

  win.CommandBindings.Add(new CommandBinding(AppCommand.AddMruEntry,
  (s, e) =>
  {
    this.AddMRUEntry_Executed(e.Parameter, e);
  }));
}

Each MRU command executes the corresponding Executed method and the LoadFile command executes the Open() method in the Workspace class. Furthermore, the MRU is realized in a Workspace property called RecentFiles. It is this property whos data is visible in the GUI. But how does it get there and what is necessary to extend AvalonDock [2.0] to show it? Thats what we discuss next.

The Most Recent Files Tool Window (View)

Merging the versions contained in V01_Edi.zip and V02_Edi_RecentFilesTW.zip shows that I added a RecentFilesView in the View namespace. This is the view that AvalonDock [2.0] will show inside its tool window docking controls. It is bound to the RecentFilesViewModel whos property is located in the Workspace class.  

The View

AvalonDock uses a DataTemplateSelector (View.Pane.PanesTemplateSelector) to determine the appropriate view whenever it sees a data item that needs a display. I extended this class by the RecentFilesViewTemplate property:

public DataTemplate RecentFilesViewTemplate
{
  get;
  set;
}

...and the corresponding statement in the SelectTemplate method:

if (item is RecentFilesViewModel)
  return RecentFilesViewTemplate;

This code works hand in hand with this view extension in the MainWindow.xaml file:

<pane:panestemplateselector.recentfilesviewtemplate>
<datatemplate>
  <view:recentfilesview>
</view:recentfilesview>
</pane:panestemplateselector.recentfilesviewtemplate>

The Style

A style PanesStyleSelector controls how fields in the viewmodel are mapped into AvalonDock's primary functions (such as, display a name of an item, or the command to execute for closing etc). Again we can extend the View.Pane.PanesStyleSelector class with an additional property:

public Style RecentFilesStyle
{
  get;
  set;
}

...and the corresponding logic in the SelectStyle method:

if (item is RecentFilesViewModel)
  return RecentFilesStyle;

...which works hand in hand with this XAML in MainWindow.xaml:

<pane:panesstyleselector.recentfilesstyle>
<style targettype="{x:Type avalonDock:LayoutItem}">
  <Setter Property="Title" Value="{Binding Model.Title}"/>
  <Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
  <Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>

  <Setter Property="ToolTip" Value="{Binding Model.FilePath}"/>
  <Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"/>
  <Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
</style>
</pane:panesstyleselector.recentfilesstyle> 

The very long line on the Visibility property (with the converter) is crucial to get the menu entry Tools>RecentFiles to work. The preferred method for this is a command binding but we just set a Boolean property here (for the sake of simplicity). The Boolean property IsVisible is located in the derived class of the RecentFilesViewModel. That is, it is present in RecentFilesViewModel because it inherits it from the Base.ToolViewModel class.

The BoolToVisibilityConverter does the conversion between the Boolean and the Visibility property. This conversion is required since Boolean has 2 states (true, false) while Visibility has 3 (Visible, Hidden, Collapsed) states in WPF. Read, for example, the article referenced in [3] if you want know more about converters and their application in WPF.

The Recent Files Menu Entry

The Recent Files menu entry at File>Recent Files is defined by this XAML code (taken from MainWindow.xaml):

<MenuItem ItemsSource="{Binding RecentFiles.MruList.ListOfMRUEntries}" Grid.Row="0" Header="Recent Files"
           Visibility="{Binding Path=RecentFiles.MruList.ListOfMRUEntries, Mode=OneWay, Converter={conv:ZeroToVisibilityConverter}}">
  <MenuItem.ItemContainerStyle>
    <Style TargetType="MenuItem">
      <Setter Property="Header" Value="{Binding DisplayPathFileName, Mode=OneWay}" />
      <Setter Property="Command" Value="cmd:AppCommand.LoadFile" />
      <Setter Property="CommandParameter" Value="{Binding PathFileName, Mode=OneWay}" />
    </Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

Here you can see at the ItemsSource binding that each entry is taken directly from RecentFiles.MruList.ListOfMRUEntries. Where RecentFiles is a property of the Workspace class and the MruList is a property of the RecentFilesViewModel viewmodel. So, the source of the menu item collection is really managed the MRUListVM class.

The command that is executed when a user clicks a recent files menu item is the LoadFile command which is implemented as a RoutedUICommand in the AppCommand class. But the AppCommand class provides just the commanding facility, the actual logic is implemented in the InitCommandBinding method in the Workspace class (see code listing on LoadFile further up in this article).

Customizing AvalonDock 2.0   

Here is one of the many things that make AvalonDock such a cool control. Users are actually able to drag items out of the main window (for example a document on to a second monitor) and keep working with that. I have often seen people asking how can I configure my application to decide whether someone can actually drag an item off or not.

Add the following setting

<Setter Property="CanFloat" Value="False" /> 

into the style of an AvalonDock LayoutItem to disable the fly out functionality. You could even bind this property in your viewmodel to make this setting configurable at run-time.

Summary

...and there you have it. That's mainly all that is necessary to get a new tool window in AvalonDock. Lets give Adolfo Marinucci a big applause because I doubt that anyone could do something so complex in such a simple way.

We finally have separation of concerns between view and viewmodel and there is a simple pattern to follow. Lets not forgot that docking and fly out functionality that AvalonDock offers out of the box. Now who thought that MVVM is too complex and too difficult to implement? Think again.

In the next article we'll be looking at creating a new document - do not forget to give me your feedback on this article and go on to the next stage [1] whenever you feel like learning to add a start page to your application.

References

History 

  • 24.20.2012 Initial creation.  
  • 01.03.2013 Updated AvalonDock version and simplified article and source code.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dirkster99
Germany Germany
The Windows Presentation Foundation (WPF) and C# are one of my favorite past time hobbies since I started developing Edi:

https://edi.codeplex.com/

and a few other sub-projects on Codeplex. I am normally an algorithms and structure type person but WPF has such interesting sides that I cannot help myself but get into this and MVVM.

You may also be interested in...

Comments and Discussions

 
Questionavalondock document instantiate on docking out Pin
meOneKey14-Apr-15 2:20
professionalmeOneKey14-Apr-15 2:20 
AnswerRe: avalondock document instantiate on docking out Pin
Dirkster9915-Apr-15 22:17
memberDirkster9915-Apr-15 22:17 
GeneralRe: avalondock document instantiate on docking out Pin
meOneKey16-Apr-15 7:50
professionalmeOneKey16-Apr-15 7:50 
GeneralRe: avalondock document instantiate on docking out Pin
Dirkster9920-Apr-15 6:29
memberDirkster9920-Apr-15 6:29 
GeneralRe: avalondock document instantiate on docking out Pin
meOneKey20-Apr-15 11:06
professionalmeOneKey20-Apr-15 11:06 
QuestionI would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey12-Nov-14 4:31
professionalmeOneKey12-Nov-14 4:31 
AnswerRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9912-Nov-14 6:12
memberDirkster9912-Nov-14 6:12 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey13-Nov-14 1:30
professionalmeOneKey13-Nov-14 1:30 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9913-Nov-14 6:38
memberDirkster9913-Nov-14 6:38 
SuggestionRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey14-Nov-14 0:56
professionalmeOneKey14-Nov-14 0:56 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9914-Nov-14 4:57
memberDirkster9914-Nov-14 4:57 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey17-Nov-14 3:03
professionalmeOneKey17-Nov-14 3:03 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9917-Nov-14 8:50
memberDirkster9917-Nov-14 8:50 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey17-Nov-14 22:17
professionalmeOneKey17-Nov-14 22:17 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9919-Nov-14 5:09
memberDirkster9919-Nov-14 5:09 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey19-Nov-14 21:44
professionalmeOneKey19-Nov-14 21:44 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
Dirkster9920-Nov-14 3:48
memberDirkster9920-Nov-14 3:48 
GeneralRe: I would like to know how to add loading avalonDock layout functionality from file based on Edi Pin
meOneKey20-Nov-14 20:15
professionalmeOneKey20-Nov-14 20:15 
Questionhave you ever met airspace WindowsFormsHost issue with WPF and avalondock Pin
meOneKey12-Nov-14 1:14
professionalmeOneKey12-Nov-14 1:14 
AnswerRe: have you ever met airspace WindowsFormsHost issue with WPF and avalondock Pin
Dirkster9912-Nov-14 6:13
memberDirkster9912-Nov-14 6:13 
GeneralRe: have you ever met airspace WindowsFormsHost issue with WPF and avalondock Pin
meOneKey12-Nov-14 6:41
professionalmeOneKey12-Nov-14 6:41 
QuestionStyles and PanesStyleSelector Pin
fre_ber18-Dec-13 0:00
memberfre_ber18-Dec-13 0:00 
AnswerRe: Styles and PanesStyleSelector Pin
Dirkster9918-Dec-13 5:22
memberDirkster9918-Dec-13 5:22 
GeneralRe: Styles and PanesStyleSelector Pin
fre_ber18-Dec-13 9:40
memberfre_ber18-Dec-13 9:40 
GeneralRe: Styles and PanesStyleSelector Pin
Dirkster9919-Dec-13 1:28
memberDirkster9919-Dec-13 1:28 
BugThe MenuItem “Recentfiles” has no response Pin
Member 103782295-Nov-13 0:53
memberMember 103782295-Nov-13 0:53 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
Dirkster995-Nov-13 1:55
memberDirkster995-Nov-13 1:55 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
tcsnhj5-Nov-13 2:09
membertcsnhj5-Nov-13 2:09 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
Dirkster995-Nov-13 4:51
memberDirkster995-Nov-13 4:51 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
tcsnhj5-Nov-13 11:59
membertcsnhj5-Nov-13 11:59 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
tcsnhj5-Nov-13 13:44
membertcsnhj5-Nov-13 13:44 
GeneralRe: The MenuItem “Recentfiles” has no response Pin
Dirkster996-Nov-13 0:50
memberDirkster996-Nov-13 0:50 
GeneralMy vote of 5 Pin
leiyangge14-Jul-13 14:33
memberleiyangge14-Jul-13 14:33 
GeneralRe: My vote of 5 Pin
Dirkster9914-Jul-13 21:58
memberDirkster9914-Jul-13 21:58 
BugBug in PanesStyleSelector class. Pin
koleraba2-Apr-13 10:02
memberkoleraba2-Apr-13 10:02 
GeneralRe: Bug in PanesStyleSelector class. Pin
Dirkster9929-Apr-13 8:54
memberDirkster9929-Apr-13 8:54 
GeneralRe: Bug in PanesStyleSelector class. Pin
koleraba29-Apr-13 10:47
memberkoleraba29-Apr-13 10:47 
SuggestionGreat article. Pin
koleraba2-Apr-13 3:08
memberkoleraba2-Apr-13 3:08 
GeneralRe: Great article. Pin
Dirkster992-Apr-13 9:13
memberDirkster992-Apr-13 9:13 
QuestionDock as tabbed document issue Pin
sharethl1-Apr-13 4:14
membersharethl1-Apr-13 4:14 
AnswerRe: Dock as tabbed document issue Pin
Dirkster991-Apr-13 5:31
memberDirkster991-Apr-13 5:31 
GeneralRe: Dock as tabbed document issue Pin
sharethl1-Apr-13 6:12
membersharethl1-Apr-13 6:12 
GeneralRe: Dock as tabbed document issue Pin
Dirkster991-Apr-13 10:43
memberDirkster991-Apr-13 10:43 
Bug[Version 2.0.1771] Bug? RightToLeft problem Pin
ARIA 518-Jan-13 19:10
memberARIA 518-Jan-13 19:10 
GeneralRe: [Version 2.0.1771] Bug? RightToLeft problem Pin
Dirkster9928-Feb-13 10:50
memberDirkster9928-Feb-13 10:50 
GeneralRe: [Version 2.0.1771] Bug? RightToLeft problem Pin
ARIA 51-Mar-13 21:30
memberARIA 51-Mar-13 21:30 
GeneralRe: [Version 2.0.1771] Bug? RightToLeft problem Pin
Dirkster995-Mar-13 10:54
memberDirkster995-Mar-13 10:54 
QuestionSome clarification Pin
Thornik12-Nov-12 23:50
memberThornik12-Nov-12 23:50 
AnswerRe: Some clarification Pin
Dirkster9919-Nov-12 1:35
memberDirkster9919-Nov-12 1:35 
SuggestionThe Introduction Pin
Jusitn_H25-Oct-12 5:09
memberJusitn_H25-Oct-12 5:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150731.1 | Last Updated 31 Jan 2014
Article Copyright 2012 by Dirkster99
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid