如何将 Avalonia 应用程序加载到系统托盘并设置菜单项?
Avalonia seems to be a UI/ WPF library/resource, so I don't think that would affect how your application runs. This would be related to WPF application development.
A bit of reading around, and it appears you may need to use the System.Windows.Forms.NotifyIcon
.
You would want to instantiate the icon in the context of your main application.
I created a sample WPF application using .NET Framework (so that I was able to reference System.Windows.Forms
) and was able to have a system tray icon appear for my application.
Here is some example code:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : System.Windows.Application
{
NotifyIcon TrayIcon;
public App()
{
// we initialize the tray icon in the application constructor
// and have that reference for the lifetime of the application
TrayIcon = new NotifyIcon()
{
Icon = SystemIcons.Information,
ContextMenu = new ContextMenu(new MenuItem[] { new MenuItem("Show/Hide MyApp", ShowHide), new MenuItem("Exit", OnExit) }),
Visible = true
};
}
private void OnExit(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void ShowHide(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}
It appears Avalonia does offer their own version of a TrayIcon. Here is the class I was able to find in their Source Code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
public sealed class TrayIcons : AvaloniaList<TrayIcon>
{
}
public class TrayIcon : AvaloniaObject, INativeMenuExporterProvider, IDisposable
{
private readonly ITrayIconImpl? _impl;
private ICommand? _command;
private TrayIcon(ITrayIconImpl? impl)
{
if (impl != null)
{
_impl = impl;
_impl.SetIsVisible(IsVisible);
_impl.OnClicked = () =>
{
Clicked?.Invoke(this, EventArgs.Empty);
if (Command?.CanExecute(CommandParameter) == true)
{
Command.Execute(CommandParameter);
}
};
}
}
public TrayIcon() : this(PlatformManager.CreateTrayIcon())
{
}
static TrayIcon()
{
IconsProperty.Changed.Subscribe(args =>
{
if (args.Sender is Application)
{
if (args.OldValue.Value != null)
{
RemoveIcons(args.OldValue.Value);
}
if (args.NewValue.Value != null)
{
args.NewValue.Value.CollectionChanged += Icons_CollectionChanged;
}
}
});
var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized.");
if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
lifetime.Exit += Lifetime_Exit;
}
}
/// <summary>
/// Raised when the TrayIcon is clicked.
/// Note, this is only supported on Win32 and some Linux DEs,
/// on OS X this event is not raised.
/// </summary>
public event EventHandler? Clicked;
/// <summary>
/// Defines the <see cref="Command"/> property.
/// </summary>
public static readonly DirectProperty<TrayIcon, ICommand?> CommandProperty =
Button.CommandProperty.AddOwner<TrayIcon>(
trayIcon => trayIcon.Command,
(trayIcon, command) => trayIcon.Command = command,
enableDataValidation: true);
/// <summary>
/// Defines the <see cref="CommandParameter"/> property.
/// </summary>
public static readonly StyledProperty<object?> CommandParameterProperty =
Button.CommandParameterProperty.AddOwner<MenuItem>();
/// <summary>
/// Defines the <see cref="TrayIcons"/> attached property.
/// </summary>
public static readonly AttachedProperty<TrayIcons> IconsProperty
= AvaloniaProperty.RegisterAttached<TrayIcon, Application, TrayIcons>("Icons");
/// <summary>
/// Defines the <see cref="Menu"/> property.
/// </summary>
public static readonly StyledProperty<NativeMenu?> MenuProperty
= AvaloniaProperty.Register<TrayIcon, NativeMenu?>(nameof(Menu));
/// <summary>
/// Defines the <see cref="Icon"/> property.
/// </summary>
public static readonly StyledProperty<WindowIcon?> IconProperty =
Window.IconProperty.AddOwner<TrayIcon>();
/// <summary>
/// Defines the <see cref="ToolTipText"/> property.
/// </summary>
public static readonly StyledProperty<string?> ToolTipTextProperty =
AvaloniaProperty.Register<TrayIcon, string?>(nameof(ToolTipText));
/// <summary>
/// Defines the <see cref="IsVisible"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsVisibleProperty =
Visual.IsVisibleProperty.AddOwner<TrayIcon>();
public static void SetIcons(AvaloniaObject o, TrayIcons trayIcons) => o.SetValue(IconsProperty, trayIcons);
public static TrayIcons GetIcons(AvaloniaObject o) => o.GetValue(IconsProperty);
/// <summary>
/// Gets or sets the <see cref="Command"/> property of a TrayIcon.
/// </summary>
public ICommand? Command
{
get => _command;
set => SetAndRaise(CommandProperty, ref _command, value);
}
/// <summary>
/// Gets or sets the parameter to pass to the <see cref="Command"/> property of a
/// <see cref="TrayIcon"/>.
/// </summary>
public object? CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
/// <summary>
/// Gets or sets the Menu of the TrayIcon.
/// </summary>
public NativeMenu? Menu
{
get => GetValue(MenuProperty);
set => SetValue(MenuProperty, value);
}
/// <summary>
/// Gets or sets the icon of the TrayIcon.
/// </summary>
public WindowIcon? Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
/// <summary>
/// Gets or sets the tooltip text of the TrayIcon.
/// </summary>
public string? ToolTipText
{
get => GetValue(ToolTipTextProperty);
set => SetValue(ToolTipTextProperty, value);
}
/// <summary>
/// Gets or sets the visibility of the TrayIcon.
/// </summary>
public bool IsVisible
{
get => GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
public INativeMenuExporter? NativeMenuExporter => _impl?.MenuExporter;
private static void Lifetime_Exit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
{
var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized.");
var trayIcons = GetIcons(app);
RemoveIcons(trayIcons);
}
private static void Icons_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.OldItems is not null)
RemoveIcons(e.OldItems.Cast<TrayIcon>());
}
private static void RemoveIcons(IEnumerable<TrayIcon> icons)
{
foreach (var icon in icons)
{
icon.Dispose();
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == IconProperty)
{
_impl?.SetIcon(Icon?.PlatformImpl);
}
else if (change.Property == IsVisibleProperty)
{
_impl?.SetIsVisible(change.GetNewValue<bool>());
}
else if (change.Property == ToolTipTextProperty)
{
_impl?.SetToolTipText(change.GetNewValue<string?>());
}
else if (change.Property == MenuProperty)
{
_impl?.MenuExporter?.SetNativeMenu(change.GetNewValue<NativeMenu?>());
}
}
/// <summary>
/// Disposes the tray icon (removing it from the tray area).
/// </summary>
public void Dispose() => _impl?.Dispose();
}
}
And an application example from them as well which shows an example implementation:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ControlCatalog.ViewModels"
x:DataType="vm:ApplicationViewModel"
x:CompileBindings="True"
Name="Avalonia ControlCatalog"
x:Class="ControlCatalog.App">
<Application.Styles>
<Style Selector="TextBlock.h1, TextBlock.h2, TextBlock.h3">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="TextBlock.h2">
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="12" />
</Style>
<Style Selector="Label.h1">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="Label.h2">
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="Label.h3">
<Setter Property="FontSize" Value="12" />
</Style>
<StyleInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
</Application.Styles>
<TrayIcon.Icons>
<TrayIcons>
<TrayIcon Icon="/Assets/test_icon.ico" ToolTipText="Avalonia Tray Icon ToolTip">
<TrayIcon.Menu>
<NativeMenu>
<NativeMenuItem Header="Settings">
<NativeMenu>
<NativeMenuItem Header="Option 1" ToggleType="Radio" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItem Header="Option 2" ToggleType="Radio" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItemSeparator />
<NativeMenuItem Header="Option 3" ToggleType="CheckBox" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItem Icon="/Assets/test_icon.ico" Header="Restore Defaults" Command="{Binding ToggleCommand}" />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Exit" Command="{Binding ExitCommand}" />
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>
</TrayIcons>
</TrayIcon.Icons>
</Application>
They do have some documentation here .
Unfortunately their documentation website is hard to navigate, and doesn't seem to mention anything regarding "TrayIcon" when using their search feature.
Fortunately, it seems they also have a Customer Support team, and you could always post questions to their support team directly on their GitHub place:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.