前回の日記で報告しましたように、TabControlにタブを動的に追加するところまで出来たのですが、
いざTabを追加してみると、一度開いたTabが閉じられないことがどうしても気になってしまいます。
各ページに閉じるボタンを付けてそれがクリックされたらTabが閉じるようにすれば良いのですが、
なんとなくかっこよくない気がします。そこでいきなりですが、TabItemを拡張してTabのヘッダの横に「閉じる」ボタンを
付けて、それがクリックされたらTabが閉じるようにしてみました。WPFの理解がまだまだなのでかなり苦労しました。。
まず、ソリューションエクスプローラから「追加」→「新しい項目」→「カスタムコントロール(WPF)」の
順に選んで、新規のカスタムコントロールを追加します。
コントロールの名前は「TabPage」にしました。すると、「TagPage.cs」というファイルと
「Themes/Generic.xaml」というファイルが追加されます。
「Generic.xaml」はコントロールの外観を定義するためのXAMLファイルです。
「TagPage.cs」はコントロールの挙動を定義するC#のソースコードです。作成された時点では「Control」クラス
を継承しているので「TabItem」を継承するように変更します。
今回は、まず「Generic.xaml」を編集してTabのヘッダに閉じるボタンを追加するまでを紹介します。

WPFではコントロールを拡張する際、ControlTemplateを作成し、外観を定義するのですが、
何分初心者なので最初は何をどうしたら良いのかさっぱり分かりませんでした。そこで、TabItemの
ControlTemplateのサンプルをここから持ってきて、それをそのまま
「Generic.xaml」に貼って動作を見てみることにしました。
外観がちょっと物足りないけどTabのページとして、TabControlに追加することが出来ました。
ただし、Contentに設定したコントロールがFillされた状態で表示されなかったので、
以下の2つのプロパティの設定を追加しました。
これをベースに閉じるボタンを追加していきます。
閉じるボタンの画像として、Tabが選択されている場合と選択されていない場合に表示する
2つのものを用意しました。
テンプレートの例を見てみると、TabItemのツリー構成は以下のようになっています。(属性は省きます)
Gridの中にタブのヘッダを囲む枠に当たるBorderが配置されています。そしてその中に
TabItemのHeaderに設定されたUIElementの表示を担当するContentPresenterが置かれています。
これを見ると、なぜTabItemの「Content」のContentPresenterが無いのって思うかもしれません。
私も思いました。理由は分かりません。とにかくここに無くてもちゃんと表示されているので
今は気にしないことにします。
さて今回、閉じるボタンを追加するために、Borderの下にDockPanelを挟んで、DockPanelの右側に
閉じるボタンを配置し、その残りの部分にContentPresenterを配置します。
(一部属性は省略しています)
閉じるボタンのスタイルには、別途リソースの中に定義したものを適用しています。
スタイルはタブが選択されている場合のものと、タブが選択されていない場合のものを
用意し、それをトリガーを使って切り替えます。
以下がボタンのスタイルを定義するしている部分です。
ButtonのContentにはImageを配置しています。イメージファイルはプロジェクトフォルダの直下に
Imageというフォルダを作り、そこに追加します。WPFの場合、これだけでアセンブリに含まれるリソースとして
ビルドしてくれます。
画像の切り替えは、トリガを使って行います。上記で紹介しましたようにデフォルトの状態ではStyleに
「CloseDisableButton」を設定し、選択された場合、以下のようにトリガを使って、「CloseButton」に
切り替えています。
閉じるボタンが押された際に実際にTabを閉じ、イベントを発生させる必要があるのですが、
その部分は次回に紹介させていただきます。
本日のソース
いざTabを追加してみると、一度開いたTabが閉じられないことがどうしても気になってしまいます。
各ページに閉じるボタンを付けてそれがクリックされたらTabが閉じるようにすれば良いのですが、
なんとなくかっこよくない気がします。そこでいきなりですが、TabItemを拡張してTabのヘッダの横に「閉じる」ボタンを
付けて、それがクリックされたらTabが閉じるようにしてみました。WPFの理解がまだまだなのでかなり苦労しました。。
まず、ソリューションエクスプローラから「追加」→「新しい項目」→「カスタムコントロール(WPF)」の
順に選んで、新規のカスタムコントロールを追加します。
コントロールの名前は「TabPage」にしました。すると、「TagPage.cs」というファイルと
「Themes/Generic.xaml」というファイルが追加されます。
「Generic.xaml」はコントロールの外観を定義するためのXAMLファイルです。
「TagPage.cs」はコントロールの挙動を定義するC#のソースコードです。作成された時点では「Control」クラス
を継承しているので「TabItem」を継承するように変更します。
今回は、まず「Generic.xaml」を編集してTabのヘッダに閉じるボタンを追加するまでを紹介します。

