Q086. DataGrid/XamDataGrid でスクロールバーをダブルクリックすると、MouseDoubleClickイベントが発生しちゃう!><
A.Forms の DataGridView はスクロールバーをダブルクリックしても MouseDoubleClick イベントは発生しません。しかし WPF の DataGrid や XamDataGrid は、スクロールバーをダブルクリックすると MouseDoubleClick イベントが発生します。どうやらこれは仕様みたいです。これを回避するには、イベントを発行したオブジェクトで判定するしかなさそうです。そこで少し手が込みますが仕掛けを用意します。
まず Forms の DataGrid で試してみました。セルやヘッダをダブルクリックすると MouseDoubleClick イベントを発行しますが、スクロールバーをダブルクリックしてもイベントは発生しません。
次に WPF の DataGrid です。スクロールバーをダブルクリックすると、見事に MouseDoubleClick イベントが発生します。
e.OriginalSource に格納された要素をビジュアルツリーで見ると、上位要素が ScrollBar であることが確認できます。
これを回避するには、上位要素が ScrollBar か否かを判定するしかありません。そこで下記のクラスを用意します。
UIHelpers.FindParent メソッドは上位要素に指定した型があるか検索し、あればその要素を返却します。元ネタは stackoverflow.com の記事を参考にさせて頂きました。
using System.Windows; using System.Windows.Media; public static class UIHelpers { /// <summary> /// 指定要素の親要素を検索します。 /// </summary> public static DependencyObject FindParent(DependencyObject child, string typeName) { var parent = VisualTreeHelper.GetParent(child) as DependencyObject; if (parent != null) { if (parent.GetType().Name == typeName) { return parent; } else { parent = FindParent(parent, typeName); } } return parent; } }
PreviewMouseDoubleClick イベント内で以下のように実走すれば、スクロールバーをダブルクリックしても MouseDoubleClick イベントの発生を抑止します。
private void dataGrid1_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) { var source = e.OriginalSource as DependencyObject; var parent = UIHelpers.FindParent(source, "ScrollBar"); if (parent != null) { e.Handled = true; } }
しかしこれだと DataGrid を配置する全ての Window にイベントを実装しなければいけません。それでは保守が面倒なのでトリガーをひとつ用意します。プロジェクトの参照設定に System.Windows.Interactivity アセンブリを追加し、以下のクラスをプロジェクトに追加します。
using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; // サンプルなので namespace はわざと外してます。ご注意ください。 public sealed class DataGridDoubleClickTrigger : TriggerBase<DataGrid> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewMouseDoubleClick += AssociatedObject_MouseDoubleClick; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewMouseDoubleClick -= AssociatedObject_MouseDoubleClick; } void AssociatedObject_MouseDoubleClick(object sender, MouseButtonEventArgs e) { var source = e.OriginalSource as DependencyObject; if (source != null) { var presenter = UIHelpers.FindParent(source, "ScrollBar"); if (presenter != null) { e.Handled = true; } } } }
XAML です。XAML 名前空間に「interactivity」と「現在のプロジェクト」を追加し、DataGrid にインタラクショントリガーを設定、先ほど用意した DataGridDoubleClickTrigger を設定します。これでスクロールバーをダブルクリックしても MouseDoubleClick イベントは発生しません。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="183" Width="323" ContentRendered="Window_ContentRendered" > <Grid> <DataGrid Name="dataGrid1" MouseDoubleClick="dataGrid1_MouseDoubleClick" > <i:Interaction.Triggers> <local:DataGridDoubleClickTrigger /> </i:Interaction.Triggers> </DataGrid> </Grid> </Window>
これを応用すれば、XamDataGrid でもスクロールバーのイベント回避が可能になります。
以下のクラスは XamDataGrid 用のトリガーです。DataRecord しかダブルクリックイベントを発生させないよう抑止してます。
using System.Windows; using System.Windows.Input; using System.Windows.Interactivity; using Infragistics.Windows.DataPresenter; public sealed class XamDataGridDoubleClickTrigger : TriggerBase<XamDataGrid> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewMouseDoubleClick += AssociatedObject_MouseDoubleClick; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewMouseDoubleClick -= AssociatedObject_MouseDoubleClick; } void AssociatedObject_MouseDoubleClick(object sender, MouseButtonEventArgs e) { var source = e.OriginalSource as DependencyObject; if (source != null) { var presenter = UIHelpers.FindParent(source, "DataRecordPresenter"); if (presenter == null) { e.Handled = true; } } } }