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

发布于 2024-11-27 13:47:26 字数 1735 浏览 2 评论 0原文

我的 C# 应用程序由一个任务栏图标 (NotifyIcon) 和一个最初隐藏的顶部窗口组成。我希望用户能够通过单击 NotifyIcon(左键单击)来切换窗口可见性。当失去焦点时,窗口也会被隐藏。

这是我到目前为止所拥有的,一个子类System.Windows.Forms.Form

初始化:

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);

实例方法:

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);
}

单击图标会显示应有的窗口。但是,只要按下鼠标,再次单击它就会隐藏它,然后将其重置为可见。

我的猜测是,鼠标按下事件窃取了窗口的焦点,因此它消失了。然后触发鼠标向上事件,显示隐藏的窗口。

我的下一个想法是读取鼠标按下事件时的窗口可见性,因此我测试了三个事件并记录了它们被调用时的 UNIX 时间:

notifyIcon.MouseDown
notifyIcon.MouseUp
this.LostFocus

结果非常奇怪:假设窗口是可见的。当我单击图标时会发生这种情况:立即调用焦点丢失。一旦我释放鼠标,鼠标按下就会被调用,就在鼠标弹起事件之前。

1312372231 focus lost
1312372235 mouse down
1312372235 mouse up

为什么鼠标按下事件会延迟?
如何切换窗口?

My C# application consists of a taskbar icon (NotifyIcon) and an overhead window initially hidden. I want the user to be able to toggle the window visibility by clicking on the NotifyIcon (left, single click). Also the window is being hidden when loosing focus.

This is what I have so far, a subclassed 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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

失而复得 2024-12-04 13:47:26

我想这可能对你有用。

我发现一个专家交流帖子,其中包含一个类,该类提供了一种检查光标当前是否位于托盘上的方法。

NotifyIcon - 检测 MouseOut

使用此类我修改了您的 HideOnEvent 方法,例如所以:

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

这似乎可以满足您的需要。

我已经包含了以下课程:

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));
    }
}

这不是一个完美的解决方案,但我希望这会有所帮助。

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

Using this class I modified your HideOnEvent method like so:

    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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文