2013-04-08 6 views
6

Mi sono trovato di fronte al prossimo malinteso.MouseButtonEventArgs.MouseDevice.DirectlyOgni equivoco

Preambolo:

ho wpf applicazione con i seguenti componenti essenziali: UI RadioButtons e qualche controllo che utilizzano discesa basato su Popup (in modo combobox). Secondo alcune logiche, ogni radio hook intercetta l'evento PreviewMouseDown e fa alcuni calcoli. apre Nello scenario successivo,

  1. utente a comparsa (non selezionare qualcosa, pop-up solo restare aperto)
  2. utente clicca sul pulsante radio

PreviewMouseDown non saranno licenziati per pulsante radio come previsto (a causa di Popupfeature).

E il mio obiettivo è sparare PreviewMouseDown per RadioButton nonostante uno.

tentativi di risolvere:

soluzione rapida e sporca è: Hook PreviewMouseDown per Popup e ri-fuoco PreviewMouseDown evento con nuova fonte se necessario, utilizzando pulsante radio come sorgente. La nuova fonte può essere ottenuta tramite MouseButtonEventArgs.MouseDevice.DirectlyOver. Il prossimo pezzo di codice farlo (evento è ri-licenziato solo se Popup "mangiare" PreviewMouseDown per click esterno):

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

Questo funziona bene quando sto allegando che gestore di Popup.PreviewMouseDown direttamente tramite Behavior e non funzionano (PreviewMouseDown non viene licenziato per RadioButton) se sto allegando una via EventManager.RegisterClassHandler (obiettivo è quello di evitare di attaccare il comportamento di ogni Popup che possono occure a pagina con queste radiobuttons):

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

Il debugger ha mostrato che e.MouseDevice.DirectlyOver (vedere il codice sopra) è Popup, non Radiobutton (come è stato quando ho collegato il gestore tramite Behavior)!

Domanda:

Come e perché MouseButtonEventArgs può essere diverso per la stessa azione, se EventHandler attaccata in due modi diversi?

Qualcuno può spiegare questo comportamento?

Grazie mille.

+0

C'è un caso di test riproducibile minimo in XAML per questo per noi giocare? Potrebbe esserci confusione su cosa significa Anteprima nel contesto WPF. Come Anteprima * Gli eventi sono eventi di tunneling e potresti voler utilizzare l'evento MouseDown come per impostazione predefinita che ribolle il tuo albero visivo – Samuel

+1

Puoi pubblicare XAML per il tuo Popup e RadioButton? Come ha detto Samuel, questo è un problema con gli eventi di tunneling vs bubbling. L'evento viene gestito sul Popup prima che possa raggiungere anche il Radiobutton è la mia ipotesi. –

+0

Solo una pugnalata al buio, ma qualcosa di simile a questo lavoro? - Gestore di eventi MouseDown per popup => popup.Close(); Il gestore di eventi MouseUp per radiobutton => fai le tue cose. L'idea è che un MouseClick è costituito da due azioni, la prima azione MouseDown chiuderà il popup e la seconda azione MouseUp verrà catturata dal pulsante di opzione ... – Marko

risposta

0

La casella combinata viene fornita come metodo per consentire agli utenti di selezionare da un gruppo di opzioni e probabilmente si desidera farlo. Ma ha anche altri contratti. Dice che l'utente dovrebbe concentrarsi su questo e solo su questo compito. Ma questa non è la tua situazione. Vuoi mostrare le opzioni, farle nascondere e consentire all'utente di fare altre cose mentre vengono mostrate.

Penso che invece di caselle combinate vuoi un altro controllo. Il mio suggerimento è di usare un expander che contiene una listbox. Dato:

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

Penso che si desidera il seguente 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> 

E il seguente code-behind:

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"); 
    } 
Problemi correlati