简体   繁体   English

WPF应用程序中的托盘图标上下文菜单定位

[英]Tray icon context menu positioning in WPF application

I have a C# WPF .NET 4 application that has an icon in the system tray. 我有一个C#WPF .NET 4应用程序,该应用程序在系统任务栏中有一个图标。 I am currently using the well-discussed WPF NotifyIcon , but the problem I am having is not dependent on this control. 我目前正在使用讨论充分的WPF NotifyIcon ,但是我遇到的问题并不取决于此控件。 The problem is that .NET 4 simply does not allow (for the most part) a WPF ContextMenu object to appear over the top of the Windows 7 taskbar. 问题在于,.NET 4完全不允许(大多数情况下)WPF ContextMenu对象出现在Windows 7任务栏的顶部。 This example illustrates the problem perfectly. 这个例子很好地说明了这个问题。

XAML: XAML:

<Window x:Class="TrayIconTesting.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="100" Width="400">

    <Window.Resources>
        <ContextMenu x:Key="TrayContextMenu" Placement="MousePoint">
            <MenuItem Header="First Menu Item" />
            <MenuItem Header="Second Menu Item" />
        </ContextMenu>
        <Popup x:Key="TrayPopup" Placement="MousePoint">
            <Border Width="100" Height="100" Background="White" BorderBrush="Orange" BorderThickness="4">
                <Button Content="Close" Click="ButtonClick"></Button>
            </Border>
        </Popup>
    </Window.Resources>

    <StackPanel Orientation="Horizontal">
        <Label Target="{Binding ElementName=UseWinFormsMenu}" VerticalAlignment="Center">
            <AccessText>Use WinForms context menu for tray menu:</AccessText>
        </Label>
        <CheckBox Name="UseWinFormsMenu" IsChecked="False" Click="UseWinFormsMenuClicked" VerticalAlignment="Center" />
    </StackPanel>
</Window>

Code: 码:

using System.Drawing;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using ContextMenu = System.Windows.Controls.ContextMenu;

namespace TrayIconTesting
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ContextMenuStrip winFormsContextMenu;

        public MainWindow()
        {
            InitializeComponent();

            this.TrayIcon = new NotifyIcon
            {
                Icon = new Icon("Bulb.ico"),
                Visible = true
            };

            this.TrayIcon.MouseClick += (sender, args) =>
                                        {
                                            switch (args.Button)
                                            {
                                                case MouseButtons.Left:
                                                    this.TrayPopup.IsOpen = true;
                                                    break;

                                                case MouseButtons.Right:
                                                    if (!this.UseWinFormsMenu.IsChecked.GetValueOrDefault())
                                                    {
                                                        this.TrayContextMenu.IsOpen = true;
                                                    }
                                                    break;
                                            }
                                        };
        }

        private void ButtonClick(object sender, RoutedEventArgs e)
        {
            this.TrayPopup.IsOpen = false;
        }

        private void UseWinFormsMenuClicked(object sender, RoutedEventArgs e)
        {
            this.TrayIcon.ContextMenuStrip = this.UseWinFormsMenu.IsChecked.GetValueOrDefault() ? this.WinFormsContextMenu : null;
        }

        private ContextMenu TrayContextMenu
        {
            get
            {
                return (ContextMenu)this.FindResource("TrayContextMenu");
            }
        }

        private Popup TrayPopup
        {
            get
            {
                return (Popup)this.FindResource("TrayPopup");
            }
        }

        private NotifyIcon TrayIcon
        {
            get;
            set;
        }

        private ContextMenuStrip WinFormsContextMenu
        {
            get
            {
                if (this.winFormsContextMenu ==  null)
                {
                    this.winFormsContextMenu = new ContextMenuStrip();
                    this.winFormsContextMenu.Items.AddRange(new[] { new ToolStripMenuItem("Item 1"), new ToolStripMenuItem("Item 2") });
                }
                return this.winFormsContextMenu;
            }
        }
    }
}

To see the problem make sure that the tray icon is always visible and not part of that Win7 tray icon popup thing. 要查看该问题,请确保托盘图标始终可见,并且不属于该Win7托盘图标弹出窗口。 When you right click on the tray icon the context menu opens ABOVE the taskbar. 右键单击任务栏图标时,上下文菜单在任务栏上方打开。 Now right click one of the standard Windows tray icons next to it and see the difference. 现在,右键单击旁边的标准Windows托盘图标之一,然后查看区别。

Now, left click on the icon and notice that it DOES allow a custom popup to open right where the mouse cursor is. 现在,在图标上单击鼠标左键,注意它确实允许自定义弹出窗口在鼠标光标所在的位置打开。

Checking the "Use WinForms..." checkbox will switch the app to use the old ContextMenuStrip context menu in the Windows.Forms assembly. 选中“使用WinForms ...”复选框将切换该应用程序以使用Windows.Forms程序集中的旧ContextMenuStrip上下文菜单。 This obviously opens the menu in the correct place, but its appearance doesn't match the default Windows 7 menus. 显然,这可以在正确的位置打开菜单,但是其外观与默认的Windows 7菜单不匹配。 Specifically, the row highlighting is different. 具体而言,行突出显示是不同的。

I have played with the Horizontal and VerticalOffset properties, and with the "right" values you can make the context menu popup all the way at the bottom right of the screen, but this is just as bad. 我已经使用了Horizo​​ntal和VerticalOffset属性,并使用“ right”值可以使上下文菜单一直弹出到屏幕的右下角,但这同样很糟糕。 It still never opens where your cursor is. 它仍然永远不会在光标所在的位置打开。

The real kicker is that if you build this same sample targeting .NET 3.5 it works just as expected. 真正的好处是,如果您针对.NET 3.5构建相同的示例,则它可以按预期运行。 Unfortunately, my real application uses many .NET 4 features, so reverting back is not an option. 不幸的是,我的实际应用程序使用了许多.NET 4功能,因此不可以选择还原。

Anyone have any idea how to make the context menu actually open where the cursor is? 任何人都不知道如何使上下文菜单实际上在光标所在的位置打开吗?

After a little more searching I stumbled across this question & answer . 经过更多搜索后,我偶然发现了这个问题和答案 I never thought to try the ContextMenu property on the NotifyIcon ! 我从没想过要在NotifyIcon上尝试ContextMenu属性! While not ideal it will work well enough until WPF address the fact that the system tray is a useful part of applications. 尽管不是很理想,但在WPF解决了系统托盘是应用程序有用部分这一事实之前,它已经足够好了。 It will really be a shame to lose all the binding and command routing features provided by the WPF ContextMenu though. 但是,如果丢失WPF ContextMenu提供的所有绑定和命令路由功能,那真是可耻的。

It feels wrong to accept my own answer though, so I'm going to leave this open for a few more days. 但是,接受我自己的答案是错误的,因此我将再开放几天。

Well, I'm glad didn't mark this as answered because I found a slightly better option for me. 好吧,我很高兴没有将此标记为已回答,因为我为我找到了一个更好的选择。 I found this article that details how to add icons to the System.Windows.Forms.MenuItem object. 我发现本文详细介绍了如何向System.Windows.Forms.MenuItem对象添加图标。 Now with just a little code I have a menu that perfectly matches system context menus! 现在,仅需少量代码,我便有了一个与系统上下文菜单完全匹配的菜单!

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

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