WPFでは、ほとんどのコントロールにMouseUp
およびMouseDown
イベント(およびマウスボタン固有のバリエーション)がありますが、すぐに使用できる単純なClick
イベントはありません。これらのイベントを使用してクリックのような動作をしたい場合は、両方を処理する必要がありますが、これは少し面倒だと思います。
明らかな問題は、クリックが別のコントロールで開始され、MouseDown
のみを処理するコントロール上で解放された場合、想定されるクリックが実際に発生する間に発生するため、MouseUp
イベントを単純に省略できないことです。すべきではない:MouseUp
とMouseDown
の両方が同じコントロール上で発生する必要があります。
ですから、もしあれば、この一般的な問題に対するよりエレガントな解決策に興味があります。
注:以下に示すように、これにはいくつかの良い解決策があります。レイチェルの回答は好評であると思われるため、受け入れることにしましたが、さらに私は次の注釈を追加したい:
レイチェルのボタンの答え は非常にクリーンでわかりやすいですが、実際のコントロールをボタンでラップする必要があり、場合によっては、クリックできるという理由だけでコントロールがボタンであるとは見なされないことがあります(例:ハイパーリンクのようなものである場合は、さらに毎回テンプレートを参照する必要があります。
Rick Sladkeyの動作の回答 クリックをシミュレートする/コントロールをクリック可能にする方法の元の質問に、より直接的に回答します。欠点は、System.Windows.Interactivity
を参照する必要があり、レイチェルのソリューションのように、 Xaml-かなりのコード。
私の添付イベントの回答 Xamlの点で通常のクリックイベントに非常に近いという利点があります-1つの属性で実行できるマークアップ、それで私が見る唯一の問題は、イベント添付ですコード内は完全にカプセル化されていません(誰かがその修正を知っている場合は、回答にコメントを追加してください)。
Button
コントロールを使用し、Button.Template
を上書きして、コンテンツを直接表示します。
<ControlTemplate x:Key="ContentOnlyTemplate" TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
<Button Template="{StaticResource ContentOnlyTemplate}">
<Label Content="Test"/>
</Button>
これは、通常のボタンロジックを使用してButtonBase.Click
イベントを発生させるように任意の要素に追加できる動作です。
public class ClickBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += (s, e) =>
{
e.Handled = true;
AssociatedObject.CaptureMouse();
};
AssociatedObject.MouseLeftButtonUp += (s, e) =>
{
if (!AssociatedObject.IsMouseCaptured) return;
e.Handled = true;
AssociatedObject.ReleaseMouseCapture();
if (AssociatedObject.InputHitTest(e.GetPosition(AssociatedObject)) != null)
AssociatedObject.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
};
}
}
マウスのキャプチャ/リリースと入力ヒットテストチェックの使用に注意してください。この動作を適切に行うと、次のようなクリックハンドラーを作成できます。
<Grid>
<Rectangle Width="100" Height="100" Fill="LightGreen" ButtonBase.Click="Rectangle_Click">
<i:Interaction.Behaviors>
<utils:ClickBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
</Grid>
そして背後にあるコード:
private void Rectangle_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Code-behind: Click");
}
これをすべてのコードビハインドに変換するのは簡単です。重要な部分は、キャプチャとクリックのロジックです。
動作に慣れていない場合は、Expression Blend 4 SDKをインストールして、次の名前空間を追加します。
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
プロジェクトにSystem.Windows.Interactivity
を追加します。
編集:
コードビハインドの要素にクリック動作をアタッチし、クリックイベントのハンドラーを追加する方法は次のとおりです。
void AttachClickBehaviors()
{
AttachClickBehavior(rectangle1, new RoutedEventHandler(OnAttachedClick));
}
void OnAttachedClick(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Attached: Click");
}
// Reusable: doesn't need to be in the code-behind.
static void AttachClickBehavior(FrameworkElement element, RoutedEventHandler clickHandler)
{
Interaction.GetBehaviors(element).Add(new ClickBehavior());
element.AddHandler(ButtonBase.ClickEvent, clickHandler);
}
さて、添付のイベントをいじって、どこで取得するかを考えてみたところ、ほとんどの場合、次のように機能するようで、いくつかの改良/デバッグを使用できます。
_public static class Extensions
{
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"Click",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(UIElement)
);
public static void AddClickHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement element = d as UIElement;
if (element != null)
{
element.MouseLeftButtonDown += new MouseButtonEventHandler(element_MouseLeftButtonDown);
element.MouseLeftButtonUp += new MouseButtonEventHandler(element_MouseLeftButtonUp);
element.AddHandler(Extensions.ClickEvent, handler);
}
}
public static void RemoveClickHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement element = d as UIElement;
if (element != null)
{
element.MouseLeftButtonDown -= new MouseButtonEventHandler(element_MouseLeftButtonDown);
element.MouseLeftButtonUp -= new MouseButtonEventHandler(element_MouseLeftButtonUp);
element.RemoveHandler(Extensions.ClickEvent, handler);
}
}
static void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement uie = sender as UIElement;
if (uie != null)
{
uie.CaptureMouse();
}
}
static void element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
UIElement uie = sender as UIElement;
if (uie != null && uie.IsMouseCaptured)
{
uie.ReleaseMouseCapture();
UIElement element = e.OriginalSource as UIElement;
if (element != null && element.InputHitTest(e.GetPosition(element)) != null) element.RaiseEvent(new RoutedEventArgs(Extensions.ClickEvent));
}
}
}
_
使用法:
_<TextBlock local:Extensions.Click="TextBlock_Click" Text="Test"/>
_
編集:十分にクリアされなかった送信者/ソースを保存する代わりに、マウスキャプチャを使用するように変更しました。残りの問題は、クリックイベントを発生させるために必要な他のマウスイベントの添付ファイルが省略されるため、UIElement.AddHandler(Extensions.ClickEvent, handler)
を使用してコードにイベントを追加できないことです(代わりにExtensions.AddClickHandler(object, handler)
を使用する必要があります)。
これは私がかなり定期的に使用する簡単な解決策です。それは簡単で、あらゆる種類のシナリオで機能します。
コードにこれを配置します。
private object DownOn = null;
private void LeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (DownOn == sender)
{
MessageBox.Show((sender as FrameworkElement).Name);
}
DownOn = null;
}
private void LeftButtonDown(object sender, MouseButtonEventArgs e)
{
DownOn = sender;
}
今、あなたがしなければならないのは、あなたが気にかけているすべてのコントロールのハンドラーをセットアップすることです。
<Border Background="Red" MouseLeftButtonUp="LeftButtonUp" MouseLeftButtonDown="LeftButtonDown".../>
<Border Background="Blue" MouseLeftButtonUp="LeftButtonUp" MouseLeftButtonDown="LeftButtonDown".../>
大量のコントロールがあり、すべてをXAMLで実行したくない場合は、コントロール/ウィンドウコンストラクターでプログラムで実行できます。
これはほとんどのシナリオをカバーし、特別な場合を処理するために簡単に調整できます。