手順1 プロジェクトの作成
- 「WpfApplicationSubWindow1」という名前のWPFアプリケーションを作成します。
- カスタムコントロールを追加します。
ソリューションエクスプローラ内の[WpfApplicationSubWindow1]プロジェクトを右クリックし、[追加]-[新しい項目]を選択して、[新しい項目の追加]ダイアログボックスを表示します。テンプレートで[ユーザーコントロール(WPF)]を選択し、ファイル名を「SubWindows」にして追加します。「SubWindows.cs」と同時に、「Themes」フォルダ内に「Generic.xaml」が生成されます。コントロールの外観はこの「Generic.xaml」内で記述します。
- ツールウィンドウ内に表示するためのページを作っておきます。
先程と同様に[新しい項目の追加]ダイアログボックスを開き、テンプレートで[ページ(WPF)]を選択し、「Page1.xaml」という名前で追加します。ページの内容は適当に編集しておいてください。
- 表示用のアイコンを追加します。
プロジェクトの「Image」フォルダ内に「arrow.PNG」「btnAutoHide.PNG」「btnClose.PNG」「btnMenu.PNG」を追加します。最初のアイコンは、左右下のタブの表示に使います。後の3つのアイコンは、ツールウィンドウのタイトルバー右端に表示されます。アイコンファイルは適当に用意してください。
- 最後に、「TitleAndPage.cs」を作成します。
ソリューションエクスプローラ内のプロジェクトを右クリックして、[追加]-[クラス]を選択し、ファイル名を「TitleAndPage.cs」にして追加します。
手順2 TitleAndPageクラス、TitleAndPagesクラスの定義
先述したように、TitleAndPageはツールウィンドウの各情報を格納するためのクラスです。TitleAndPagesはTitleAndPageのリストになります。
public class TitleAndPage { public TitleAndPage() { } public string Title { get; set; } public string PageUri { get; set; } public string IconUri { get; set; } } public class TitleAndPages : List<TitleAndPage> { public TitleAndPages() : base() { } }
手順3 SubWindowsにプロパティを追加する
「SubWindows.cs」を開いてください。カスタムコントロールのC#コードは、このファイルに記述していきます。
長いコメントの次の行にあるクラス定義を見てください。デフォルトではSubWindowsがControlから派生しているはずです。このControlをContentControlに書き換えます。すると、このコントロールはContentプロパティを持つようになり、このコントロールの下の階層に置かれた要素は自動的にコンテンツとして扱われます。
次にSubWindowsに3つのプロパティ、LeftContents、RightContents、BottomContentsを追加します。それぞれ、左右下のツールウィンドウのリストを格納することになるので、型はList<TitleAndPage>を継承しているTitleAndPagesになります。スタイル、バインディングなどのWPFの機能を利用できるようにするため、普通のCLRプロパティではなく、依存関係プロパティとして定義します。LeftContentsの定義は次のようになります。
public static readonly DependencyProperty LeftContentsProperty = DependencyProperty.Register("LeftContents", typeof(TitleAndPages), typeof(SubWindows), new PropertyMetadata(new TitleAndPages() { })); public TitleAndPages LeftContents { get { return (TitleAndPages)GetValue(LeftContentsProperty); } set { SetValue(LeftContentsProperty, value); } }
依存関係プロパティは、通常DependencyPropertyクラスと“ラッパー”と呼ばれるプロパティを組にして使います。DependencyPropertyは依存関係プロパティの本体で、通常は静的で読み取り専用の公開フィールドとして定義します。
登録時は、DependencyPropertyの静的メソッドであるRegisterメソッドを呼び出します。呼び出し時には、プロパティ名、プロパティの型、プロパティを定義するクラスの型を順に渡します。その後に続けて、プロパティのメタデータを渡すことも可能で、リスト3ではメタデータとしてプロパティの既定値(空のリスト)を渡しています(注)。
ラッパーは、外部から依存関係プロパティを参照/設定する時に、通常のプロパティであるかのように扱えるようにするもので、getアクセサ内でGetValueメソッド、setアクセサ内でSetValueメソッドを呼び出します。
RightContents、BottomContentsについては、LeftContentsとほぼ同様なので省略します。ダウンロードしたプロジェクトファイル内の「SubWindows.cs」を参照してください。
nullとなるため、NullReferenceExceptionが発生してしまいます。手順4 コントロールの外観設定1 ~大きな構成要素を見る~
使用するクラスとプロパティの定義が終わったので、いよいよコントロールの外観を設定していきます。しかし、XAMLコードで200行以上あって見難いため、まず大まかな全体像を説明した後、細かい部分を説明していきます。コントロールの外観設定は、「Themes」フォルダの「Generic.xaml」で行います。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplicationSubWindow1"> <Style TargetType="{x:Type local:SubWindows}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:SubWindows}"> <DockPanel> <ItemsControl Name="LeftTab" DockPanel.Dock="Left" ItemsSource="{TemplateBinding LeftContents}"> ・・・ </ItemsControl> <ItemsControl Name="RightTab" DockPanel.Dock="Right" ItemsSource="{TemplateBinding RightContents}"> ・・・ </ItemsControl> <ItemsControl Name="BottomTab" DockPanel.Dock="Bottom" ItemsSource="{TemplateBinding BottomContents}"> ・・・ </ItemsControl> <Grid Name="MainContent"> <ContentPresenter/> </Grid> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
コントロールの外観を最初から作成していく場合は、Templateプロパティに外観を書き加えていきます。
<Style TargetType="{x:Type local:SubWindows}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:SubWindows}">
テンプレートには、コントロールの外観を作成するControlTemplateクラスと、データの表示方法を作成するDataTemplateクラスがありますが、この場合はコントロールの外観を作成するのでControlTemplateになります。
ControlTemplate内はDockPanelによって左右下、そして中央の4つの部分に分割されています。
<DockPanel> <ItemsControl Name="LeftTab" DockPanel.Dock="Left" ItemsSource="{TemplateBinding LeftContents}"> ・・・ </ItemsControl> <ItemsControl Name="RightTab" DockPanel.Dock="Right" ItemsSource="{TemplateBinding RightContents}"> ・・・ </ItemsControl> <ItemsControl Name="BottomTab" DockPanel.Dock="Bottom" ItemsSource="{TemplateBinding BottomContents}"> ・・・ </ItemsControl> <Grid Name="MainContent"> <ContentPresenter/> </Grid>
そして、左右下の領域にはそれぞれItemsControlが、中央にはGridが配置されています。左右下のItemsControlには、それぞれLeftContents、RightContents、BottomContentsがバインドされ、中央のGridにはContentPresenterが配置されています。
こうすることによって、左右下の領域にそれぞれのツールウィンドウ(とタブ)が、また、中央にコントロールのContentプロパティの内容が表示されることになります。
