--> -->
#blog2navi() *XAMLをincludeしてイベントを上に上げる [#k5a65819] MicroTwitの機能を拡張していくにつれ、XAMLがどんどん長くなってきたので、UserControlを作成してincludeする形にしました。~ そのとき、イベントの取り回しで色々苦労したのでφ(.. )です。~ ~ 以下の3つについて記述します。~ + XAMLをincludeする + イベントをMainWindowに上げる + MainWindowのXAMLからUserControlに値を渡す + MainWindowのXAMLからUserControlに静的に値を渡す まずはいきなりソース全体像から。~ ~ ■UserInformation.xaml #code(xml,nooutline){{ <UserControl x:Class="MicroTwit.Views.UserInformation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <TextBlock Text="テキスト文字列" ContextMenuOpening="ctxContextMenu_Opening" PreviewMouseRightButtonDown="txtPreviewMouseRightButtonDown"> <TextBlock.ContextMenu> <ContextMenu Opened="ctxContextMenu_Opened"> <MenuItem Header="メニュー項目1" Click="MenuItem1_Click" /> </ContextMenu> </TextBlock.ContextMenu> </TextBlock> </Grid> </UserControl> }} ~ ■UserInformation.xaml.cs #code(CSharp,nooutline){{ using System.Windows.Controls; using System.Windows; using System; namespace MyProject.Views { /// <summary> /// UserInformation.xaml の相互作用ロジック /// </summary> public partial class UserInformation : UserControl { public object sender; private static string mode; // イベントRouteの定義 public static readonly RoutedEvent evt_MouseRightOnText = EventManager.RegisterRoutedEvent("EventMouseRightClickOnText", RoutingStrategy.Bubble, typeof(MouseButtonEventHandler), typeof(TextBox)); public static readonly RoutedEvent evt_ctxContextMenu_Opening = EventManager.RegisterRoutedEvent("EventContextMenu_Opening", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ContextMenu)); public static readonly RoutedEvent evt_ctxContextMenu_Opened = EventManager.RegisterRoutedEvent("EventContextMenu_Opened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ContextMenu)); public static readonly RoutedEvent evt_MenuItem1_Click = EventManager.RegisterRoutedEvent("EventMenuItem1_Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem)); // カスタムプロパティ定義 internal static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(string), typeof(UserInformation), new PropertyMetadata(null, new PropertyChangedCallback(OnTweetModeChanged))); internal string Mode { get { return (string)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } // イベント登録 public event MouseButtonEventHandler EventMouseRightClickOnText{ add { AddHandler(evt_MouseRightOnText, value); } remove { RemoveHandler(evt_MouseRightOnText, value); } } public event RoutedEventHandler EventContextMenu_Opening { add { AddHandler(evt_ctxContextMenu_Opening, value); } remove { RemoveHandler(evt_ctxContextMenu_Opening, value); } } public event RoutedEventHandler EventContextMenu_Opened { add { AddHandler(evt_ctxContextMenu_Opened, value); } remove { RemoveHandler(evt_ctxContextMenu_Opened, value); } } public event RoutedEventHandler EventMenuItem1_Click { add { AddHandler(evt_MenuItem1_Click, value); } remove { RemoveHandler(evt_MenuItem1_Click, value); } } // コンストラクタ public UserInformation() { InitializeComponent(); } // イベントを上に上げる処理 private void txtPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton) { RoutedEvent = evtMouseRightOnText, RoutedEvent = evt_MouseRightOnText, }; this.sender = sender; RaiseEvent(eventArgs); } private void ctxContextMenu_Opening(object sender, ContextMenuEventArgs e) { if (mode.Equals("modeB")) { // コンテキストメニューキャンセル e.Handled = true; } else { RoutedEventArgs eventArgs = new RoutedEventArgs(evt_ctxContextMenu_Opening); this.sender = sender; RaiseEvent(eventArgs); } } private void ctxContextMenu_Opened(object sender, RoutedEventArgs e) { RoutedEventArgs eventArgs = new RoutedEventArgs(evt_ContextMenu_Opened); this.sender = sender; RaiseEvent(eventArgs); } private void MenuItem1_Click(object sender, RoutedEventArgs e) { RoutedEventArgs eventArgs = new RoutedEventArgs(evt_MenuItem1_Click); this.sender = sender; RaiseEvent(eventArgs); } // モードプロパティの変更 private static void OnModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { UserInformation ui = (UserInformation)obj; mode = (string)args.NewValue; } } } }} ~ ■MainWindow.xaml #code(xml, nooutline){{ <Window x:Class="MyProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell" xmlns:p="clr-namespace:MyProject.Properties" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:v="clr-namespace:MyProject.Views"> <!-- user情報用の行を定義 --> <v:UserInformation TweetMode="Timeline" Mode="modeA" EventMouseRightClickOnText="userinfo_MouseRightClickOnText_Click" EventContextMenu_Opening="userinfo_ContextMenu_Opening" EventContextMenu_Opened="userinfo_ContextMenu_Opened" EventMenuItem1_Click="userinfo_MenuItem1_Click" /> <v:UserInformation Mode="modeB" EventMouseRightClickOnText="userinfo_MouseRightClickOnText_Click" EventContextMenu_Opening="userinfo_ContextMenu_Opening" EventContextMenu_Opened="userinfo_ContextMenu_Opened" EventMenuItem1_Click="userinfo_MenuItem1_Click" /> }} ~ ■MainWindow.xaml.cs #code(CSharp,nooutline){{ private void userinfo_MouseRightClickOnText_Click(object sender, MouseButtonEventArgs e) { object s = ((UserInformation)sender).sender; ・・・色々処理・・・ } private void userinfo_ContextMenu_Opened(object sender, RoutedEventArgs e) { object s = ((UserInformation)sender).sender; ・・・色々処理・・・ } : : }} では上から順に。 ** XAMLをincludeする [#yb7bdb57] やっていることはまんまこちらのページで紹介されている内容です。→ [[XAML を分割して記述する方法:http://foreignkey.jp/archives/315]]~ ここでの注意点は、namespaceに「MyProject.Views」と指定しているので、一緒に作られるMyProject.xaml.csも以下のようにする必要があることです。 UserInformation.xaml.csのnamespaceの指定だけ、注意が必要です。~ ** イベントをMainWindowに上げる [#ra1c0212] イベントをユーザーコントロールからMainWindowに上げるには、RoutedEventの仕組みを使います。~ 「RoutedEventの定義」「イベント登録」「イベントの転送」の三点セットです。~ ここでの注意点は「RoutedEventArgs」と「sender」「MainWindow.xamlのイベント定義」の3つです。~ - RoutedEventArgs~ 検索して出てくるサンプルは、RoutedEventArgsを使ったものが殆どですが、Mouseのイベント処理などではマウスのPositionなど、MouseButtonEventArgsが必要になります。evt_MouseRightOnText〜EventMouseRightClickOnText〜txtPreviewMouseRightButtonDownではその例を示しています。 - sender~ MainWindow側のイベント処理では、senderに「UserInformation」が入ってきてしまい、実際にイベントが発生したオブジェクトは(私が調べた限りでは)取ることができません。これを回避するため、UserInformationにクラス変数としてsenderを用意し、そこにイベント発生元のsenderセットすることで、MainWindowから使えるようにしています。 - MainWindow.xamlのイベント定義~ ユーザーコントロールから上がってきたイベントと、MainWindow.xaml.csのイベント処理を紐付ける記述が、MainWindow.xamlのイベント定義です。イベントの数だけ並べてあげる必要があります。 ** MainWindowのXAMLからUserControlに値を渡す [#fe91a948] XAMLを何カ所かでincludeしても、一部で異なる処理をしたい場合があります。~ その際は、カスタムプロパティを作ってMainWindow.xamlから指定してやることで、プロパティの内容によって処理を分けることができます。~ 上の例では、カスタムプロパティ「Mode」を設け、MainWindow.xamlで異なる値を指定しています(10,17行目)。~ UserInformation.xaml.csでは、指定されたModeの値をクラス変数modeに保存しておき、「ctxContextMenu_Opening」の中で判別しています。この例では、modeAのときはコンテキストメニューが表示されますが、modeBの時は表示されません。~ ~ ~ 書きかけ〜・・・ senderの扱いがちょっとお行儀の悪い感じですが、とりあえず出来ているので良しとします^^;。~ イベントが短時間に連続して発生した場合、MainWindow側で正しい値が取れない場合があるかもしれません。~ もっと良い方法無いですかねぇ。。 #htmlinsert(twitterbutton.html) RIGHT:Category: [[[CSharp>日記/Category/CSharp]]][[[Windows>日記/Category/Windows]]] - 23:25:32 ---- RIGHT:&blog2trackback(); #comment(above) #blog2navi()