2010-02-23 40 views
22

Trong giao diện người dùng Silverlight của tôi, tôi có một nút khi nhấp vào bật lên một điều khiển với một số thông số lọc. Tôi muốn điều khiển này tự ẩn khi bạn nhấp vào bên ngoài. Nói cách khác, nó sẽ hoạt động theo cách tương tự như hộp tổ hợp, nhưng nó không phải là một hộp kết hợp (bạn không chọn một mục trong đó). Đây là cách tôi đang cố gắng để nắm bắt một nhấp chuột bên ngoài sự kiểm soát để bỏ qua nó:Làm cách nào để loại bỏ cửa sổ bật lên trong Silverlight khi nhấp vào bên ngoài kiểm soát?

public partial class MyPanel : UserControl 
{ 
    public MyPanel() 
    { 
     InitializeComponent(); 
    } 

    private void FilterButton_Click(object sender, RoutedEventArgs e) 
    { 
     // Toggle the open state of the filter popup 
     FilterPopup.IsOpen = !FilterPopup.IsOpen; 
    } 

    private void UserControl_Loaded(object sender, RoutedEventArgs e) 
    { 
     // Capture all clicks and close the popup 
     App.Current.RootVisual.MouseLeftButtonDown += delegate { 
      FilterPopup.IsOpen = false; }; 
    } 
} 

Thật không may, xử lý sự kiện cho MouseLeftButtonDown không bao giờ bị sa thải. Có cách nào được thiết lập tốt để tạo điều khiển bật lên tự động loại bỏ khi bạn nhấp vào bên ngoài không? Nếu không, tại sao không phải là việc xử lý số MouseLeftButtonDown của tôi?

Giải pháp:

tôi nghĩ rằng tôi muốn gửi toàn bộ giải pháp của tôi trong trường hợp người khác tìm thấy nó hữu ích. Trong top-level của tôi hình ảnh, tôi tuyên bố một "lá chắn" cho các quảng cáo, như thế này:

<UserControl xmlns:my="clr-namespace:Namespace" 
    x:Class="Namespace.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" 
    xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
> 
    <Grid Background="Black" HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch"> 
    <my:MyStuff/> 
    <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
      x:Name="PopupShield" Background="Transparent" Width="Auto" 
      Height="Auto" Visibility="Collapsed"/> 
    </Grid> 
</UserControl> 

Sau đó, tôi đã thêm một phương pháp mở rộng cho lớp Popup, như thế này:

public static class PopupUtils 
{ 
    public static void MakeAutoDismissing(this Popup popup) 
    { 
     var shield = (App.Current.RootVisual as MainPage).PopupShield; 

     // Whenever the popup opens, deploy the shield 
     popup.HandlePropertyChanges(
      "IsOpen", 
      (s, e) => 
      { 
       shield.Visibility = (bool)e.NewValue 
        ? Visibility.Visible : Visibility.Collapsed; 
      } 
     ); 

     // Whenever the shield is clicked, dismiss the popup 
     shield.MouseLeftButtonDown += (s, e) => popup.IsOpen = false; 
    } 
} 

public static class FrameworkUtils 
{ 
    public static void HandlePropertyChanges(
     this FrameworkElement element, string propertyName, 
     PropertyChangedCallback callback) 
    { 
     //Bind to a depedency property 
     Binding b = new Binding(propertyName) { Source = element }; 
     var prop = System.Windows.DependencyProperty.RegisterAttached(
      "ListenAttached" + propertyName, 
      typeof(object), 
      typeof(UserControl), 
      new System.Windows.PropertyMetadata(callback)); 

     element.SetBinding(prop, b); 
    } 
} 

Các phương pháp mở rộng được sử dụng như thế này:

private void UserControl_Loaded(object sender, RoutedEventArgs e) 
{ 
    FilterPopup.MakeAutoDismissing(); 
} 
+0

Giải pháp của bạn đã hiệu quả đối với tôi. Nó sẽ là tốt hơn để tạo ra một giao diện với một tài sản duy nhất PopupShield và sử dụng điều này trong phương pháp MakeAutoDismissing. MainPage của bạn hoặc UserControl khác sẽ phải triển khai thuộc tính và trả về Canvas trong suốt. (Tại sao không đăng giải pháp của bạn như một câu trả lời để mọi người có thể bỏ phiếu cho nó?) – kgiannakakis

Trả lời

4

Bạn đã đặt màu nền trên RootVisual chưa?

+0

Ý nghĩ hay. Tôi sẽ thử vào ngày mai. – Jacob

+0

Khi tôi đã có một nền mờ, tôi bắt đầu nhận được các sự kiện chuột xuống. – Jacob

+0

Điều này làm việc như một sự quyến rũ đối với tôi cũng vậy, cảm ơn cho tip. –

5

Một cách là đặt điều khiển trên một khung trong suốt lấp đầy toàn bộ bề mặt Silverlight. Khi canvas được nhấp vào đóng khung và kiểm soát. Điều quan trọng là phải đảm bảo rằng cọ nền của canvas được đặt thành "Trong suốt" nếu bạn muốn nhận sự kiện chuột.

Một phương pháp thay thế mà tôi chưa thành công với việc sử dụng tính năng chụp chuột trong Silverlight và phát hiện khi chuột được nhấp bên ngoài cửa sổ bật lên.

+0

Tôi ước tôi cũng có thể chấp nhận câu trả lời này. Vấn đề chỉ với việc nắm bắt các sự kiện trên hình ảnh gốc là các điều khiển hậu duệ như các nút đã chặn các sự kiện chuột xuống. Vì vậy, tôi đã tạo một canvas cấp cao nhất sẽ nằm ngay bên dưới các điều khiển cửa sổ bật lên. Khi hiển thị cửa sổ bật lên, tôi làm cho "lá chắn" này hiển thị và làm cho nó bị sập khi cửa sổ bật lên bị loại bỏ. – Jacob

+0

Cảm ơn bạn đã cho tôi biết rằng câu trả lời đã giúp bạn. –

1

Trên lần nhấp đầu tiên, hãy gọi phương thức CaptureMouse() trên điều khiển. Sau đó gọi ReleaseMouseCapture() trên lần nhấp thứ hai.

+0

Tôi muốn làm một cái gì đó đơn giản này, nhưng tôi không thể làm cho nó hoạt động. Bạn có hướng dẫn cụ thể hơn về cách thực hiện điều này hoặc mã mẫu không? – Jacob

3

Tôi vừa tạo ra một cái gì đó tương tự và hy vọng điều này có thể giúp đỡ bất kỳ. :)

Trong điều khiển PopUp của tôi, tôi có Đường viền có Lưới có chứa một số hộp văn bản. Tôi đặt tên là 'PopUpBorder' của Border.

Trong constructor UserControl của tôi, tôi có,

 this.PopUpBorder.MouseLeave += (s, e) => 
     { 
      Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) => 
      { 
       this.PopUp.IsOpen = false; 
      }; 
     }; 

Và có vẻ như nó đang làm việc như mong đợi. Vui lòng cho tôi biết nếu điều này không hiệu quả trong trường hợp của bạn.

+0

Không phải là nó đăng ký vào cùng một sự kiện mỗi khi chuột rời khỏi đường viền bật lên? Có lẽ, nó kết quả sử dụng bộ nhớ không cần thiết? – synergetic

+0

có, bạn cần phải hủy đăng ký sự kiện –

+1

Tôi thực sự thích câu trả lời này vì sự đơn giản của nó. –

1

