【问题标题】:Tray icon context menu positioning in WPF applicationWPF 应用程序中的托盘图标上下文菜单定位
【发布时间】:2012-02-12 18:57:13
【问题描述】:

我有一个 C# WPF .NET 4 应用程序,它在系统托盘中有一个图标。我目前正在使用经过充分讨论的WPF NotifyIcon,但我遇到的问题不依赖于这个控件。问题是 .NET 4 根本不允许(在大多数情况下)WPF ContextMenu 对象出现在 Windows 7 任务栏的顶部。这个例子完美地说明了这个问题。

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>

代码:

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

要查看问题,请确保托盘图标始终可见,而不是 Win7 托盘图标弹出内容的一部分。当您右键单击托盘图标时,上下文菜单会在任务栏上方打开。现在右键单击旁边的标准 Windows 托盘图标之一,看看有什么不同。

现在,左键单击图标并注意它确实允许在鼠标光标所在的右侧打开自定义弹出窗口。

选中“使用 WinForms...”复选框将切换应用程序以使用 Windows.Forms 程序集中的旧 ContextMenuStrip 上下文菜单。这显然会在正确的位置打开菜单,但它的外观与默认的 Windows 7 菜单不匹配。具体来说,行高亮是不同的。

我已经使用了 Horizo​​ntal 和 VerticalOffset 属性,并且使用“正确”值可以使上下文菜单一直弹出到屏幕的右下角,但这同样糟糕。它仍然永远不会在您的光标所在的位置打开。

真正的问题在于,如果您针对 .NET 3.5 构建相同的示例,它的工作方式与预期一样。不幸的是,我的实际应用程序使用了许多 .NET 4 功能,因此无法恢复。

有人知道如何使上下文菜单在光标所在的位置实际打开吗?

【问题讨论】:

  • 你能不能把所有的通知图标逻辑放在一个单独的程序集中,以 .NET 3.5 为目标,然后在你的实际项目中使用它?
  • 不幸的是,这不起作用。即使通知图标和上下文菜单存在于针对 3.5 的单独程序集中,问题仍然存在。看来只要可执行目标 4.0 就存在问题。
  • 不是解决问题的方法,但我会使用 WinForms 菜单。最终在一个单独的程序集中,在 WinForms 和 WPF 之间做出明确的划分。这样,NotifyIcon 的 Context-Menu 应该看起来像为普通应用设计的那样。

标签: wpf c#-4.0 contextmenu


【解决方案1】:

经过一番搜索,我偶然发现了这个question & answer。我从没想过要尝试NotifyIcon 上的ContextMenu 属性!虽然并不理想,但在 WPF 解决系统托盘是应用程序的有用部分这一事实之前,它会运行得很好。但是,失去 WPF ContextMenu 提供的所有绑定和命令路由功能真的很可惜。

不过,接受我自己的答案感觉不对,所以我打算再开放几天。

【讨论】:

    【解决方案2】:

    嗯,我很高兴没有将此标记为已回答,因为我找到了一个对我来说更好的选择。我发现this article 详细说明了如何将图标添加到 System.Windows.Forms.MenuItem 对象。现在只需一点代码,我就有了一个与系统上下文菜单完美匹配的菜单!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      • 2010-12-01
      • 2016-02-15
      相关资源
      最近更新 更多