简体   繁体   English

单击NotifyIcon(任务栏图标)切换C#窗口

[英]C# toggle window by clicking NotifyIcon (taskbar icon)

My C# application consists of a taskbar icon (NotifyIcon) and an overhead window initially hidden. 我的C#应用​​程序包含一个任务栏图标(NotifyIcon)和一个最初隐藏的开销窗口。 I want the user to be able to toggle the window visibility by clicking on the NotifyIcon (left, single click). 我希望用户能够通过单击NotifyIcon(左键单击)来切换窗口可见性。 Also the window is being hidden when loosing focus. 失去焦点时窗口也被隐藏。

This is what I have so far, a subclassed System.Windows.Forms.Form : 这是我到目前为止,一个子类System.Windows.Forms.Form

Initialization: 初始化:

this.ControlBox = false;
this.ShowIcon = false;
this.ShowInTaskbar = false;

// Instance variables: bool allowVisible;
//                     System.Windows.Forms.NotifyIcon notifyIcon;

this.allowVisible = false;
this.notifyIcon = new NotifyIcon();
this.notifyIcon.MouseUp += new MouseEventHandler(NotifyIconClicked);
this.Deactivate += new EventHandler(HideOnEvent);

Instance methods: 实例方法:

private void NotifyIconClicked(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        if (this.Visible)
            this.Hide();
        else
            this.Show();
    }
}

new public void Show()
{
    this.allowVisible = true;
    this.Visible = true;
    this.Activate();
}

new public void Hide()
{
    this.allowVisible = false;
    this.Visible = false;
}

private void HideOnEvent(object sender, EventArgs e)
{
    this.Hide();
}

protected override void SetVisibleCore(bool visible)
{
    base.SetVisibleCore(this.allowVisible ? visible : this.allowVisible);
}

Clicking the icon reveals the window like it should. 单击该图标会显示窗口。 But clicking it again hides it for as long as the mouse is being pressed, then resets it to visible. 但是,只要按下鼠标,再次单击它会隐藏它,然后将其重置为可见。

My guess is that the mouse down event steals the focus from the window so it disappears. 我的猜测是,鼠标按下事件会从窗口中窃取焦点,因此它会消失。 Then the mouse up event is triggered, showing the window as it is hidden. 然后触发鼠标按下事件,显示隐藏的窗口。

My next idea was to read the window visibility at mouse down event, so I tested three events and logged the UNIX time as they are called: 我的下一个想法是在鼠标按下事件时读取窗口可见性,因此我测试了三个事件并记录了UNIX时间,因为它们被调用:

notifyIcon.MouseDown
notifyIcon.MouseUp
this.LostFocus

The result is pretty weird: Let's say the window is visible. 结果很奇怪:假设窗口是可见的。 This happens when I click the icon: Focus lost is called immediately. 单击图标时会发生这种情况:立即调用焦点丢失。 Mouse down is called as soon as I release the mouse , right before the mouse up event. 在鼠标向上事件之前释放鼠标时立即调用鼠标按下。

1312372231 focus lost
1312372235 mouse down
1312372235 mouse up

Why is the mouse down event delayed? 为什么鼠标停止事件会延迟?
How can I toggle the window? 如何切换窗口?

I think this may work for you. 我认为这可能对你有用。

I found an expert exchange post which contains a class which provides a method for checking whether the cursor is currently over the tray. 我找到了一个专家交换帖子,其中包含一个类,该类提供检查光标当前是否在托盘上方的方法。

NotifyIcon - Detect MouseOut NotifyIcon - 检测MouseOut

Using this class I modified your HideOnEvent method like so: 使用这个类我修改了你的HideOnEvent方法,如下所示:

    private void HideOnEvent(object sender, EventArgs e)
    {
        if (!WinAPI.GetTrayRectangle().Contains(Cursor.Position))
        {
            this.Hide();
        }
    }

Which seems to do what you need. 这似乎做你需要的。

I have included the class below: 我把下面的课程包括在内:

using System.Runtime.InteropServices;
using System.Drawing;

public class WinAPI
{
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;

        public override string ToString()
        {
            return "(" + left + ", " + top + ") --> (" + right + ", " + bottom + ")";
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);


    public static IntPtr GetTrayHandle()
    {
        IntPtr taskBarHandle = WinAPI.FindWindow("Shell_TrayWnd", null);
        if (!taskBarHandle.Equals(IntPtr.Zero))
        {
            return WinAPI.FindWindowEx(taskBarHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero);
        }
        return IntPtr.Zero;
    }

    public static Rectangle GetTrayRectangle()
    {
        WinAPI.RECT rect;
        WinAPI.GetWindowRect(WinAPI.GetTrayHandle(), out rect);
        return new Rectangle(new Point(rect.left, rect.top), new Size((rect.right - rect.left) + 1, (rect.bottom - rect.top) + 1));
    }
}

It is not a perfect solution but I hope this helps. 这不是一个完美的解决方案,但我希望这会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM