如何检测在winform控件外单击鼠标的时间? [英] How to detect when mouse is clicked outside a winform control?

查看:54
本文介绍了如何检测在winform控件外单击鼠标的时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的表单中有一个FlowLayoutPanel控件,一个用于添加PictureBox的UserControl类。

我想显示文件夹中的一些图像,当我点击图像时,它的边框将会改变当我点击它外面时,它的边框会再次改变。





http://postimg.org/image/pt3qngulx/ [ ^ ]





这是我的UserControl类中的一些代码



I have a FlowLayoutPanel control in my form , an UserControl class to add a PictureBox.
I want to display some images from a folder, and when I click on an image it's border will be change , when I click outside of it , it's border is change again.


http://postimg.org/image/pt3qngulx/[^]


Here some code in my UserControl class

public bool clicked;
public MyImageViewer()
{
    this.MouseClick += Control_MouseClick;
    this.ControlAdded += MyImageViewer_ControlAdded;

    InitializeComponent();
}

void MyImageViewer_ControlAdded(object sender, ControlEventArgs e)
{
    e.Control.MouseClick += Control_MouseClick;
}

void Control_MouseClick(object sender, MouseEventArgs e)
{
    this.BackColor = Color.LightBlue;
    this.clicked = true;
}

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    if (clicked)
    {
        ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, Color.Blue,                                     ButtonBorderStyle.Solid);
    }

}







但是如何检测鼠标是否在我的usercontrol外部被点击?




But how can I detect the mouse is clicked outside of my usercontrol?

推荐答案

那么,您应该考虑检测控件何时变为非活动状态(松散焦点)。



您无法直接检测到控件外的点击,因为事件始终指向被点击的控件。