Giải pháp được đề xuất sử dụng khung che chắn đặc biệt để chặn đầu vào cho các điều khiển khác. Điều này là tốt, nhưng tôi đang cố gắng thực hiện một điều khiển chung và không thể phụ thuộc vào sự tồn tại của một khung khiên như vậy. Sự ràng buộc của sự kiện MouseLeftButtonDown trên hình ảnh gốc không hoạt động đối với tôi, bởi vì các nút hiện có trên canvas của tôi sẽ vẫn kích hoạt sự kiện nhấp của riêng chúng chứ không phải MouseLeftButtonDown trên hình ảnh gốc. Tôi tìm thấy giải pháp trong bài viết về băng này: Popup.StaysMở trong Silverlight từ Kent Boograart. Các phương pháp chính cho câu hỏi này là:

 private void OnPopupOpened(object sender, EventArgs e) 
     { 
      var popupAncestor = FindHighestAncestor(this.popup); 

      if (popupAncestor == null) 
      { 
       return; 
      } 

      popupAncestor.AddHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true); 
     } 

     private void OnPopupClosed(object sender, EventArgs e) 
     { 
      var popupAncestor = FindHighestAncestor(this.popup); 

      if (popupAncestor == null) 
      { 
       return; 
      } 

      popupAncestor.RemoveHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown); 
     } 

     private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
     { 
      // in lieu of DependencyObject.SetCurrentValue, this is the easiest way to enact a change on the value of the Popup's IsOpen 
      // property without overwriting any binding that may exist on it 
      var storyboard = new Storyboard() { Duration = TimeSpan.Zero }; 
      var objectAnimation = new ObjectAnimationUsingKeyFrames() { Duration = TimeSpan.Zero }; 
      objectAnimation.KeyFrames.Add(new DiscreteObjectKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero), Value = false }); 
      Storyboard.SetTarget(objectAnimation, this.popup); 
      Storyboard.SetTargetProperty(objectAnimation, new PropertyPath("IsOpen")); 
      storyboard.Children.Add(objectAnimation); 
      storyboard.Begin(); 
     } 
    private static FrameworkElement FindHighestAncestor(Popup popup) 
    { 
     var ancestor = (FrameworkElement)popup; 

     while (true) { 
      var parent = VisualTreeHelper.GetParent(ancestor) as FrameworkElement; 

      if (parent == null) { 
       return ancestor; 
      } 

      ancestor = parent; 
     } 
    } 

Tôi vẫn không chắc chắn, tại sao giải pháp này làm việc cho tôi và điều này không:

Application.Current.RootVisual.MouseLeftButtonDown += (s1, e1) => 
    { 
     this.PopUp.IsOpen = false; 
    }; 

Dường như các phương pháp FindHighestAncestor sẽ chỉ trả lại gốc trực quan? Nhưng sau đó sự khác biệt phải là xử lý sự kiện? tôi đoán sự khác biệt chính là tham số cuối cùng của phương pháp AddHandler đó là đúng trong trường hợp:

AddHandler(Windows.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true); 

Các tài liệu MSDN nói:

handledEventsToo

Loại: System.Boolean

đúng để đăng ký trình xử lý sao cho nó được gọi ngay cả khi sự kiện định tuyến được đánh dấu xử lý trong dữ liệu sự kiện của nó; sai để đăng ký trình xử lý với điều kiện mặc định là nó sẽ không được gọi là nếu sự kiện được định tuyến đã được đánh dấu đã xử lý. Mặc định này sai. Không thường xuyên yêu cầu để tái xử lý sự kiện được định tuyến. Để biết thêm thông tin, hãy xem Ghi chú.

0

Cách thay thế đơn giản hơn (mặc dù không chính xác những gì bạn yêu cầu) sẽ đóng cửa sổ bật lên trên MouseLeave. MouseLeave trên chính đối tượng Popup sẽ không hoạt động, nhưng MouseLeave trên vùng chứa mức cao nhất trong Popup.

0

tôi đã đăng giải pháp cho điều này trên blog của mình, bạn có thể xem bài đăng tại đây - Silverlight: close Popup on click outside. như bạn có thể thấy việc sử dụng rất đơn giản - đó là thuộc tính đính kèm bạn thêm vào cửa sổ bật lên của mình. bạn không phải thêm bất kỳ trình bao bọc nào, bạn không phải cẩn thận nếu bạn làm hoặc không có màu nền nào ... mã của tôi sẽ xử lý tất cả.

Các vấn đề liên quan