Search code examples
.netwpfsystem-traytrayicon

How to disappear ContextMenu when user click outside ContextMenu for Tray Icon in WPF .net


I am working on a .NET WPF project using Visual Studio 2022 and I added tray icon functionality to my app. Everything works fine except when tray icon is right clicked context menu opened but when user clicks outside ContextMenu it does not disappear. I tried to add "StaysOpen" property to false but it did not work. What should I do to solve this? here is my code:

public partial class MainWindow : Window
{
        public NotifyIcon m_notifyIcon;

        public MainWindow()
        {
            InitializeComponent();
            m_notifyIcon = new System.Windows.Forms.NotifyIcon();
            m_notifyIcon.BalloonTipText = "The app has been minimised. Click the tray icon to show.";
            m_notifyIcon.BalloonTipTitle = "The App";
            m_notifyIcon.Text = "The App";
            m_notifyIcon.Icon = new System.Drawing.Icon("48x48_active.ico");
            m_notifyIcon.DoubleClick += new EventHandler(m_notifyIcon_Click);
            m_notifyIcon.MouseDown += new MouseEventHandler(notifier_MouseDown);

            Hide();
            if (m_notifyIcon != null)
                m_notifyIcon.ShowBalloonTip(2000);

            CheckTrayIcon();
        }
        void OnClose(object sender, CancelEventArgs args)
        {
            m_notifyIcon.Dispose();
            m_notifyIcon = null;
        }

        private WindowState m_storedWindowState = WindowState.Normal;

        void OnStateChanged(object sender, EventArgs args)
        {
            if (WindowState == WindowState.Minimized)
            {
                Hide();
                if (m_notifyIcon != null)
                    m_notifyIcon.ShowBalloonTip(2000);
            }
            else
                m_storedWindowState = WindowState;
        }

        void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            CheckTrayIcon();
        }

        void m_notifyIcon_Click(object sender, EventArgs e)
        {
            Show();
            WindowState = m_storedWindowState;
        }

        void CheckTrayIcon()
        {
            ShowTrayIcon(true);
        }

        void ShowTrayIcon(bool show)
        {
            if (m_notifyIcon != null)
                m_notifyIcon.Visible = show;
        }

        void notifier_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)

            {
                ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");

                menu.IsOpen = true;
            }
        }

        private void Menu_Open(object sender, RoutedEventArgs e)
        {
            Show();
            WindowState = m_storedWindowState;
        }

        private void Menu_Close(object sender, RoutedEventArgs e)
        {
            System.Windows.Application.Current.Shutdown();
        }
    }

<Window x:Class="TrayApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TrayApp"
        mc:Ignorable="d"
        StateChanged="OnStateChanged" IsVisibleChanged="OnIsVisibleChanged"
        Title="MainWindow" Height="450" Width="800">
    <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>
    <Grid>
    </Grid>
</Window>

Solution

  • What should I do to solve this?

    Activate the ContextMenu using the Win32 SetForegroundWindow API:

    [DllImport("User32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    
    void notifier_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");
            menu.IsOpen = true;
    
            IntPtr handle = ((HwndSource)PresentationSource.FromVisual(menu)).Handle;
            SetForegroundWindow(handle);
        }
    }