好吧,我认为如果应用程序抓取鼠标捕获,你可以检测应用程序内部的点击,并且当应用程序外部点击时,如果它在另一个应用程序被激活时变为非活动状态,则可以间接检测到点击(但你不能确切地知道它是一个点击(而不是Alt + Tab)。



实际上,检测焦点/激活变化应该绰绰有余。为什么你会保持边界,如果另一个使用键盘激活Windows,然后在该应用程序中不使用鼠标?对于你的用户而言,在使用键盘的同时使用另一个应用程序来激活它会不会直观。



好​​吧,可能会有一些技巧在任何地方检测点击(透明窗口,系统挂钩......)但是你真的不想走这条路,如果你不需要,因为你的应用程序可能会对系统的稳定性产生重大影响。



更新

要修改控件样式,可以使用 Control.SetStyle https:// msdn .microsoft.com / zh-cn / library / system.windows.forms.control.setstyle(v = vs.110).aspx [ ^ ])。



可选择 https://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles(v = vs.110) .aspx [ ^ ])可能是你要改变的那个。



那么你考虑使用一个清单吗?查看或一些类似的控制。对于你的问题,这可能是一个更好的解决方案。



或者,如果你想要在显示视图的方式上有很多控制权,托管WPF控件可能会给最好的结果。
Well, you should consider detecting when the control become inactive (loose the focus).

You cannot directly detect a click outside the control since event are always directed toward the control that was clicked.

Well, I think that you can detect click inside your application if the application grab mouse capture and you indirectly detect a click outside the application as it become inactive when another application is activated (but you won't reliably know for sure that it was a click (and not Alt+Tab for example).

In practice, however detecting focus/activation change should be more than enough. Why would you keep the border if another Windows is activated using the keyboard and then the mouse is not used in that application? It would not be intuitive to your user that cliking on another application to activate it would remove the norder while using the keyboard would not.

Well, there could be tricks to detect clicks anywhere (transparent window, system hook...) but you really don't want to go that road if you don't have to as your application could then have a major impact on the stability of the system.

Update
To modify control style, you can use Control.SetStyle (https://msdn.microsoft.com/en-us/library/system.windows.forms.control.setstyle(v=vs.110).aspx[^]).

Selectable (https://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles(v=vs.110).aspx[^]) would probably be the one you want to change.

By the way, have you consider using a list view or some similar control. It might be a better solution for your problem.

Alternatively, if you want lot of control on the way the view is displayed, hosting a WPF control would probably give the best result.


我假设你正在用它内部的PictureBox创建一个UserControl,并用UserControl的实例填充TableLayoutPanel的Cells。



我假设你这样做是因为你发现当你想要表明它是当前选中的PictureBox时,只将PictureBox放入TableLayoutPanel Cell会产生令人不满意的视觉效果......因为当PictureBox停靠时在Cell中,你设置它的BorderStyle:它看起来像废话。



而且:当你在TableLayoutCell中拥有UserControl时,你无法设计-time ...在使用UserControl的Form中...访问UserControl中的PictureBox,为它设置Click EventHandler。



如果这些假设是正确的,这里有一种方法可以让你以一致的方式看到活动边框效果:



I.在UserControlL中:创建一个Click EventHandler for PictureBox,并在该Handler中调用Action类型的简单委托:
I assume you are creasting a UserControl with a PictureBox inside it, and filling the Cells of a TableLayoutPanel with instances of the UserControl.

I assume you are doing this because you've found that putting only a PictureBox into TableLayoutPanel Cell gives an unsatisfactory visual result when you want to indicate it is the currently selected PictureBox ... because when the PictureBox is docked inside the Cell, and you set its BorderStyle: it looks like crap.

And, also: when you have the UserControl in a TableLayoutCell, you have no way at design-time ... in the Form where the UserControl is used ... to access the PictureBox in the UserControl to set a Click EventHandler for it.

If those assumptions are correct, here's one way you can get the "active border" visible in a consistent way effect:

I. In the UserControlL: create a Click EventHandler for the PictureBox, and in that Handler call a simple delegate of type Action:
public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public Action<UserControl1> Clicked;

    private void pictureBox1_Click(object sender, EventArgs e)
    {
        Clicked(this);
    }
}

这里我们创建一个公开公开的可执行方法(委托)。



II。在带有TableLayoutPanel的Form中,我们将把我们想要的特定可执行代码注入TableLayoutPanel中的UserControl的每个实例:

Here we are creating an executable method (delegate) that is exposed publicly.

II. In the Form with the TableLayoutPanel we will inject the specific executable code we want into each instance of the UserControl in the TableLayoutPanel:

private void Form1_Load(object sender, EventArgs e)
{
    foreach (UserControl1 uc1 in tableLayoutPanel1.Controls)
    {
        uc1.Clicked = PictureBox_Click;
    }
}

III。所以现在我们可以定义我们想要处理的代码,突出显示活动(单击)UserControl:

III. So now we can define the code we want to handle highlighting the active (clicked) UserControl:

private UserControl1 lastPictureBoxClicked = null;
private UserControl1 currentPictureBoxClicked = null;

private void PictureBox_Click(UserControl1 uc1)
{
    if (lastPictureBoxClicked != null) lastPictureBoxClicked.BackColor = Color.GhostWhite;

    currentPictureBoxClicked = uc1;

    currentPictureBoxClicked.BackColor = Color.Red;

    lastPictureBoxClicked = currentPictureBoxClicked;
}

你也可以在UserControl代码中公开PictureBox控件(通过公开引用它),这意味着你可以在你使用的Form中为它定义一个EventHandler TableLayoutPanel。并且,如果您需要在使用PictureBox的应用程序中运行时执行其他操作,那么这可能是一个好主意。



在此示例中,我选择了不公开PictureBox控件,但只暴露一种让它引发事件的方法。

You could also just expose the PictureBox Control (by making a Public reference to it available) in UserControl code, which would mean you could define an EventHandler for it in the Form where you use the TableLayoutPanel. And, if you need to do other things at run-time in your App with the PictureBox, then that may be a good idea.

In the example here I chose not to expose the PictureBox Control, but only expose a way to have it raise an Event.


一个小问题是表单通常被其他控件覆盖,表单的子节点,所以它不会发送鼠标事件(顺便说一句,与键盘事件相反)。所以,你可以人为地将一些其他事件冒泡到表单中。这很容易做到。



为简单起见,我们假设您只在一个类中编写所有相关代码,一些表单上课。创建一些事件参数类,派生自 System.EventArgs 。它需要的主要属性是对控件的引用,该控件是原始事件目标(鼠标单击的控件)。在窗体中添加一个控件实例。



在窗体的所有控件上订阅一些鼠标事件。这是通过递归遍历所有控件并将处理程序添加到每个实例来完成的。最好订阅 MouseDown 而不是点击,因为点击 - 惊喜! - 不是鼠标事件;它是由鼠标或键盘调用的更高级别的逻辑事件。在此事件处理程序中,对于所有这些控件都是通用的,请调用上一段中描述的事件。将处理事件的第一个事件参数(sender)传递给调用事件的原始事件目标属性。换句话说,您从处理的鼠标事件中获取发件人,并将此数据重定向到您在表单上调用的事件实例。



这样,添加了任何处理程序到你定义的表单事件的调用列表将获得一个通知,即在某些子控件上甚至调用了鼠标,并告诉你什么是子控件。然后将此子控件的引用与您问题中对用户控件的引用进行比较。如果这些是不同的对象,则表示用户在用户控件外部单击。很简单,不是吗。



我的意思是,这很简单。请尝试编写这个简单的代码;它只是几行。但如果某些事情不清楚或似乎很难,请提出您的问题;然后我会告诉你更多细节。请具体说明。但我希望你能马上做到。



让我们考虑一个简单的例子:







首先,让我们定义一些事件参数类:

One little problem is that a form is normally covered by other controls, children of the form, so it won't get mouse events dispatched to it (in contrast to keyboard events, by the way). So, you can artificially "bubble" some other event to the form. This is pretty easy to do.

For simplicity, let's assume you write all the relevant code in just one class, some Form class. Create some event arguments class, derived from System.EventArgs. Main property it needs is the reference to the control which was the original event target (the control a mouse clicks on). Add a control instance to your form.

Subscribe to some mouse event on all the control of the form. This is done by recursively traversing all the control and adding the handler to each instance. It's better to subscribe to MouseDown not Click, because Click — surprise! — is not a mouse event; it is a higher-level "logical" event invoked by either mouse or keyboard. In this event handler, common for all those controls, invoke the event defined as described in the previous paragraph. Pass the first event argument ("sender") of the handled event to the "original event target" property of the invoked event. In other words, you get a sender from a mouse event you handle and redirect this data to the event instance you invoke on the form.

This way, any handler added to the invocation list of the forms event you define will get a notification that on some child control the mouse even was invoked, and tell you on what child control. Then compare the reference of this child control with the reference to the user control in your question. If these are different object, it mean that the user clicked outside of your user control. Simple, isn't it.

I mean, it's simple. Please try to write this simple code; it will be just few lines. But if something is unclear or seems to be difficult, please ask your questions; then I'll show you more detail. Please be specific. But I hope you can do it right away.

Let's consider one simple example:



First, let's define some event arguments class:
class ClickFormControlEventArgs : System.EventArgs {
    internal ClickFormControlEventArgs(Control originalTarget) {
        this.OriginalTarget = originalTarget;
    }
    internal Control OriginalTarget { get; private set; }
}



现在,表格,任何形式,主要与否:


Now, the form, any form, main or not:

public partial class MainForm : Form {

    void AddMouseDownEventHandler(Control control, MouseEventHandler handler) {
        control.MouseDown += handler;
        foreach (Control child in control.Controls)
            AddMouseDownEventHandler(child, handler);
    } //AddClickEventHandlerToChildren

    public MainForm() {
        InitializeComponent();
        Text = Application.ProductName;
        // to all invocation lists of the MouseDown event instance
        // of all the child controls, recursively:
        AddMouseDownEventHandler(this, (sender, eventArgs) => {
            if (FormControlClicked != null) {
                // we can be sure that sender is always control
                Control control = (Control)sender;
                FormControlClicked.Invoke(
                    sender,
                    new ClickFormControlEventArgs(control));
            } //if
        });
    } //MainForm

    internal event System.EventHandler<ClickFormControlEventArgs>
        FormControlClicked;

    // ...

} //class MainForm





现在,添加到表单的偶数实例 FormControlClicked 的调用列表中的每个事件处理程序都会收到一些通知。对于一般示例,我将在表单类之外显示一些代码,假设我们拥有带有此事件的表单实例。如果你在表单类中执行它,它可能是一些方法,其中表单的实例是this(而不是下面代码中的form)。





Now, each event handler added to the invocation list of the form's even instance FormControlClicked will get notification of some. For general example, I'll show some code outside the form class, assuming that we have the instance of the form with this event. In case you do it inside the form class, it could be some method where the instance of the form is "this" (instead of "form" in the code below).

Control control = //...; // some control we want to check up
Form form = //... "this", if this code withing the call.

// now, pay attention that we handled <code>MouseDown</code> on all the controls
// on the form, including the form itself; so we can check it up:

form.FormControlClicked += (sender, eventArgs) => {
   if (eventArgs.OriginalTarget == control) {
      // mouse was pressed down on this very control 
   } else if (eventArgs.OriginalTarget == form) {
      // mouse was pressed down on the form;
      // not on any of the form's children,
      // but on some form client area free from controls
   } else {
      // mouse was pressed on the form
      // but outside the control in question 
   }
}





现在,看看Philippe Mori的反对意见,他们关注所有这些控件上其他事件的常规事件处理中断,相对于此更高级别的MouseDown 。不,这不会是一个问题,因为您从未设置事件处理程序,也从不阻止其他事件处理程序或事件传播。其他事件处理程序将执行其通常的工作,不同的事件处理程序将不会干涉。



-SA



Now, look at the objections by Philippe Mori who concerned about disruption in usual event handling of other events on all those controls, higher-level relative to this MouseDown. No, it won't be a problem, because you never set an event handler and never block other event handler or event propagation. Other event handler will do their usual work and different event handlers won't interfere.

—SA


这篇关于如何检测在winform控件外单击鼠标的时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