Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7bc297a

Browse files
committed
JumpList
1 parent bfa409d commit 7bc297a

31 files changed

Lines changed: 495 additions & 486 deletions

System.Application.SteamTools.Client.Desktop.Avalonia.App/App.axaml.cs

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Avalonia.Controls;
1+
#pragma warning disable CA1416 // 验证平台兼容性
2+
using Avalonia.Controls;
23
using Avalonia.Controls.ApplicationLifetimes;
34
using Avalonia.Markup.Xaml;
45
using ReactiveUI;
@@ -10,6 +11,13 @@
1011
using System.Properties;
1112
using System.Windows;
1213
using AvaloniaApplication = Avalonia.Application;
14+
using ShutdownMode = Avalonia.Controls.ShutdownMode;
15+
using Window = Avalonia.Controls.Window;
16+
using WindowState = Avalonia.Controls.WindowState;
17+
#if WINDOWS
18+
using System.Windows.Shell;
19+
using WpfApplication = System.Windows.Application;
20+
#endif
1321

1422
namespace System.Application.UI
1523
{
@@ -37,6 +45,13 @@ public override void Initialize()
3745

3846
public ContextMenu? NotifyIconContextMenu { get; private set; }
3947

48+
static void IsNotOfficialChannelPackageWarning()
49+
{
50+
var text = "The program currently running is not from the official channel.";
51+
var title = "Warning";
52+
MessageBoxCompat.Show(text, title, MessageBoxButtonCompat.OK, MessageBoxImageCompat.Warning);
53+
}
54+
4055
public override void OnFrameworkInitializationCompleted()
4156
{
4257
// 在UI预览中,ApplicationLifetime 为 null
@@ -47,9 +62,7 @@ public override void OnFrameworkInitializationCompleted()
4762
{
4863
if (!AppHelper.IsOfficialChannelPackage)
4964
{
50-
MessageBox.Show("The program currently running is not from the official channel.", "Warning",
51-
MessageBoxButton.OK,
52-
MessageBoxImage.Warning);
65+
IsNotOfficialChannelPackageWarning();
5366
}
5467
#region NotifyIcon
5568
var notifyIcon = INotifyIcon.Instance;
@@ -95,9 +108,25 @@ public override void OnFrameworkInitializationCompleted()
95108
});
96109
#endregion
97110

111+
#if WINDOWS
112+
AddJumpTask();
113+
#endif
114+
98115
desktop.MainWindow = MainWindow;
99116
desktop.Exit += Desktop_Exit;
100117
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
118+
119+
#if DEBUG
120+
AreYouOk();
121+
static async void AreYouOk()
122+
{
123+
await MessageBoxCompat.ShowAsync(
124+
"Are You Ok?",
125+
"title",
126+
MessageBoxButtonCompat.OK,
127+
MessageBoxImageCompat.Information);
128+
}
129+
#endif
101130
}
102131

103132

@@ -157,6 +186,37 @@ public static void Shutdown()
157186
{
158187
desktop.Shutdown(0);
159188
}
189+
#if WINDOWS
190+
WpfApplication.Current.Shutdown();
191+
#endif
192+
}
193+
194+
#if WINDOWS
195+
196+
// JumpList
197+
// 表示作为菜单显示在 Windows 7 任务栏按钮上的项和任务的列表。
198+
// https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.shell.jumplist?view=net-5.0
199+
200+
// Taskbar Extensions
201+
// https://docs.microsoft.com/zh-cn/windows/win32/shell/taskbar-extensions?redirectedfrom=MSDN
202+
203+
static void AddJumpTask()
204+
{
205+
// Configure a new JumpTask.
206+
var jumpTask1 = new JumpTask
207+
{
208+
// Get the path to Calculator and set the JumpTask properties.
209+
ApplicationPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "calc.exe"),
210+
IconResourcePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "calc.exe"),
211+
Title = "Calculator",
212+
Description = "Open Calculator.",
213+
CustomCategory = "User Added Tasks"
214+
};
215+
// Get the JumpList from the application and update it.
216+
JumpList jumpList1 = JumpList.GetJumpList(WpfApplication.Current);
217+
jumpList1.JumpItems.Add(jumpTask1);
218+
JumpList.AddToRecentCategory(jumpTask1);
219+
jumpList1.Apply();
160220
}
161221
/// <summary>
162222
/// Exits the app by calling <c>Shutdown()</c> on the <c>IClassicDesktopStyleApplicationLifetime</c>.
@@ -177,4 +237,5 @@ void IDisposable.Dispose()
177237
}
178238
#endregion
179239
}
180-
}
240+
}
241+
#pragma warning restore CA1416 // 验证平台兼容性

System.Application.SteamTools.Client.Desktop.Avalonia.App/FluentWindow.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ public class FluentWindow : Window, IStyleable
1111
Type IStyleable.StyleKey => typeof(Window);
1212

