#blog2navi()
*XAMLをincludeしてイベントを上に上げる [#k5a65819]
MicroTwitの機能を拡張していくにつれ、XAMLがどんどん長くなってきたので、UserControlを作成してincludeする形にしました。~
そのとき、イベントの取り回しで色々苦労したのでφ(.. )です。~
~
以下の3つについて記述します。~
+ XAMLをincludeする
+ イベントをMainWindowに上げる
+ 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 = 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
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]]~
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()