У меня есть приложение 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, поэтому возврат назад невозможен.
Кто-нибудь знает, как сделать контекстное меню действительно открытым там, где находится курсор?