Расположение контекстного меню иконки в трее в приложении WPF - PullRequest
1 голос
/ 12 февраля 2012

У меня есть приложение 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 ...» переключит приложение на использование старого контекстного меню ContextMenuStrip в сборке Windows.Forms. Это, очевидно, открывает меню в правильном месте, но его внешний вид не соответствует стандартным меню Windows 7. В частности, выделение строк отличается.

Я поиграл со свойствами Horizontal и VerticalOffset, и с помощью "правильных" значений вы можете сделать контекстное меню всплывающим полностью в правом нижнем углу экрана, но это так же плохо. Он так и не открывается там, где находится ваш курсор.

Настоящим козырем является то, что если вы создаете этот же пример для .NET 3.5, он работает так, как ожидалось. К сожалению, мое реальное приложение использует много функций .NET 4, поэтому возврат назад невозможен.

Кто-нибудь знает, как сделать контекстное меню действительно открытым там, где находится курсор?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2012

Ну, я рад, что не пометил это как ответившее, потому что нашел немного лучший вариант для меня.Я нашел эту статью , в которой подробно описано, как добавлять значки в объект System.Windows.Forms.MenuItem.Теперь с небольшим кодом у меня есть меню, которое идеально соответствует системным контекстным меню!

0 голосов
/ 15 февраля 2012

После небольшого поиска я наткнулся на этот вопрос и ответ . Я никогда не думал попробовать ContextMenu свойство на NotifyIcon! Хотя он и не идеален, он будет работать достаточно хорошо, пока WPF не решит тот факт, что системный трей является полезной частью приложений. Однако будет действительно стыдно потерять все функции привязки и маршрутизации команд, предоставляемые WPF ContextMenu.

Мне кажется, что принимать свой ответ неправильно, поэтому я собираюсь оставить это открытым еще на несколько дней.

...