I'm having trouble achieving tray menu in packaged WinUI app

水 知 445 Reputation points
2026-08-15T12:46:38.5133333+00:00

Hi community!

I'm trying to writting an app and here's some trouble about it. I write a WinUI lib using WinForm to achieve tray icon, and I made this tray icon able to react when clicked by mouse. It was working good, I tested it with my new app.

Then something strange happens. I was testing my app on other machines, and I found I'm unable to use its right tray menu while the left is still usable. I found my app(package) created when debugging by VS is not a packaged app hence it would work properly. I removed that on my machine and reinstalled with msix package and the same thing happens.

The WinUI lib is on https://github.com/ShimizuTheLotus/ShimizuToolkit.TrayIconWinUI. For whole project, it's on https://github.com/ShimizuTheLotus/DebrisToys. Nothing personal, it's completely open source.

Plus, here's new problem since I made this question. When I try to build it, it's keep showing The "GenerateProjectPriFile" task was not given a value for the required parameter "ProjectPriIndexName". I tried to revert my changes but seems doesn't works.

I was using the lib in my app(created with WinUI template with packaging project) like this. If you checked the repo DebrisToys, just ignore the code below.

// App.xaml.cs

using Windows.Storage;
using System.Windows.Forms;
using System.Diagnostics;
using System;
using Microsoft.UI;
using Microsoft.UI.Xaml;

        protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
        {
            MainWindow = new MainWindow();
            MainWindow.Activate();

            ToysConfigManager.Current.Initialize();
            SetUpTrayIcon();
        }

       private async void SetUpTrayIcon()
       {
           Uri fileUri = new("ms-appx:///Images/Icon/icon.ico");
           StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(fileUri);

           string iconPath = file.Path;

           if (System.IO.Path.Exists(file.Path))
           {
               NotifyIcon notifyIcon = new()
               {
                   Icon = new System.Drawing.Icon(iconPath),
                   Visible = true
               };
               ShimizuToolkit.TrayIconWinUI.TrayIconManager.Current.SetNotifyIcon(notifyIcon);
           }
           TrayWindow = new();
           ShimizuToolkit.TrayIconWinUI.TrayIconManager.Current.LeftClickAction += ShowTrayWindow;
           ShimizuToolkit.TrayIconWinUI.TrayIconManager.Current.RightMenuWindow = new ShimizuToolkit.TrayIconWinUI.UI.TrayFlyoutBaseWindow(() =>
           {
               return new DebrisToys.UI.Tray.RightTrayMenu();
           });
       }

        public void ShowTrayWindow()
        {
            TrayWindow = new DebrisToys.UI.Window.TrayWindow();
            // Move window to right bottom corner
            var (screenWidth, screenHeight) = Global.Helper.ScreenInteraction.GetMonitorWorkArea();
            // Window size consts
            int windowWidth = 600;
            int windowHeight = 800;

            int x = screenWidth - windowWidth;
            int y = screenHeight - windowHeight;
            IntPtr hwnd = WinRT.Interop.WindowNative.GetWindowHandle(TrayWindow);
            var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
            var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

            appWindow.MoveAndResize(new Windows.Graphics.RectInt32(x, y, windowWidth, windowHeight));

            TrayWindow.Activate();
        }

TrayWindow is the window working properly, it was invoked by clicking tray icon with left mouse button. Some classes are not included in the sample code and they're not important.

// TrayWindow.xaml.cs
public sealed partial class TrayWindow : Microsoft.UI.Xaml.Window
{
  
    private Global.Helper.WindowExternalClickDetector _clickDetector;
    public TrayWindow()
    {
        InitializeComponent();

        // Hide app bar
        var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
        var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
        var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

        Win32.Window.RemoveTaskbarIcon(hWnd);
        if (appWindow.Presenter is Microsoft.UI.Windowing.OverlappedPresenter presenter)
        {
            presenter.IsAlwaysOnTop = true;
            presenter.SetBorderAndTitleBar(true, false);
        }

        this.Closed += TrayWindow_Closed;

        _clickDetector = new Global.Helper.WindowExternalClickDetector(this, () =>
        {
            this.Close();
        });

        //Global.Helper.WindowInteraction.SetForegroundWindowAndSetFocus(this);
        this.Activate();
    }

    private void TrayWindow_Closed(object sender, WindowEventArgs args)
    {
        this.Closed -= TrayWindow_Closed;
        _clickDetector.Dispose();
    }

    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        App.MainWindow?.AppWindow.Show();
        App.MainWindow?.Activate();
        //Global.Helper.WindowInteraction.SetForegroundWindowAndSetFocus(App.MainWindow);
        this.Close();
    }
}

