1

好的,我已经尝试使用弹出窗口来让它工作,但是有很多原因表明这似乎不是我想要采取的路线......特别是因为我已经花了最后两个小时试图让它工作,我认为它比地狱更不圣洁(尽管事实上我在应用程序的其他地方有弹出窗口可以正常工作,但我离题了......)

基本上我只需要一个在 WPF 中似乎不是标准的开箱即用功能......我必须确定何时有人点击了已知 UI 元素以外的其他东西(即他们点击远离某物以关闭它...很像设置为 StaysOpen = false 的弹出窗口)

从我收集到的信息来看,这是一项相当艰巨的任务,我似乎无法找到最佳方法的直接答案……有什么想法吗?

编辑:

其中一位评论者希望我发布一些示例代码并重新阅读我的问题,我真的不想发布不相关的内容(XY 问题)。我发布这个问题有两个原因:

  1. 一旦弹出窗口打开,onmouseleave 事件就会被触发。这意味着如果弹出窗口设置为 'StaysOpen="False"',无论发生什么,弹出窗口都会出现并立即消失。我全心全意地相信,如果我创建一个使用 Visibility 属性出现和消失的组件,而不是将其放置在弹出窗口中,这将不是问题。我考虑弹出组件的唯一原因是因为它的 StaysOpen=False 功能,而不是因为它需要浮动在其他所有东西之上

  2. 弹出窗口本身感觉hacky,特别是因为它需要适合可视树中的父组件。正如您从下面的代码中看到的那样,我已经让弹出窗口适合它的父级......但我真的不喜欢将组件的宽度和高度绑定到另一个组件的实际宽度和高度。这是我想避免使用弹出窗口的第二个原因。

因此,虽然这个问题可能是“我怎样才能让弹出窗口工作”,但最初的问题仍然存在:“我怎样才能监听点击离开事件?” 我想创建一个在逻辑上适合可视化树的组件,其行为如下:

  1. 将鼠标悬停在组件上时,会出现
  2. 离开时组件消失
  3. 单击组件时会持续出现
  4. 单击远离组件或自身关闭时

我已经处理了上述所有问题,除了点击离开

4

2 回答 2

1

UIElement.LostFocus-Event怎么样?这似乎是你需要的。

于 2013-08-21T01:42:16.557 回答
0

我认为在这种情况下,您可以对路由事件进行有用的处理。有两种类型的事件:冒泡、直接和隧道。应注意BubblingTunneling。冒泡事件上升逻辑树并向下隧道。下面是这里的图表:

在此处输入图像描述

所以那个事件向上/向下树,应该在每个控件上设置。通常,演示冒泡事件,应用此示例:

XAML

<Window x:Class="DemoRoutedEvents.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" MouseUp="somethingClicked">

    <Grid MouseUp="somethingClicked">
        <StackPanel MouseUp="somethingClicked" Margin="0,0,10,0">
            <Label x:Name="btnClickMe" Content="Click Me!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="101,22,0,0" MouseUp="somethingClicked"/>
            <CheckBox x:Name="chkhandle" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="241,28,0,0" RenderTransformOrigin="-0.588,1.188"/>
            <ListBox x:Name="lstEvents" HorizontalAlignment="Left" Height="604" VerticalAlignment="Top" Width="416" Margin="29,66,0,0"/>
        </StackPanel>
    </Grid>    
</Window>

Code behind

public int eventCounter = 0;

private void somethingClicked(object sender, RoutedEventArgs e)
{
    eventCounter++;

    String message = "#" + eventCounter.ToString() + ":\r\n" +
            " Sender: " + sender.ToString() + ":\r\n" +
            " Source: " + e.Source + ":\r\n" +
            " Original Source: " + e.OriginalSource;

    lstEvents.Items.Add(message);
    e.Handled = (bool)chkhandle.IsChecked;

    if (e.Handled)
        lstEvents.Items.Add("Completed");

}

Output

在此处输入图像描述

我试图针对多个面板和组件优化此过程。我创建了一个附加的依赖属性IsDebugEvent,它属于EventBehaviours. 原理很简单,我们获取一个事件处理程序,并为该类型的所有元素Control(几乎所有它继承的 UIElements)设置它。对于 Grid、StackPanel、WrapPanel 等面板,Panel是基类。

在处理程序中,我们找到ListBox并显示面板的名称 s 导致事件的元素,仅用于测试。该示例使用事件PreviewMouseLeftButtonDown(隧道),因为第一次触发是在 的事件Button.Click,并且当它工作时,它与事件冲突MouseUp从这里引用:

ButtonBase 继承自 UIElement,Button 还可以访问为 UIElement 定义的所有鼠标按钮事件。因为 Button 会响应按钮按下而做一些事情,所以它会吞下冒泡事件(例如 MouseLeftButtonDown 和 MouseDown)。您仍然可以通过为隧道事件添加处理程序(例如 PreviewMouseLeftButtonDown 和 PreviewMouseDown)来检测这些较低级别的按钮按下事件。

XAML

