気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
さて、今日は前回外観を作った「TabPage」に「閉じる」ボタンが押された場合のイベント処理を実装するところを
紹介します。

「閉じる」ボタンが押された場合、ページが閉じられたことを他のコントロールに伝えるためのルーティングイベントを
発生させます。そして、その後自身を「TabControl」から削除する処理を行います。

今回、開発している「仕訳入力アプリケーション」では同じページが複数開かないように、現在開いているページの
インスタンスをメインフォームのインスタンス変数に設定して管理しています。

「TabPage」が閉じられた際に「TabPage」から発生させるルーティングイベントを処理して、メインフォームのインスタンス変数に
「null」を設定するコードも最後に紹介します。

まずは、「閉じる」ボタンがクリックされた際のイベント処理から。

前回、「Generic.xaml」で以下のようにして閉じるボタンを追加しました。

<Grid>
  <Border>
    <DockPanel x:Name="ContentPanel">
      <Button x:Name="CloseButton" DockPanel.Dock="Right" Style="{StaticResource CloseDisableButton}"/>
      <ContentPresenter ContentSource="Header"/>
    </DockPanel>
  </Border>
</Grid>

「閉じる」ボタンのx:Name属性に[CloseButton]を設定してコードからアクセスできるようにしてあります。

「TabPage.cs」の中では、まず、「FramewoekElement」クラスの「OnApplyTemplate()」というメソッドをオーバーライドして
xamlのテンプレート内で定義した「閉じる」ボタンの「Click」イベントにハンドラを登録します。

 public override void OnApplyTemplate()
 {
     base.OnApplyTemplate();

     Button closeButton = base.GetTemplateChild("CloseButton") as Button;
     closeButton.Click += new RoutedEventHandler(closeButton_Click);
 }

テンプレート内で定義したUIElement(Buttonなど)は、テンプレートが適用された後でないと取得することが出来ません。
「OnApplyTemplate()」はコントロールにテンプレートが適用された後にコールされるメソッドなので
この中で「閉じる」ボタンのインスタンスを取得しています。

そして「閉じる」ボタンのクリックイベントを処理して、新たなルーティングイベントを発生させます。
ルーティングイベントを発生させるためには、あらかじめ、WPFのイベントシステムにイベントを登録しておく必要があります。
以下がその部分です。

 public static readonly RoutedEvent ClosePageEvent = EventManager.RegisterRoutedEvent("ClosePage", RoutingStrategy.Bubble,
                                                                             typeof(RoutedEventHandler), typeof(TabPage));

第1引数はイベントの名前。これはどこで利用されるのか正直わかりませんが、内容が分かりやすいものを付けておけば良いかと思います。
第2引数はルーティングの方法です。Tunnel、Bubble、Directのいずれかを指定します。Tunnelは要素ツリーの上から下へとイベントが伝えられる
ルーティング、Bubbleは下から上です。Directは従来の方法と同じく直接登録したハンドラにのみイベントが伝えられます。
第3引数はイベントのハンドラのデリゲートの型、第4引数にはイベントの所有者?(と書かれています。)これも何に使われるか分かりません。

ここで重要なのは、このメソッドの戻り値であるRoutedEventをクラス変数に設定しておくことです。後でイベントを発生させる際や
このイベントのハンドラを登録する際にキーの役割をします。

次がイベントを発生させる部分です。

 private void closeButton_Click(object sender, RoutedEventArgs e)
 {
     //ClosePageEventを発生させる
     RaiseEvent(new RoutedEventArgs(ClosePageEvent, this));

     //TabControlを取得し、自身を削除する
     TabControl tabControl = Parent as TabControl;
     tabControl.Items.Remove(this);

     e.Handled = true;
 }

RaiseEvent()はUIElementに定義されているメソッドで、引数に指定したルーティングイベントを発生させてくれます。引数にはRoutedEventArgs
を指定します。RoutedEventArgsにはEventManager.RegisterRoutedEvent()から返されたRoutedEventと、イベントのソースとなる自身の
インスタンスを設定します。

イベントを発生させた後、自身をTabControlから削除する処理を行っています。イベントを発生させる前にこれをやってしまうと、
要素ツリーの上下関係が失われ、ルーティングイベントが伝わらなくなってしまいます。また、「閉じる」ボタンのClickイベントも
ルーティングイベントなのですが、これ以上伝える必要が無いので処理済としてマークしています。

最後にこのイベントを処理しているMainWindow.xaml.csのソースを紹介します。
まずコンストラクタでClosePageEventイベントのハンドラを登録しています。この際、AddHandler()メソッドの第1引数に
TabPage.csでEventManager.RegisterRoutedEvent()から返されたRoutedEventを指定します。

 //ページが閉じた際のイベントにハンドラを登録する
 AddHandler(TabPage.ClosePageEvent,new RoutedEventHandler(TabPageClosed));

イベントを発生させる際にもこれを引数に利用していましたよね。このイベントのインスタンスを介して、イベントの発生ソースと
ハンドラを関連付けていることが分かるかと思います。

MainWindowの方では、このイベントを利用して、各ページのUserControlのインスタンス管理をしています。

 private void TabPageClosed(Object sender, RoutedEventArgs e)
 {
     TabPage page = e.OriginalSource as TabPage;

     if (page.Content is JournalEntry)
     {
         _journalEntry = null;
     }
     else if (page.Content is JournalRef)
     {
         _journalRef = null;
     }
 }

WPFを始めてまだ僅かですが、ここまででも、WPFのパワフルな機能にワクワクしてきます。
とっつき難さはあるのですが、今回のような拡張をWindowsフォームでやるとなると、倍以上苦労する気がします。

引き続きレポートします。

本日のソース





コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

http://csfun.blog49.fc2.com/tb.php/25-2bd14972

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。