1313
public FluentWindow()
14+
{
15+
Constructor();
16+
}
17+
18+
public FluentWindow(IWindowImpl impl) : base(impl)
19+
{
20+
Constructor();
21+
}
22+
23+
void Constructor()
1424
{
1525
ExtendClientAreaToDecorationsHint = true;
1626
ExtendClientAreaTitleBarHeightHint = -1;

System.Application.SteamTools.Client.Desktop.Avalonia.App/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Avalonia;
22
using Avalonia.ReactiveUI;
33
using NLog;
4+
using System.Threading.Tasks;
45

56
namespace System.Application.UI
67
{
@@ -18,6 +19,11 @@ static void Main(string[] args)
1819
var logger = LogManager.GetCurrentClassLogger();
1920
try
2021
{
22+
#if WINDOWS
23+
var app = new WpfApp();
24+
app.InitializeComponent();
25+
Task.Factory.StartNew(app.Run);
26+
#endif
2127
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
2228
}
2329
catch (Exception ex)

System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
using Avalonia.Controls;
22
using Microsoft.Extensions.DependencyInjection;
33
using System.Application.Models;
4+
using System.Application.Services;
45
using System.Application.Services.Implementation;
6+
using System.Application.UI.Views;
7+
using System.Diagnostics.CodeAnalysis;
8+
using System.Threading.Tasks;
9+
using System.Windows;
510

611
namespace System.Application.UI
712
{
@@ -70,9 +75,128 @@ static void ConfigureServices(IServiceCollection services)
7075
services.AddAppUpdateService();
7176

7277
// 托盘图标
78+
#if WINDOWS
79+
services.AddTransient<INotifyIconWindow<ContextMenu>, Win32NotifyIconWindow>();
80+
#endif
7381
services.AddNotifyIcon<NotifyIconImpl>();
82+
83+
#region MessageBox
84+
85+
/* System.Windows.MessageBox 在 WPF 库中,仅支持 Win 平台
86+
* 改为 System.Windows.MessageBoxCompat 可跨平台兼容
87+
* 在其他平台上使用 MessageBox.Avalonia 库实现
88+
* API变更说明:
89+
* - 如果需要获取返回值,即点击那个按钮,则使用异步版本 ShowAsync
90+
* - 如果不需要获取返回值,则可直接使用 同步版本 Show
91+
* 注意事项:
92+
* - 图标(Icon)与按钮(Button)不要使用标记为 Obsolete 的
93+
* - WPF 中 显示窗口(Show)会锁死父窗口等,类似 ShowDialog
94+
* - MessageBox.Avalonia 中则不会锁死窗口
95+
* 已知问题:
96+
* - 在 MessageBox.Avalonia 中
97+
* - 如果内容文本(messageBoxText)过短 UI 上的文字显示不全
98+
* - 点击窗口按 Ctrl+C 无法复制弹窗中的文本内容
99+
* - 按钮文本(ButtonText)缺少本地化翻译(Translate)
100+
* - 某些图标图片与枚举值不太匹配,例如 Information
101+
*/
102+
103+
#if WINDOWS
104+
// 可选项,在 Win 平台使用 WPF 实现的 MessageBox
105+
//services.AddSingleton<IMessageBoxCompatService, WPFMessageBoxCompatService>();
106+
#endif
107+
108+
#endregion
74109
}
75110

76111
sealed class NotifyIconImpl : NotifyIcon<ContextMenu>, INotifyIcon { }
112+
113+
#if WINDOWS
114+
sealed class Win32NotifyIconWindow : MainWindow, INotifyIconWindow<ContextMenu>
115+
{
116+
sealed class Win32WindowImpl : Avalonia.Win32.WindowImpl
117+
{
118+
public Win32NotifyIconWindow? Window { get; set; }
119+
120+
protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
121+
{
122+
var _notifyIcon = Window?.NotifyIcon;
123+
var value = _notifyIcon == null ? null : NotifyIcon<ContextMenu>.WndProc(_notifyIcon, msg, wParam, lParam);
124+
return value ?? base.WndProc(hWnd, msg, wParam, lParam);
125+
}
126+
}
127+
128+
public Win32NotifyIconWindow() : base(new Win32WindowImpl())
129+
{
130+
if (PlatformImpl is Win32WindowImpl impl)
131+
{
132+
impl.Window = this;
133+
}
134+
else
135+
{
136+
throw new PlatformNotSupportedException();
137+
}
138+
}
139+
140+
public IntPtr Handle => PlatformImpl.Handle.Handle;
141+
142+
[NotNull, DisallowNull] // C# 8 not null
143+
public NotifyIcon<ContextMenu>? NotifyIcon { get; private set; }
144+
145+
public void Initialize(INotifyIcon<ContextMenu> notifyIcon)
146+
{
147+
if (notifyIcon is NotifyIcon<ContextMenu> _notifyIcon)
148+
{
149+
NotifyIcon = _notifyIcon;
150+
}
151+
else
152+
{
153+
throw new PlatformNotSupportedException();
154+
}
155+
//Content = NotifyIcon;
156+
}
157+
}
158+
159+
sealed class WPFMessageBoxCompatService : IMessageBoxCompatService
160+
{
161+
static MessageBoxButton GetButtonEnum(MessageBoxButtonCompat button) => button switch
162+
{
163+
MessageBoxButtonCompat.OK => MessageBoxButton.OK,
164+
MessageBoxButtonCompat.OKCancel => MessageBoxButton.OKCancel,
165+
MessageBoxButtonCompat.YesNo => MessageBoxButton.YesNo,
166+
MessageBoxButtonCompat.YesNoCancel => MessageBoxButton.YesNoCancel,
167+
_ => throw new ArgumentOutOfRangeException(nameof(button), $"value: {button}"),
168+
};
169+
170+
static MessageBoxImage GetIcon(MessageBoxImageCompat icon) => icon switch
171+
{
172+
MessageBoxImageCompat.Asterisk => MessageBoxImage.Asterisk,
173+
MessageBoxImageCompat.Error => MessageBoxImage.Error,
174+
MessageBoxImageCompat.Exclamation => MessageBoxImage.Exclamation,
175+
MessageBoxImageCompat.None => MessageBoxImage.None,
176+
#pragma warning disable CS0618 // 类型或成员已过时
177+
MessageBoxImageCompat.Question => MessageBoxImage.Question,
178+
#pragma warning restore CS0618 // 类型或成员已过时
179+
_ => throw new ArgumentOutOfRangeException(nameof(icon), $"value: {icon}"),
180+
};
181+
182+
static MessageBoxResultCompat GetResult(MessageBoxResult result) => result switch
183+
{
184+
MessageBoxResult.OK => MessageBoxResultCompat.OK,
185+
MessageBoxResult.Yes => MessageBoxResultCompat.Yes,
186+
MessageBoxResult.No => MessageBoxResultCompat.No,
187+
MessageBoxResult.Cancel => MessageBoxResultCompat.Cancel,
188+
MessageBoxResult.None => MessageBoxResultCompat.None,
189+
_ => throw new ArgumentOutOfRangeException(nameof(result), $"value: {result}"),
190+
};
191+
192+
public Task<MessageBoxResultCompat> ShowAsync(string messageBoxText, string caption, MessageBoxButtonCompat button, MessageBoxImageCompat icon)
193+
{
194+
var button_ = GetButtonEnum(button);
195+
var icon_ = GetIcon(icon);
196+
var result = MessageBox.Show(messageBoxText, caption, button_, icon_);
197+
return Task.FromResult(GetResult(result));
198+
}
199+
}
200+
#endif
77201
}
78202
}