<Window x:Class="AwayEventHelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AwayEventHelp"
    Title="MainWindow" Height="550" Width="525"
    WindowStartupLocation="CenterScreen">        

    <Grid>                
        <CheckBox Name="DebugCheckBox" Width="100" Height="30" 
              VerticalAlignment="Top"
              Content="Debug event" IsChecked="False" 
              Checked="DebugCheckBox_Checked" Unchecked="DebugCheckBox_Unchecked" />

        <StackPanel Name="LeftStackPanel" Width="150" local:EventBehaviours.IsDebugEvent="False"
                HorizontalAlignment="Left" Background="BlanchedAlmond">

            <Button Name="LeftButton1" Height="30" Content="LeftButton1" />
            <Button Name="LeftButton2" Height="30" Content="LeftButton2" />
            <Button Name="LeftButton3" Height="30" Content="LeftButton3" />

            <Label Name="JustLabelLeft" Content="JustLabelLeft" Background="Azure" HorizontalContentAlignment="Center" />
        </StackPanel>

        <StackPanel Name="RightStackPanel" Width="150" local:EventBehaviours.IsDebugEvent="False"
                HorizontalAlignment="Right" Background="Azure">

            <Button Name="RightButton1" Height="30" Content="RightButton1" />
            <Button Name="RightButton2" Height="30" Content="RightButton2" />
            <Button Name="RightButton3" Height="30" Content="RightButton3" />

            <Label Name="JustLabelRight" Content="JustLabelRight" Background="BlanchedAlmond" HorizontalContentAlignment="Center" />
        </StackPanel>

        <Grid Name="GridPanel" Width="100" Height="100" local:EventBehaviours.IsDebugEvent="False"
              VerticalAlignment="Bottom" Background="CadetBlue">

            <Label Name="LabelInGrid" Width="100" Height="50" Content="LabelInGrid" Background="AliceBlue" />
        </Grid>

        <ListBox Name="EventOutput" Width="180" Height="180" Background="AliceBlue" />
    </Grid>
</Window>

Code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void DebugCheckBox_Checked(object sender, RoutedEventArgs e)
    {
        EventBehaviours.SetIsDebugEvent(LeftStackPanel, true);
        EventBehaviours.SetIsDebugEvent(RightStackPanel, true);
        EventBehaviours.SetIsDebugEvent(GridPanel, true);
    }

    private void DebugCheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
        EventBehaviours.SetIsDebugEvent(LeftStackPanel, false);
        EventBehaviours.SetIsDebugEvent(RightStackPanel, false);
        EventBehaviours.SetIsDebugEvent(GridPanel, false);
    }
}

public class EventBehaviours : DependencyObject
{
    #region IsDebugEvent declaration

    public static void SetIsDebugEvent(DependencyObject target, bool value)
    {
        target.SetValue(IsDebugEventProperty, value);
    }

    public static bool GetIsDebugEvent(DependencyObject DepObject)
    {
        return (bool)DepObject.GetValue(IsDebugEventProperty);
    }

    public static readonly DependencyProperty IsDebugEventProperty =
                                              DependencyProperty.RegisterAttached("IsDebugEvent",
                                              typeof(bool),
                                              typeof(EventBehaviours),
                                              new UIPropertyMetadata(false, OnIsDebugEvent));

    #endregion

    private static void OnIsDebugEvent(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        Panel MyPanel = sender as Panel;            

        if (e.NewValue is bool && ((bool)e.NewValue == true))
        {
            MyPanel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);

            if (MyPanel.Children.Count != 0)
            {
                foreach (Control MyControl in MyPanel.Children)
                {
                    MyControl.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
                }
            }
        }
        else
        {
            foreach (Control MyControl in MyPanel.Children)
            {
                MyControl.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
            }

            MyPanel.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
        }
    }

    /// <summary>
    /// Main handler of PreviewMouseLeftButtonDown event
    /// </summary>
    private static void MyControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        string OutInfo = string.Empty;

        if (sender.GetType() == typeof(StackPanel))
        {
            StackPanel MyStackPanel = sender as StackPanel;
            Grid MyGrid = MyStackPanel.Parent as Grid;

            OutInfo = "PanelName: " + MyStackPanel.Name;
            OutInfoInListBox(MyGrid, OutInfo);
        }
        else if (sender.GetType() == typeof(Grid))
        {
            Grid MyGrid = sender as Grid;
            Grid MyMainGrid = MyGrid.Parent as Grid;

            OutInfo = "PanelName: " + MyGrid.Name;
            OutInfoInListBox(MyMainGrid, OutInfo);
        }
        else
        {
            Control MyControl = sender as Control;
            Panel MyStackPanel = MyControl.Parent as Panel;
            Grid MyGrid = MyStackPanel.Parent as Grid;

            OutInfo = "ControlName: " + MyControl.Name;
            OutInfoInListBox(MyGrid, OutInfo);
        }
    }

    /// <summary>
    /// Get ListBox and insert some info
    /// </summary>
    /// <param name="ParentGrid">Panel, where locate ListBox</param>
    /// <param name="info">Just string</param>
    private static void OutInfoInListBox(Grid ParentGrid, string info) 
    {
        ListBox MyEventOutput = ParentGrid.FindName("EventOutput") as ListBox;
        MyEventOutput.Items.Add(info);
    }
}

Output

在此处输入图像描述

通过单击,在主题中CheckBox设置依赖属性,从而导致我们设置处理程序的位置。如果取消选择in,则删除所有事件处理程序。IsDebugEventTrueOnIsDebugEventCheckBox

要在启动时立即设置事件,您需要确保成功启动上的所有项目。这可以在 的情况下ContentRendered完成Window

于 2013-08-22T18:10:02.513 回答