MouseButtonEventArgs.MouseDevice.DirectlyOver misunderstanding

I came across the following misunderstanding.

Preamble

I have a wpf application with the following important parts of the user interface: RadioButtons and some control that uses a popup based dropdown menu (in list order). According to some logic, each case of the PreviewMouseDown beam hook and some calculations. In the following scenario

  • The user opens a pop-up window (do not select something, the pop-up window remains open)
  • User clicks on the drum

PreviewMouseDown will not start for the radio unit as expected (due to the Popup function ).

And my goal is to shoot PreviewMouseDown for RadioButton , despite one.

Attempts to solve :

Quick and dirty solution: hook PreviewMouseDown for Popup and restart the PreviewMouseDown event with a new source, if required, using the radio as the source. A new source can be obtained through MouseButtonEventArgs.MouseDevice.DirectlyOver . The following code fragment does this (the event is restarted only if Popup "has" PreviewMouseDown for an external click):

  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); } } 

This one works well when I attach this handler to Popup.PreviewMouseDown directly through Behavior and does not work ( PreviewMouseDown does not start for radio EventManager.RegisterClassHandler ) if I attach one through EventManager.RegisterClassHandler (the goal is to avoid attaching behavior to each Popup that may appear on the page with these radio exchanges):

 EventManager.RegisterClassHandler( typeof (Popup), PreviewMouseDownEvent, new MouseButtonEventHandler(GrantedPopupPreviewMouseDown)); 

The debugger showed that e.MouseDevice.DirectlyOver (see code above) is Popup , not RadioButton (as it was when I bound the handler through Behavior )!

Question

How and why can MouseButtonEventArgs be different for the same action if event binding has two different ways?

Can anyone explain this behavior?

Thank you very much.

+6
source share
1 answer

Layout is provided as a way for users to select from a group of options, and you probably want to do this. But he also has other contracts. It states that the user should focus on this and only on this task. But this is not your situation. You want to show the parameters, make them hide the possibilities and let the user do other things while they are showing.

I think that instead of combo boxes you need a different control. My suggestion is to use an expander containing a list. Given:

 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); } } 

I think you need the following 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> 

And the following code:

  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"); } 
0
source

All Articles