Reputation: 23
I am working on a .NET WPF project using Visual Studio 2022 and I added tray icon functionality to my app. I also show toast notification whenever my app is minimized to windows tray. I also added a context menu to show up when user right clicks onto tray icon. When toast notification appears and user clicks on the notification ballon, context menu does not open at the first right click event.
I tried to debug and solve the problem. I observe that first right click does not trigger Notifier_MouseDown event. I suppose it is a focus issue that when user clicks onto notification ballon, focus moves somewhere else and thats why it does not trigger at first click. However, I could not figure out how to solve this issue. Any help would be appreciated.
Work so far:
public MainWindow()
{
InitializeComponent();
notifyIcon = new NotifyIcon();
ShowNotifications = true;
notifyIcon.BalloonTipText = TextRes.Get("TrayNotifyBalloonText");
notifyIcon.BalloonTipTitle = TextRes.Get("TrayAppTitle");
notifyIcon.Text = TextRes.Get("TrayAppTitle");
notifyIcon.DoubleClick += new EventHandler(NotifyIcon_Click);
notifyIcon.MouseDown += new MouseEventHandler(Notifier_MouseDown);
notifyIcon.BalloonTipClicked += new EventHandler(NotifyIcon_BalloonTipClicked);
Hide();
menu = (ContextMenu)this.FindResource("NotifierContextMenu");
if (notifyIcon != null)
{
notifyIcon.ShowBalloonTip(60000);
}
}
private void NotifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
Show();
WindowState = m_storedWindowState;
}
private void Window_Closing(object sender, CancelEventArgs e)
{
notifyIcon.BalloonTipText = ResourceHelper.GetResourceText("TrayNotifyBalloonText");
e.Cancel = true;
WindowState = WindowState.Minimized;
Hide();
}
void OnClose(object sender, CancelEventArgs args)
{
notifyIcon.Dispose();
notifyIcon = null;
}
private WindowState m_storedWindowState = WindowState.Normal;
void OnStateChanged(object sender, EventArgs args)
{
if (WindowState == WindowState.Minimized)
{
notifyIcon.BalloonTipText = ResourceHelper.TextRes.Get("TrayNotifyBalloonText");
}
else
m_storedWindowState = WindowState;
}
void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
{
CheckTrayIcon();
}
void NotifyIcon_Click(object sender, EventArgs e)
{
Show();
WindowState = m_storedWindowState;
}
void CheckTrayIcon()
{
ShowTrayIcon(true);
}
void ShowTrayIcon(bool show)
{
if (notifyIcon != null)
notifyIcon.Visible = show;
}
void Notifier_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
menu.IsOpen = true;
IntPtr handle = ((HwndSource)PresentationSource.FromVisual(menu)).Handle;
ApiHelper.SetForegroundWindow(handle);
}
}
private void Menu_Open(object sender, RoutedEventArgs e)
{
Show();
WindowState = m_storedWindowState;
IntPtr handle = ((HwndSource)PresentationSource.FromVisual(this)).Handle;
ApiHelper.SetForegroundWindow(handle);
}
private void Menu_Close(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
<Window.Resources>
<ContextMenu
Focusable="{Binding FocusMenu}"
x:Key="NotifierContextMenu"
StaysOpen="False"
Placement="MousePoint">
<MenuItem Header="Open" Click="Menu_Open"/>
<MenuItem Header="Close" Click="Menu_Close"/>
</ContextMenu>
</Window.Resources>
Upvotes: 2
Views: 307
Reputation: 3246
Try to use ContextMenuStrip
instead of ContextMenu
and assign it to the ContextMenuStrip
property of the NotifyIcon
instance.
Here is an example code that works for every right click event.
private NotifyIcon trayIcon;
// Called in the constructor after calling InitializeComponent() method.
private void ConfigureTrayIcon()
{
var contextMenuStrip = new ContextMenuStrip();
contextMenuStrip.Items.AddRange(
new[]
{
new ToolStripMenuItem("Toggle", null, OnToggleRequested),
new ToolStripMenuItem("Exit", null, OnExitRequested)
}
);
trayIcon = new NotifyIcon()
{
Icon = Resources.Resources.ApplicationIcon,
ContextMenuStrip = contextMenuStrip,
Text = "Initial text",
Visible = true
};
}
private void OnExitRequested(object? sender, EventArgs e)
{
trayIcon.Visible = false;
Application.Exit();
}
Note the trayIcon.ContextMenuStrip
property is set to the menu, so there is no need to call Win32 API methods, e.g. SetForegroundWindow()
.
The menu will be displayed automatically.
Upvotes: 2