2013-04-08 10 views
6

私は次の誤解に直面しました。MouseButtonEventArgs.MouseDevice.DirectlyOver誤解

プリアンブルRadioButtonsと(コンボボックスのように)Popupに基づいて、ドロップダウンを使用し、いくつかのコントロール:

Iは、次の必須のUI部品とアプリケーションWPF有します。いくつかのロジックに応じてすべてのラジオボタンのフックPreviewMouseDownフックといくつかの計算を行います。次のシナリオでは 、

  1. ユーザーは、ポップアップを開きます(何かを選択しないでください、ポップアップがちょうどオープン滞在)
  2. 期待通りのRadioButton

PreviewMouseDown上のユーザーのクリックでラジオボタンのために解雇されることはありません(理由Popupfeature)。

PreviewMouseDownRadioButtonにもかかわらず、私の目的は発砲しています。を解決する

試み:

高速かつ汚いソリューションです:必要に応じてソースとしてラジオボタン使用して、PopupためPreviewMouseDownと再火新しいソースとPreviewMouseDownイベントをフック。新しいソースはMouseButtonEventArgs.MouseDevice.DirectlyOverで取得できます。

private static void GrantedPopupPreviewMouseDown(object sender, MouseButtonEventArgs e) 
    { 
     var popup = sender as Popup; 
     if(popup == null) 
      return; 

     var realSource = e.MouseDevice.DirectlyOver as FrameworkElement; 
     if(realSource == null || !realSource.IsLoaded) 
      return; 

     var parent = LayoutTreeHelper.GetParent<Popup>(realSource); 

     if(parent == null || !Equals(parent, popup)) 
     { 
      e.Handled = true; 
      var args = new MouseButtonEventArgs(e.MouseDevice, 
               e.Timestamp, 
               e.ChangedButton) 
      { 
       RoutedEvent = UIElement.PreviewMouseDownEvent, 
       Source = e.MouseDevice.DirectlyOver, 
      }; 
      realSource.RaiseEvent(args); 
     } 
    } 

この作品をうまくを私はBehaviorを介して直接Popup.PreviewMouseDownにそのハンドラを装着していたときに:コードの次の部分は、(Popupが外側クリック用PreviewMouseDownを「食べる」場合にのみ、イベントは、解雇し直される)ことをやります

EventManager.RegisterClassHandler(
      typeof (Popup), 
      PreviewMouseDownEvent, 
      new MouseButtonEventHandler(GrantedPopupPreviewMouseDown)); 
0123:私はEventManager.RegisterClassHandlerを経由して1を装着していた場合PreviewMouseDownは、ラジオボタンのために解雇されていない)動作しません(その目的は、これらのラジオボタンでページ上occureことができるすべてのPopupに行動を取り付ける避けるためです)

デバッガは、(上記のコードを参照)がPopupではなく、Radiobuttonではないことを示しました(ハンドラをBehavior経由で接続したときと同じです)。

質問

どのように、なぜEventHandlerは2つの異なる方法で接続されている場合MouseButtonEventArgsは、同じアクションのために異なることができますか?

誰かがこの動作を説明できますか?

ありがとうございます。

+0

は私たちのためにそのためのXAMLで、最小限の再現可能なテストケースがあります遊ぶ?プレビューがWPFのコンテキストで何を意味するのか混乱するかもしれません。プレビューとして*イベントはイベントをトンネリングしているので、デフォルトではMouseDownイベントを使用してビジュアルツリーをバブリングすることができます – Samuel

+1

ポップアップとラジオボタンのXAMLを投稿できますか?サムエルの言ったように、これはトンネリングとバブリングの出来事の問題です。ポップアップでイベントが処理される前に、Radiobuttonに到達することもできます。 –

+0

暗闇の中でちょうど刺すが、この作品のようなものだろうか?ポップアップのMouseDownイベントハンドラ=> popup.Close(); radiobuttonのMouseUpイベントハンドラ=>あなたのことをやってください。最初のMouseDownアクションはポップアップを閉じ、2番目のMouseUpアクションはラジオボタンでキャプチャされます。 – Marko

答えて

0

コンボボックスは、ユーザーがオプションのグループから選択する方法として提供されています。しかしそれには他の契約もあります。ユーザーには、この作業と、この作業だけに焦点を当てる必要があります。しかしそれはあなたの状況ではありません。オプションを表示し、その機能を隠すようにし、ユーザーが表示されている間に他の操作を実行できるようにしたいとします。

私はコンボボックスの代わりにいくつかの他のコントロールが必要だと思います。私の提案は、リストボックスを含むエクスパンダを使うことです。与えられた:

class NotificationObject : INotifyPropertyChanged 
{ 
    public void RaisePropertyChanged(string name) 
    { 
     if(PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(name)); 
     } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

class ComboEntry : NotificationObject 
{ 
    public string Name { get; private set; } 

    private string _option = "Off"; 
    public string Option 
    { 
     get { return _option; } 
     set { _option = value; RaisePropertyChanged("Option"); } 
    } 

    public ComboEntry() 
    { 
     Name = Guid.NewGuid().ToString(); 
    } 
} 

class MyDataContext : NotificationObject 
{ 
    public ObservableCollection<ComboEntry> Entries { get; private set; } 

    private ComboEntry _selectedEntry; 
    public ComboEntry SelectedEntry 
    { 
     get { return _selectedEntry; } 
     set { _selectedEntry = value; RaisePropertyChanged("SelectedEntry"); } 
    } 
    public MyDataContext() 
    { 
     Entries = new ObservableCollection<ComboEntry> 
     { 
      new ComboEntry(), 
      new ComboEntry(), 
      new ComboEntry() 
     }; 
     SelectedEntry = Entries.FirstOrDefault(); 
    } 
    public void SetOption(string value) 
    { 
     Entries 
      .ToList() 
      .ForEach(entry => entry.Option = value); 
    } 
} 

は、私はあなたが以下のXAMLをしたいと思う:

<Window x:Class="RadioInCombo.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:RadioInCombo" 
    Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <local:MyDataContext x:Key="myDataContext" /> 
     <DataTemplate x:Key="ComboEntryTemplate"> 
      <StackPanel Orientation="Horizontal"> 
       <TextBlock Text="{Binding Name}" /> 
       <Border Width="5" /> 
       <TextBlock Text="{Binding Option}" /> 
      </StackPanel> 
     </DataTemplate> 
    </Window.Resources> 
    <StackPanel DataContext="{StaticResource myDataContext}"> 
     <RadioButton x:Name="OnButton" 
        Content="On" 
        PreviewMouseDown="OnButton_PreviewMouseDown" /> 
     <RadioButton x:Name="OffButton" 
        Content="Off" 
        PreviewMouseDown="OffButton_PreviewMouseDown" /> 
     <Expander Header="{Binding SelectedEntry}" 
        HeaderTemplate="{StaticResource ComboEntryTemplate}"> 
      <ListBox ItemsSource="{Binding Entries}" 
        ItemTemplate="{StaticResource ComboEntryTemplate}" /> 
     </Expander> 
    </StackPanel> 
</Window> 

そして、次のコードビハインド:

private MyDataContext GetMyDataContext() 
    { 
     var candidate = FindResource("myDataContext") as MyDataContext; 
     if (candidate == null) throw new ApplicationException("Could not locate the myDataContext object"); 
     return candidate; 
    } 

    private void OnButton_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
    { 
     GetMyDataContext().SetOption("On");    
    } 

    private void OffButton_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
    { 
     GetMyDataContext().SetOption("Off"); 
    } 
関連する問題