2013-04-08 24 views
6

Tôi đã phải đối mặt với sự hiểu lầm tiếp theo.MouseButtonEventArgs.MouseDevice.DirectlyOver sự hiểu lầm

Preamble:

Tôi có WPF ứng dụng với tới phần giao diện người dùng thiết yếu: RadioButtons và một số điều khiển mà sử dụng thả xuống dựa trên Popup (theo cách combobox). Theo một số logic, mỗi móc nối vô tuyến PreviewMouseDown sự kiện và thực hiện một số phép tính. Trong kịch bản tiếp theo,

  1. tài mở cửa sổ bật lên (không chọn một cái gì đó, bật lên chỉ ở mở)
  2. tài nhấp chuột vào radiobutton

PreviewMouseDown sẽ không bị đuổi vì RadioButton như mong đợi (vì của Popupfeature).

Và mục đích của tôi là bắn PreviewMouseDown cho RadioButton bất kể một.

nỗ lực để giải quyết:

nhanh và bẩn giải pháp là: treo PreviewMouseDown cho Popup và tái lửa PreviewMouseDown kiện với nguồn mới nếu cần thiết, sử dụng RadioButton như nguồn. Có thể lấy nguồn mới qua MouseButtonEventArgs.MouseDevice.DirectlyOver. Các mảnh tiếp theo của mã làm điều đó (sự kiện được tái sa thải chỉ khi Popup "ăn" PreviewMouseDown cho nhấp chuột bên ngoài):

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

này hoạt động tốt khi tôi đang gắn handler đó để Popup.PreviewMouseDown trực tiếp qua Behaviorkhông làm việc (PreviewMouseDown không được sa thải vì radioButton) nếu tôi gắn một qua EventManager.RegisterClassHandler (mục đích là để tránh đính kèm hành vi cho mọi Popup có thể occure trên trang với những radiobuttons):

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

Trình gỡ lỗi cho thấy rằng e.MouseDevice.DirectlyOver (xem mã ở trên) là Popup, không phải Radiobutton (vì đó là khi tôi đã đính kèm trình xử lý qua Behavior)!

Câu hỏi:

Làm thế nào và tại sao MouseButtonEventArgs có thể khác nhau đối với các hành động tương tự, nếu eventhandler gắn theo hai cách khác nhau?

Ai đó có thể phát hiện hành vi này không?

Cảm ơn rất nhiều.

+0

Có một trường hợp thử nghiệm tái sản xuất tối thiểu trong XAML cho điều đó đối với chúng tôi chơi? Có thể có một sự nhầm lẫn về những gì Xem trước có nghĩa là trong bối cảnh WPF. Khi Xem trước * Sự kiện là các sự kiện đường hầm và bạn có thể muốn sử dụng Sự kiện MouseDown vì theo mặc định, bạn sẽ sủi bọt lên cây thị giác của mình – Samuel

+1

Bạn có thể đăng XAML cho Popup và RadioButton của mình không? Giống như Samuel nói, đây là một vấn đề với đường hầm và các sự kiện sủi bọt. Sự kiện này đang được xử lý trên Popup trước khi nó thậm chí có thể đạt được Radiobutton là dự đoán của tôi. –

+0

Chỉ cần đâm trong bóng tối, nhưng sẽ giống như công việc này? - Trình xử lý sự kiện MouseDown cho popup => popup.Close(); Trình xử lý sự kiện MouseUp cho radiobutton => làm việc của bạn. Ý tưởng là một MouseClick bao gồm hai hành động, hành động MouseDown đầu tiên sẽ đóng cửa sổ bật lên và hành động MouseUp thứ hai sau đó sẽ được chụp bằng nút radio ... – Marko

Trả lời

0

Hộp tổ hợp được cung cấp như một cách để người dùng chọn từ một nhóm tùy chọn và bạn có thể muốn làm điều đó. Nhưng nó cũng có các hợp đồng khác. Nó nói rằng người dùng nên tập trung vào điều này và chỉ có nhiệm vụ này. Nhưng đó không phải là tình huống của bạn. Bạn muốn hiển thị các tùy chọn, yêu cầu họ ẩn và có thể cho phép người dùng thực hiện các việc khác khi chúng được hiển thị.

Tôi nghĩ thay vì hộp tổ hợp bạn muốn kiểm soát khác. Đề nghị của tôi là sử dụng một expander có chứa một listbox. Đưa ra:

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

Tôi nghĩ rằng bạn muốn XAML sau:

<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> 

Và sau 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"); 
    } 
Các vấn đề liên quan