Skip to content

Commit

Permalink
JumpList
Browse files Browse the repository at this point in the history
  • Loading branch information
AigioL committed Feb 28, 2021
1 parent bfa409d commit 7bc297a
Show file tree
Hide file tree
Showing 31 changed files with 495 additions and 486 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Avalonia.Controls;
#pragma warning disable CA1416 // 验证平台兼容性
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using ReactiveUI;
Expand All @@ -10,6 +11,13 @@
using System.Properties;
using System.Windows;
using AvaloniaApplication = Avalonia.Application;
using ShutdownMode = Avalonia.Controls.ShutdownMode;
using Window = Avalonia.Controls.Window;
using WindowState = Avalonia.Controls.WindowState;
#if WINDOWS
using System.Windows.Shell;
using WpfApplication = System.Windows.Application;
#endif

namespace System.Application.UI
{
Expand Down Expand Up @@ -37,6 +45,13 @@ public override void Initialize()

public ContextMenu? NotifyIconContextMenu { get; private set; }

static void IsNotOfficialChannelPackageWarning()
{
var text = "The program currently running is not from the official channel.";
var title = "Warning";
MessageBoxCompat.Show(text, title, MessageBoxButtonCompat.OK, MessageBoxImageCompat.Warning);
}

public override void OnFrameworkInitializationCompleted()
{
// 在UI预览中,ApplicationLifetime 为 null
Expand All @@ -47,9 +62,7 @@ public override void OnFrameworkInitializationCompleted()
{
if (!AppHelper.IsOfficialChannelPackage)
{
MessageBox.Show("The program currently running is not from the official channel.", "Warning",
MessageBoxButton.OK,
MessageBoxImage.Warning);
IsNotOfficialChannelPackageWarning();
}
#region NotifyIcon
var notifyIcon = INotifyIcon.Instance;
Expand Down Expand Up @@ -95,9 +108,25 @@ public override void OnFrameworkInitializationCompleted()
});
#endregion

#if WINDOWS
AddJumpTask();
#endif

desktop.MainWindow = MainWindow;
desktop.Exit += Desktop_Exit;
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;

#if DEBUG
AreYouOk();
static async void AreYouOk()
{
await MessageBoxCompat.ShowAsync(
"Are You Ok?",
"title",
MessageBoxButtonCompat.OK,
MessageBoxImageCompat.Information);
}
#endif
}


Expand Down Expand Up @@ -157,6 +186,37 @@ public static void Shutdown()
{
desktop.Shutdown(0);
}
#if WINDOWS
WpfApplication.Current.Shutdown();
#endif
}

#if WINDOWS

// JumpList
// 表示作为菜单显示在 Windows 7 任务栏按钮上的项和任务的列表。
// https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.shell.jumplist?view=net-5.0

// Taskbar Extensions
// https://docs.microsoft.com/zh-cn/windows/win32/shell/taskbar-extensions?redirectedfrom=MSDN

static void AddJumpTask()
{
// Configure a new JumpTask.
var jumpTask1 = new JumpTask
{
// Get the path to Calculator and set the JumpTask properties.
ApplicationPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "calc.exe"),
IconResourcePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "calc.exe"),
Title = "Calculator",
Description = "Open Calculator.",
CustomCategory = "User Added Tasks"
};
// Get the JumpList from the application and update it.
JumpList jumpList1 = JumpList.GetJumpList(WpfApplication.Current);
jumpList1.JumpItems.Add(jumpTask1);
JumpList.AddToRecentCategory(jumpTask1);
jumpList1.Apply();
}
/// <summary>
/// Exits the app by calling <c>Shutdown()</c> on the <c>IClassicDesktopStyleApplicationLifetime</c>.
Expand All @@ -177,4 +237,5 @@ void IDisposable.Dispose()
}
#endregion
}
}
}
#pragma warning restore CA1416 // 验证平台兼容性
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ public class FluentWindow : Window, IStyleable
Type IStyleable.StyleKey => typeof(Window);

public FluentWindow()
{
Constructor();
}

