当用户点击当前元素或子元素时获取事件

时间:2013-08-21 01:18:28

标签: c# .net wpf .net-4.5

好吧,我曾尝试使用弹出窗口来实现这一点,但有很多理由说明为什么这似乎不是我想采取的路线...特别是因为我花了最后两个小时试图让它工作,我认为它比所有地狱更不合适(尽管事实上我在应用程序的其他地方有弹出窗口工作得很好,但我离题了......)

基本上我只需要一个在WPF中看起来不是标准的标准功能......我必须确定有人点击其他东西而不是已知的UI元素(他们点击了IE的某些东西)关闭它...很像设置为StaysOpen = false的弹出窗口

从我收集的内容来看,这是一项相当艰巨的任务,我似乎无法找到最佳方法的直接答案......任何想法都可以吗?

修改

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

  1. 弹出窗口打开后会立即触发onmouseleave事件。这意味着如果弹出窗口设置为'StaysOpen =“False”'弹出窗口出现并立即消失,无论如何。我全心全意地相信,如果我创建一个使用Visibility属性出现的组件出现并消失而不是将其放在弹出窗口中,这将不会成为问题。我认为弹出组件的唯一原因是因为它的StaysOpen = False功能,而不是因为它需要浮动在其他所有内容

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

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

    1. 将鼠标悬停在某个组件上时,显示
    2. 离开时,组件消失
    3. 点击暂停显示的组件
    4. 点击远离组件或其自身关闭
    5. 除了点击之外,我已完成上述所有操作

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

我认为在这种情况下,您可以成为有用的路由事件。有两种类型的事件:冒泡,直接和隧道。应该注意BubblingTunneling。冒泡事件在逻辑树上升起并向下挖掘。以下是here

的图表

enter image description here

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

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

enter image description here

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

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

  

ButtonBase继承自UIElement,Button也可以访问为UIElement定义的所有鼠标按钮事件。因为按钮按下按钮会执行某些操作,因此会吞下冒泡事件(例如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

enter image description here

点击CheckBox,在IsDebugEvent中设置依赖项属性 True ,主题因此导致OnIsDebugEvent,我们设置处理程序。如果取消选择CheckBox,则会删除所有事件处理程序。

要在启动时立即设置事件,您需要确保成功启动所有项目。这可以在Window的{​​{3}}事件中完成。