Here's RightTrayMenu, it can't show itself after packaged.


    internal class RightTrayMenu : ShimizuToolkit.TrayIconWinUI.UI.TrayMenuFlyout
    {
        public RightTrayMenu()
        {
            AddMenuItems();
        }

        private void AddMenuItems()
        {
            AddMenuItem("Open MainWindow", ShowMainWindow);
            AddMenuItem("Exit", ExitApp);
        }

        private void ShowMainWindow()
        {
            App.MainWindow?.Activate();
        }

        public void ExitApp()
        {
            App.RequestExitApp();
        }
    }

Here's TrayMenuFlyout, base class of RightTrayMenu.

    public class TrayMenuFlyout : Microsoft.UI.Xaml.Controls.MenuFlyout
    {
        public TrayMenuFlyout()
        {
            var presenterResourceDict = new ResourceDictionary
            {
                Source = new Uri("ms-appx:///ShimizuToolkit.TrayIconWinUI/Themes/TrayMenuFlyoutPresenterStyle.xaml")
            };
            this.MenuFlyoutPresenterStyle = (Style)presenterResourceDict["TrayMenuFlyoutPresenterStyle"];
            this.ShouldConstrainToRootBounds = false;
        }

        public void AddMenuItem(TrayMenuFlyoutItem trayMenuFlyoutItem, Action action)
        {
            trayMenuFlyoutItem.OnClick = action;
            Items.Add(trayMenuFlyoutItem);
        }
        public void AddMenuItem(string Text, Action action)
        {
            TrayMenuFlyoutItem trayMenuFlyoutItem = new()
            {
                Text = Text,
                OnClick = action
            };
            Items.Add(trayMenuFlyoutItem);
        }
    }

If there's any other code needed, please ask. All code are open source. Please help.

Windows development | WinUI
0 comments No comments

1 answer

Sort by: Most helpful
  1. Jay Pham (WICLOUD CORPORATION) 4,160 Reputation points Microsoft External Staff Moderator
    2026-08-17T01:34:13.9366667+00:00

    Hi @水 知 ,

    I see that the tray icon and left-click action still work after the MSIX is installed. The current evidence therefore does not show that packaged WinUI or MSIX generally blocks tray menus.

    The right-click path is more complex. The library creates a transparent WinUI window, loads two XAML resource dictionaries from a class library, and then calls MenuFlyout.ShowAt against that window's content. Microsoft confirms that the ms-appx:///ClassLibraryName/path format is valid for resources in a WinUI class library. However, those resources must still be built into the final package/PRI, and the placement target must be attached to the window's XamlRoot.

    The most likely root cause is a failure while the custom flyout loads or applies its XAML resources, or while it is attached to and shown from the hidden window in the packaged build. The exception is currently hidden because the app marks unhandled exceptions as handled and the library contains an empty catch. I would not classify this as a packaged WinUI limitation until the actual exception is captured.

    I recommend the following minimum isolation tests:

    1. Log the full e.Exception.ToString() value and temporarily remove the empty catch.
    2. Test a plain MenuFlyout with standard MenuFlyoutItem objects and no custom styles.
    3. Record ((FrameworkElement)base.Content).XamlRoot immediately before ShowAt.
    4. Verify that the final MSIX/resources.pri contains both XAML resources from ShimizuToolkit.TrayIconWinUI.

    If the plain menu works, the failure is isolated to the custom XAML/style or PRI packaging path. If XamlRoot is null, the hidden window/content must be attached before ShowAt is called.

    The lowest-risk workaround is to assign a ContextMenuStrip directly to the existing NotifyIcon. Microsoft provides the NotifyIcon.ContextMenuStrip API specifically for the shortcut menu associated with a notification icon. This removes the hidden WinUI window, XamlRoot, ShowAt, and class-library XAML/PRI dependencies from the right-click workflow.

    The later GenerateProjectPriFile error for a missing ProjectPriIndexName should be treated as a separate build/package issue. I do not recommend adding an arbitrary value only to suppress the error. Revert custom packaging-target changes, compare the projects with a fresh WinUI template using the same SDK, or migrate to single-project MSIX if the package contains only one executable.

    If you found my response helpful or informative, I would greatly appreciate it if you could provide feedback by interacting with the system or leaving a comment below.

    Thank you.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.