WPFでドッキングウィンドウ(AvalonDock) 使い方 その1 - 導入

半年くらい前にアプリ開発の仕事で使ったのですが、最近そのアプリをメンテしたときにコレの使い方を忘れすぎてたのでメモです。

WPF でドッキングウィンドウ

ドッキングウィンドウとは、VisualStudio をはじめとした各IDEではお馴染みの
ウィンドウをくっつけたりしてレイアウトを自由に調整できる機能です。
AvalonDock は WPF アプリでドッキングウィンドウを実現するライブラリです。

Form アプリでもそうでしたが WPF でも標準では用意されていないので、
外部からライブラリ等をお借りしてくることになります。

WPF用のドッキングライブラリは有償であればいくつかありますが、
・フリー (緩めOSSライセンス)
・.NET Framwork 4.5 対応
・MVVMサポート
という感じで探すと AvalonDock 一択なのかなと。

AvalonDock
http://avalondock.codeplex.com/

プロジェクトに AvalonDock をインストールする

プロジェクトサイトから直接DLLをダウンロードしてきてもいいのですが、
NuGet からの方が楽なのでそちらからインストールします。

今回は .NET4.5 で WPF アプリケーションプロジェクトを新規作成しました。

メニューの[ツール] > [ライブラリパッケージマネージャー] > [パッケージマネージャーコンソール] を選択。
表示されたウィンドウで次のコマンドを打ち込みます。

PM> Install-Package AvalonDock

とりあえずドッキングウィンドウを作る

ナニはともあれ、動くものを出してみたいと思います。

まずは XAML名前空間 を定義します。

xmlns:avalonDock="http://schemas.xceed.com/wpf/xaml/avalondock"

Window 要素の下に、おもむろに以下のコードを張り付けます。

<avalonDock:DockingManager x:Name="_dockingManager">
    <avalonDock:LayoutRoot>
        <avalonDock:LayoutPanel Orientation="Vertical">
            <avalonDock:LayoutPanel Orientation="Horizontal">
                <avalonDock:LayoutDocumentPane>
                    <!-- ドキュメント1 -->
                    <avalonDock:LayoutDocument Title="Document1">
                        <TextBox/>
                    </avalonDock:LayoutDocument>
                    <!-- ドキュメント2 -->
                    <avalonDock:LayoutDocument Title="Document2">
                        <TextBox/>
                    </avalonDock:LayoutDocument>
                </avalonDock:LayoutDocumentPane>
                <avalonDock:LayoutAnchorablePane DockWidth="150">
                    <!-- ツールウィンドウ1 -->
                    <avalonDock:LayoutAnchorable Title="ToolWindow1">
                        <StackPanel>
                            <Button Content="Button1" />
                            <Button Content="Button2" />
                        </StackPanel>
                    </avalonDock:LayoutAnchorable>
                </avalonDock:LayoutAnchorablePane>
            </avalonDock:LayoutPanel>
            <avalonDock:LayoutAnchorablePane DockHeight="100">
                <!-- ツールウィンドウ2 -->
                <avalonDock:LayoutAnchorable Title="ToolWindow2">
                    <TextBox/>
                </avalonDock:LayoutAnchorable>
                <!-- ツールウィンドウ3 -->
                <avalonDock:LayoutAnchorable Title="ToolWindow3">
                    <Button Content="Button3"/>
                </avalonDock:LayoutAnchorable>
            </avalonDock:LayoutAnchorablePane>
        </avalonDock:LayoutPanel>
    </avalonDock:LayoutRoot>
</avalonDock:DockingManager>

出ます。
f:id:lriki:20141201233613p:plain

ちょっといじってみる

原理理論よりも少し遊んでみたくなったので寄り道。

先ほどの例のままだと、一度閉じたウィンドウはもう開けなくなってしまうので、
MenuItem の IsCheckd にドッキングウィンドウの IsVisible をバインドして
表示・非表示を切り替えられるようにしてみました。
バインドソースは LayoutAnchorable.IsVisible です。

ついでに、ドキュメントウィンドウも閉じられないようにしています。

<Window x:Class="AvalonDockTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:avalonDock="http://schemas.xceed.com/wpf/xaml/avalondock"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Menu>
            <MenuItem Header="表示">
                <MenuItem 
                    Header="ToolWindow1"
                    IsCheckable="True"
                    IsChecked="{Binding IsVisible, ElementName=_toolWindow1}" />
            </MenuItem>
        </Menu>
        <avalonDock:DockingManager x:Name="_dockingManager" Grid.Row="1">
            <avalonDock:LayoutRoot>
                <avalonDock:LayoutPanel Orientation="Vertical">
                    <avalonDock:LayoutPanel Orientation="Horizontal">
                        <avalonDock:LayoutDocumentPane>
                            <!-- ドキュメント1 -->
                            <avalonDock:LayoutDocument Title="Document1" CanClose="False" CanFloat="False">
                                <TextBox/>
                            </avalonDock:LayoutDocument>
                        </avalonDock:LayoutDocumentPane>
                        <avalonDock:LayoutAnchorablePane DockWidth="150">
                            <!-- ツールウィンドウ1 -->
                            <avalonDock:LayoutAnchorable x:Name="_toolWindow1" Title="ToolWindow1">
                                <StackPanel>
                                    <Button Content="Button1" />
                                    <Button Content="Button2" />
                                </StackPanel>
                            </avalonDock:LayoutAnchorable>
                        </avalonDock:LayoutAnchorablePane>
                    </avalonDock:LayoutPanel>
                </avalonDock:LayoutPanel>
            </avalonDock:LayoutRoot>
        </avalonDock:DockingManager>
    </Grid>
</Window>

f:id:lriki:20141201234002p:plain
f:id:lriki:20141201234005p:plain

XAML だけでここまでできます。
簡単なアプリならこれでもう十分なんじゃないかな。

でも・・・

ホンキで MVVM に則って実装する場合、今回のように LayoutAnchorable を XAML にべた書きはしません。

ViewModel との関連付けや詳しい原理とかは次回以降で。
Selector や デフォルトレイアウトの定義、レイアウトの保存とかも
何回かの記事に分けて書いていきたいと思います。