WPFではコントロールを拡張する際、ControlTemplateを作成し、外観を定義するのですが、
何分初心者なので最初は何をどうしたら良いのかさっぱり分かりませんでした。そこで、TabItemの
ControlTemplateのサンプルをここから持ってきて、それをそのまま
「Generic.xaml」に貼って動作を見てみることにしました。
外観がちょっと物足りないけどTabのページとして、TabControlに追加することが出来ました。
ただし、Contentに設定したコントロールがFillされた状態で表示されなかったので、
以下の2つのプロパティの設定を追加しました。
<Setter Property="HorizontalContentAlignment" Value="Stretch"/> <Setter Property="VerticalContentAlignment" Value="Stretch"/>
これをベースに閉じるボタンを追加していきます。
閉じるボタンの画像として、Tabが選択されている場合と選択されていない場合に表示する
2つのものを用意しました。


テンプレートの例を見てみると、TabItemのツリー構成は以下のようになっています。(属性は省きます)
<Grid> <Border> <ContentPresenter ContentSource="Header"/> </Border> </Grid>
Gridの中にタブのヘッダを囲む枠に当たるBorderが配置されています。そしてその中に
TabItemのHeaderに設定されたUIElementの表示を担当するContentPresenterが置かれています。
これを見ると、なぜTabItemの「Content」のContentPresenterが無いのって思うかもしれません。
私も思いました。理由は分かりません。とにかくここに無くてもちゃんと表示されているので
今は気にしないことにします。
さて今回、閉じるボタンを追加するために、Borderの下にDockPanelを挟んで、DockPanelの右側に
閉じるボタンを配置し、その残りの部分にContentPresenterを配置します。
(一部属性は省略しています)
<Grid> <Border> <DockPanel x:Name="ContentPanel"> <Button x:Name="CloseButton" DockPanel.Dock="Right" Style="{StaticResource CloseDisableButton}"/> <ContentPresenter ContentSource="Header"/> </DockPanel> </Border> </Grid>
閉じるボタンのスタイルには、別途リソースの中に定義したものを適用しています。
スタイルはタブが選択されている場合のものと、タブが選択されていない場合のものを
用意し、それをトリガーを使って切り替えます。
以下がボタンのスタイルを定義するしている部分です。
<Style TargetType="Button" x:Key="CloseButton"> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border> <Grid> <Image Source="/Image/close.gif" Width="12" Height="12"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="Button" x:Key="CloseDisableButton"> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border> <Grid> <Image Source="/Image/close_disable.gif" Width="12" Height="12"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
ButtonのContentにはImageを配置しています。イメージファイルはプロジェクトフォルダの直下に
Imageというフォルダを作り、そこに追加します。WPFの場合、これだけでアセンブリに含まれるリソースとして
ビルドしてくれます。
画像の切り替えは、トリガを使って行います。上記で紹介しましたようにデフォルトの状態ではStyleに
「CloseDisableButton」を設定し、選択された場合、以下のようにトリガを使って、「CloseButton」に
切り替えています。
<Trigger Property="IsSelected" Value="True"> <Setter Property="Panel.ZIndex" Value="100" /> <Setter TargetName="Border" Property="Background" Value="{StaticResource WindowBackgroundBrush}" /> <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" /> <Setter TargetName="CloseButton" Property="Style" Value="{StaticResource CloseButton}"/> </Trigger>
閉じるボタンが押された際に実際にTabを閉じ、イベントを発生させる必要があるのですが、
その部分は次回に紹介させていただきます。
本日のソース
コメント
コメントの投稿
トラックバック
http://csfun.blog49.fc2.com/tb.php/24-7dd5c725