xaml-csharp-development-skill-for-avalonia

Tray Icons and System Tray Integration

Table of Contents

  1. Scope and APIs
  2. Platform and Lifecycle Model
  3. XAML Pattern in Application
  4. C# Pattern for Runtime Control
  5. Native Menu Integration for Tray Icons
  6. Platform-Specific Notes
  7. Best Practices
  8. Troubleshooting

Scope and APIs

Primary APIs:

Related menu/platform APIs:

Reference source files:

Platform and Lifecycle Model

Key behavior:

Important detail:

XAML Pattern in Application

Declare tray icons directly in App.axaml:

<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:MyApp"
             x:Class="MyApp.App"
             x:DataType="vm:App">
  <Application.Styles>
    <FluentTheme />
  </Application.Styles>

  <TrayIcon.Icons>
    <TrayIcons>
      <TrayIcon Icon="/Assets/app.ico"
                ToolTipText="MyApp"
                Command="{Binding ActivateFromTrayCommand}"
                CommandParameter="open-main-window"
                MacOSProperties.IsTemplateIcon="True">
        <TrayIcon.Menu>
          <NativeMenu>
            <NativeMenuItem Header="Open" Command="{Binding OpenCommand}" />
            <NativeMenuItem Header="Pause Sync"
                            ToggleType="CheckBox"
                            IsChecked="{Binding IsSyncPaused}" />
            <NativeMenuItemSeparator />
            <NativeMenuItem Header="Exit" Command="{Binding ExitCommand}" />
          </NativeMenu>
        </TrayIcon.Menu>
      </TrayIcon>
    </TrayIcons>
  </TrayIcon.Icons>
</Application>

Notes:

C# Pattern for Runtime Control

using System.Linq;
using Avalonia;
using Avalonia.Controls;

public static class TrayIconHelpers
{
    public static TrayIcon? GetPrimaryTrayIcon()
    {
        var app = Application.Current;
        if (app is null)
            return null;

        return TrayIcon.GetIcons(app)?.FirstOrDefault();
    }

    public static void ToggleTrayIconVisibility()
    {
        var icon = GetPrimaryTrayIcon();
        if (icon is null)
            return;

        icon.IsVisible = !icon.IsVisible;
    }

    public static void UpdateTooltip(string text)
    {
        var icon = GetPrimaryTrayIcon();
        if (icon is null)
            return;

        icon.ToolTipText = text;
    }
}

Manual creation (code-only):

using Avalonia;
using Avalonia.Controls;

var tray = new TrayIcon
{
    Icon = new WindowIcon("Assets/app.ico"),
    ToolTipText = "MyApp",
    IsVisible = true,
    Menu = new NativeMenu
    {
        new NativeMenuItem("Open") { Command = viewModel.OpenCommand },
        new NativeMenuItemSeparator(),
        new NativeMenuItem("Exit") { Command = viewModel.ExitCommand }
    }
};

var icons = new TrayIcons { tray };
TrayIcon.SetIcons(Application.Current!, icons);

Native Menu Integration for Tray Icons

TrayIcon.Menu uses NativeMenu APIs:

For dynamic tray menus, update menu items in response to state changes before user interaction, similar to native menu guidance in:

Platform-Specific Notes

Best Practices

Troubleshooting

  1. Tray icon does not appear.
    • Ensure TrayIcon.Icons is set on Application (not Window).
    • Verify the icon asset path is valid and loadable.
  2. Tray click does nothing on macOS.
    • Expected on macOS for Clicked; use tray menu commands instead.
  3. Tray menu not shown.
    • Ensure TrayIcon.Menu is a NativeMenu with items.
  4. Icon remains in tray after app closes.
    • Verify app shutdown is graceful and no external process holds the tray icon.
  5. Runtime icon update has no visible effect.
    • Replace Icon with a valid WindowIcon and confirm platform icon format support.