System.Application.SteamTools.Client.Desktop.Avalonia.App/System.Application.SteamTools.Client.Desktop.Avalonia.App.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
1212
</PropertyGroup>
1313

14+
<PropertyGroup Condition=" $(RuntimeIdentifier.Contains(`win`)) Or ('$(OS)' == 'Windows_NT' And !$(DefineConstants.Contains(`PUBLISH`))) ">
15+
<TargetFramework>net5.0-windows</TargetFramework>
16+
</PropertyGroup>
17+
1418
<ItemGroup>
1519
<Compile Include="..\System.Common.CoreLib\Properties\AssemblyInfo.cs">
1620
<Link>Properties\AssemblyInfo.cs</Link>

System.Application.SteamTools.Client.Desktop.Avalonia.App/Views/MainWindow.axaml.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Avalonia.Markup.Xaml;
44
using System.Application.UI.ViewModels;
55
using System.ComponentModel;
6+
using Avalonia.Platform;
67

78
namespace System.Application.UI.Views
89
{
@@ -16,6 +17,14 @@ public MainWindow()
1617
#endif
1718
}
1819

20+
public MainWindow(IWindowImpl impl) : base(impl)
21+
{
22+
InitializeComponent();
23+
#if DEBUG
24+
this.AttachDevTools();
25+
#endif
26+
}
27+
1928
protected override void OnClosing(CancelEventArgs e)
2029
{
2130
e.Cancel = true;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Threading.Tasks;
2+
using System.Windows;
3+
4+
namespace System.Application.Services
5+
{
6+
/// <summary>
7+
/// System.Windows.MessageBoxCompat.ShowAsync
8+
/// </summary>
9+
public interface IMessageBoxCompatService
10+
{
11+
/// <summary>
12+
/// 显示一个消息框,该消息框包含消息、标题栏标题、按钮和图标,并且返回结果。
13+
/// </summary>
14+
/// <param name="messageBoxText">一个 <see cref="string"/>,用于指定要显示的文本。</param>
15+
/// <param name="caption">一个 <see cref="string"/>,用于指定要显示的标题栏标题。</param>
16+
/// <param name="button">一个 <see cref="MessageBoxButtonCompat"/> 值,用于指定要显示哪个按钮或哪些按钮。</param>
17+
/// <param name="icon">一个 <see cref="MessageBoxImageCompat"/> 值,用于指定要显示的图标。</param>
18+
/// <returns>一个 <see cref="MessageBoxResultCompat"/> 值,用于指定用户单击了哪个消息框按钮。</returns>
19+
Task<MessageBoxResultCompat> ShowAsync(string messageBoxText, string caption, MessageBoxButtonCompat button, MessageBoxImageCompat icon);
20+
}
21+
}

System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/NotifyIconHelper.cs renamed to System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/INotifyIcon.cs

File renamed without changes.

0 commit comments

Comments
 (0)