public FluentWindow(IWindowImpl impl) : base(impl)
{
Constructor();
}

void Constructor()
{
ExtendClientAreaToDecorationsHint = true;
ExtendClientAreaTitleBarHeightHint = -1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.ReactiveUI;
using NLog;
using System.Threading.Tasks;

namespace System.Application.UI
{
Expand All @@ -18,6 +19,11 @@ static void Main(string[] args)
var logger = LogManager.GetCurrentClassLogger();
try
{
#if WINDOWS
var app = new WpfApp();
app.InitializeComponent();
Task.Factory.StartNew(app.Run);
#endif
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
catch (Exception ex)
Expand Down
124 changes: 124 additions & 0 deletions System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
using System.Application.Models;
using System.Application.Services;
using System.Application.Services.Implementation;
using System.Application.UI.Views;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using System.Windows;

namespace System.Application.UI
{
Expand Down Expand Up @@ -70,9 +75,128 @@ static void ConfigureServices(IServiceCollection services)
services.AddAppUpdateService();

// 托盘图标
#if WINDOWS
services.AddTransient<INotifyIconWindow<ContextMenu>, Win32NotifyIconWindow>();
#endif
services.AddNotifyIcon<NotifyIconImpl>();

#region MessageBox

/* System.Windows.MessageBox 在 WPF 库中,仅支持 Win 平台
* 改为 System.Windows.MessageBoxCompat 可跨平台兼容
* 在其他平台上使用 MessageBox.Avalonia 库实现
* API变更说明:
* - 如果需要获取返回值,即点击那个按钮,则使用异步版本 ShowAsync
* - 如果不需要获取返回值,则可直接使用 同步版本 Show
* 注意事项:
* - 图标(Icon)与按钮(Button)不要使用标记为 Obsolete 的
* - WPF 中 显示窗口(Show)会锁死父窗口等,类似 ShowDialog
* - MessageBox.Avalonia 中则不会锁死窗口
* 已知问题:
* - 在 MessageBox.Avalonia 中
* - 如果内容文本(messageBoxText)过短 UI 上的文字显示不全
* - 点击窗口按 Ctrl+C 无法复制弹窗中的文本内容
* - 按钮文本(ButtonText)缺少本地化翻译(Translate)
* - 某些图标图片与枚举值不太匹配,例如 Information
*/

#if WINDOWS
// 可选项,在 Win 平台使用 WPF 实现的 MessageBox
//services.AddSingleton<IMessageBoxCompatService, WPFMessageBoxCompatService>();
#endif

#endregion
}

sealed class NotifyIconImpl : NotifyIcon<ContextMenu>, INotifyIcon { }

#if WINDOWS
sealed class Win32NotifyIconWindow : MainWindow, INotifyIconWindow<ContextMenu>
{
sealed class Win32WindowImpl : Avalonia.Win32.WindowImpl
{
public Win32NotifyIconWindow? Window { get; set; }

protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
var _notifyIcon = Window?.NotifyIcon;
var value = _notifyIcon == null ? null : NotifyIcon<ContextMenu>.WndProc(_notifyIcon, msg, wParam, lParam);
return value ?? base.WndProc(hWnd, msg, wParam, lParam);
}
}

public Win32NotifyIconWindow() : base(new Win32WindowImpl())
{
if (PlatformImpl is Win32WindowImpl impl)
{
impl.Window = this;
}
else
{
throw new PlatformNotSupportedException();
}
}

public IntPtr Handle => PlatformImpl.Handle.Handle;

[NotNull, DisallowNull] // C# 8 not null
public NotifyIcon<ContextMenu>? NotifyIcon { get; private set; }

public void Initialize(INotifyIcon<ContextMenu> notifyIcon)
{
if (notifyIcon is NotifyIcon<ContextMenu> _notifyIcon)
{
NotifyIcon = _notifyIcon;
}
else
{
throw new PlatformNotSupportedException();
}
//Content = NotifyIcon;
}
}

sealed class WPFMessageBoxCompatService : IMessageBoxCompatService
{
static MessageBoxButton GetButtonEnum(MessageBoxButtonCompat button) => button switch
{
MessageBoxButtonCompat.OK => MessageBoxButton.OK,
MessageBoxButtonCompat.OKCancel => MessageBoxButton.OKCancel,
MessageBoxButtonCompat.YesNo => MessageBoxButton.YesNo,
MessageBoxButtonCompat.YesNoCancel => MessageBoxButton.YesNoCancel,
_ => throw new ArgumentOutOfRangeException(nameof(button), $"value: {button}"),
};

static MessageBoxImage GetIcon(MessageBoxImageCompat icon) => icon switch
{
MessageBoxImageCompat.Asterisk => MessageBoxImage.Asterisk,
MessageBoxImageCompat.Error => MessageBoxImage.Error,
MessageBoxImageCompat.Exclamation => MessageBoxImage.Exclamation,
MessageBoxImageCompat.None => MessageBoxImage.None,
#pragma warning disable CS0618 // 类型或成员已过时
MessageBoxImageCompat.Question => MessageBoxImage.Question,
#pragma warning restore CS0618 // 类型或成员已过时
_ => throw new ArgumentOutOfRangeException(nameof(icon), $"value: {icon}"),
};

static MessageBoxResultCompat GetResult(MessageBoxResult result) => result switch
{
MessageBoxResult.OK => MessageBoxResultCompat.OK,
MessageBoxResult.Yes => MessageBoxResultCompat.Yes,
MessageBoxResult.No => MessageBoxResultCompat.No,
MessageBoxResult.Cancel => MessageBoxResultCompat.Cancel,
MessageBoxResult.None => MessageBoxResultCompat.None,
_ => throw new ArgumentOutOfRangeException(nameof(result), $"value: {result}"),
};

public Task<MessageBoxResultCompat> ShowAsync(string messageBoxText, string caption, MessageBoxButtonCompat button, MessageBoxImageCompat icon)
{
var button_ = GetButtonEnum(button);
var icon_ = GetIcon(icon);
var result = MessageBox.Show(messageBoxText, caption, button_, icon_);
return Task.FromResult(GetResult(result));
}
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>

<PropertyGroup Condition=" $(RuntimeIdentifier.Contains(`win`)) Or ('$(OS)' == 'Windows_NT' And !$(DefineConstants.Contains(`PUBLISH`))) ">
<TargetFramework>net5.0-windows</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\System.Common.CoreLib\Properties\AssemblyInfo.cs">
<Link>Properties\AssemblyInfo.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avalonia.Markup.Xaml;
using System.Application.UI.ViewModels;
using System.ComponentModel;
using Avalonia.Platform;

namespace System.Application.UI.Views
{
Expand All @@ -16,6 +17,14 @@ public MainWindow()
#endif
}

public MainWindow(IWindowImpl impl) : base(impl)
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}

protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Threading.Tasks;
using System.Windows;

namespace System.Application.Services
{
/// <summary>
/// System.Windows.MessageBoxCompat.ShowAsync
/// </summary>
public interface IMessageBoxCompatService
{
/// <summary>
/// 显示一个消息框,该消息框包含消息、标题栏标题、按钮和图标,并且返回结果。
/// </summary>
/// <param name="messageBoxText">一个 <see cref="string"/>,用于指定要显示的文本。</param>
/// <param name="caption">一个 <see cref="string"/>,用于指定要显示的标题栏标题。</param>
/// <param name="button">一个 <see cref="MessageBoxButtonCompat"/> 值,用于指定要显示哪个按钮或哪些按钮。</param>
/// <param name="icon">一个 <see cref="MessageBoxImageCompat"/> 值,用于指定要显示的图标。</param>
/// <returns>一个 <see cref="MessageBoxResultCompat"/> 值,用于指定用户单击了哪个消息框按钮。</returns>
Task<MessageBoxResultCompat> ShowAsync(string messageBoxText, string caption, MessageBoxButtonCompat button, MessageBoxImageCompat icon);
}
}
Loading

0 comments on commit 7bc297a

Please sign in to comment.