From cf3cf976c81ee04c99d5e4d1f3e86b0a74fc080c Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 21 May 2023 17:04:08 +0800 Subject: [PATCH 01/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginCard):=20Click?= =?UTF-8?q?=20plugin=20card=20to=20open=20plugin=20detail=20window.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/Controls/PluginCardViewModel.cs | 37 ++++++++++++++++--- Views/Pages/Controls/PluginCard.axaml | 6 ++- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/ViewModels/Pages/Controls/PluginCardViewModel.cs b/ViewModels/Pages/Controls/PluginCardViewModel.cs index 6db915b5..c1a7179e 100644 --- a/ViewModels/Pages/Controls/PluginCardViewModel.cs +++ b/ViewModels/Pages/Controls/PluginCardViewModel.cs @@ -1,10 +1,14 @@ -using Avalonia.Media.Imaging; +using Avalonia.Controls; +using Avalonia.Media.Imaging; using KitX.Web.Rules; using KitX_Dashboard.Data; using KitX_Dashboard.Managers; +using KitX_Dashboard.Views; +using ReactiveUI; using Serilog; using System; using System.IO; +using System.Reactive; namespace KitX_Dashboard.ViewModels.Pages.Controls; @@ -15,20 +19,39 @@ internal class PluginCardViewModel public PluginCardViewModel() { pluginStruct.IconInBase64 = GlobalInfo.KitXIconBase64; - Log.Information($"Icon Loaded: {pluginStruct.IconInBase64}"); + + InitCommands(); + } + + private void InitCommands() + { + ViewDetailsCommand = ReactiveCommand.Create(() => + { + if (Program.MainWindow is not null) + new PluginDetailWindow() + { + WindowStartupLocation = WindowStartupLocation.CenterOwner + } + .SetPluginStruct(pluginStruct) + .Show(Program.MainWindow); + }); } internal string DisplayName => pluginStruct.DisplayName .ContainsKey(ConfigManager.AppConfig.App.AppLanguage) - ? pluginStruct.DisplayName[ConfigManager.AppConfig.App.AppLanguage] - : pluginStruct.DisplayName.Values.GetEnumerator().Current; + ? + pluginStruct.DisplayName[ConfigManager.AppConfig.App.AppLanguage] + : + pluginStruct.DisplayName.Values.GetEnumerator().Current; internal string Version => pluginStruct.Version; internal string SimpleDescription => pluginStruct.SimpleDescription .ContainsKey(ConfigManager.AppConfig.App.AppLanguage) - ? pluginStruct.SimpleDescription[ConfigManager.AppConfig.App.AppLanguage] - : pluginStruct.SimpleDescription.GetEnumerator().Current.Value; + ? + pluginStruct.SimpleDescription[ConfigManager.AppConfig.App.AppLanguage] + : + pluginStruct.SimpleDescription.GetEnumerator().Current.Value; internal string IconInBase64 => pluginStruct.IconInBase64; @@ -59,4 +82,6 @@ internal Bitmap IconDisplay } } } + + internal ReactiveCommand? ViewDetailsCommand { get; set; } } diff --git a/Views/Pages/Controls/PluginCard.axaml b/Views/Pages/Controls/PluginCard.axaml index 67ac7216..dc485766 100644 --- a/Views/Pages/Controls/PluginCard.axaml +++ b/Views/Pages/Controls/PluginCard.axaml @@ -18,14 +18,16 @@ HorizontalAlignment="Right" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" - VerticalContentAlignment="Bottom"> + VerticalContentAlignment="Bottom" + Command="{Binding ViewDetailsCommand}"> + Text="{Binding DisplayName}" + ToolTip.Tip="{Binding DisplayName}"/> Date: Mon, 22 May 2023 15:53:55 +0800 Subject: [PATCH 02/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginsServer):=20Now?= =?UTF-8?q?=20can=20use=20`Send`,=20`Broadcast`=20functions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/PluginsServer.cs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Network/PluginsServer.cs b/Network/PluginsServer.cs index 7f57bd96..52875c44 100644 --- a/Network/PluginsServer.cs +++ b/Network/PluginsServer.cs @@ -168,7 +168,14 @@ public async Task Broadcast(byte[] content) { await Task.Run(() => { - //TODO: 向所有插件广播内容的方法 + foreach (var client in clients.Values) + { + var stream = client.GetStream(); + + stream.Write(content, 0, content.Length); + + stream.Flush(); + } }); return this; @@ -178,7 +185,15 @@ public async Task BroadCast(byte[] content, Func { await Task.Run(() => { - //TODO: 向符合条件的插件广播内容的方法 + foreach (var client in clients.Values) + if (pattern?.Invoke(client) ?? false) + { + var stream = client.GetStream(); + + stream.Write(content, 0, content.Length); + + stream.Flush(); + } }); return this; @@ -188,7 +203,13 @@ public async Task Send(byte[] content, string target) { await Task.Run(() => { - //TODO: 向指定插件发送内容的方法 + var client = clients[target]; + + var stream = client.GetStream(); + + stream.Write(content, 0, content.Length); + + stream.Flush(); }); return this; From 18ea7696482b5495b04d693bf25d24fbdf37a15e Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Mon, 22 May 2023 16:02:04 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginsSystem):=20Mov?= =?UTF-8?q?e=20messages=20processor=20into=20`PluginsNetwork`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/PluginsNetwork.cs | 38 +++++++++++++++++++++++++------------- Network/PluginsServer.cs | 22 +--------------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/Network/PluginsNetwork.cs b/Network/PluginsNetwork.cs index e64a3c3c..22a495b4 100644 --- a/Network/PluginsNetwork.cs +++ b/Network/PluginsNetwork.cs @@ -1,4 +1,5 @@ using Avalonia.Threading; +using Common.BasicHelper.Utils.Extensions; using KitX.Web.Rules; using KitX_Dashboard.Data; using KitX_Dashboard.Models; @@ -26,23 +27,34 @@ internal static void Execute(string msg, IPEndPoint endPoint) try { - var pluginStruct = JsonSerializer.Deserialize(msg); + if (msg.StartsWith("PluginStruct: ")) + { + var json = msg[14..]; + + var pluginStruct = JsonSerializer.Deserialize(json); + + pluginStruct.Tags ??= new(); - pluginStruct.Tags ??= new(); + // 标注实例注册 ID + pluginStruct.Tags.Add("Authorized_ID", + $"{pluginStruct.PublisherName}" + + $"." + + $"{pluginStruct.Name}" + + $"." + + $"{pluginStruct.Version}" + ); - // 标注实例注册 ID - pluginStruct.Tags.Add("Authorized_ID", - $"{pluginStruct.PublisherName}" + - $"." + - $"{pluginStruct.Name}" + - $"." + - $"{pluginStruct.Version}" - ); + // 标注 IPEndPoint + pluginStruct.Tags.Add("IPEndPoint", endPoint.ToString()); - // 标注 IPEndPoint - pluginStruct.Tags.Add("IPEndPoint", endPoint.ToString()); + pluginsToAdd.Enqueue(pluginStruct); - pluginsToAdd.Enqueue(pluginStruct); + var workPath = ConfigManager.AppConfig.App.LocalPluginsDataFolder.GetFullPath(); + var sendtxt = $"WorkPath: {workPath}"; + var bytes = sendtxt.FromUTF8(); + + Program.WebManager?.pluginsServer?.Send(bytes, endPoint.ToString()); + } } catch (Exception e) { diff --git a/Network/PluginsServer.cs b/Network/PluginsServer.cs index 52875c44..6a91e406 100644 --- a/Network/PluginsServer.cs +++ b/Network/PluginsServer.cs @@ -116,27 +116,7 @@ private static void ReceiveMessage(TcpClient client) Log.Information($"From: {endpoint}\tReceive: {msg}"); - if (msg.StartsWith("PluginStruct: ")) - { - PluginsNetwork.Execute(msg[14..], endpoint); - - var workPath = ConfigManager.AppConfig.App.LocalPluginsDataFolder.GetFullPath(); - var sendtxt = $"WorkPath: {workPath}"; - var bytes = sendtxt.FromUTF8(); - - stream?.Write(bytes, 0, bytes.Length); - } - - //发送到其他客户端 - //foreach (KeyValuePair kvp in clients) - //{ - // if (kvp.Value != client) - // { - // byte[] writeData = Encoding.UTF8.GetBytes(msg); - // NetworkStream writeStream = kvp.Value.GetStream(); - // writeStream.Write(writeData, 0, writeData.Length); - // } - //} + PluginsNetwork.Execute(msg, endpoint); } else break; //客户端断开连接 跳出循环 } From cb329ee61903be198cf6788b44b9471a4d1f91c3 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 24 May 2023 19:43:36 +0800 Subject: [PATCH 04/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(NetworkHelper):=20Enh?= =?UTF-8?q?anced=20`NetworkHelper`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/NetworkHelper.cs | 100 ++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/Network/NetworkHelper.cs b/Network/NetworkHelper.cs index e19e78d7..c5df705d 100644 --- a/Network/NetworkHelper.cs +++ b/Network/NetworkHelper.cs @@ -28,6 +28,7 @@ IPInterfaceProperties adapterProperties ) { var userPointed = ConfigManager.AppConfig.Web.AcceptedNetworkInterfaces; + if (userPointed is not null) if (userPointed.Contains(adapter.Name)) return true; @@ -50,15 +51,21 @@ IPInterfaceProperties adapterProperties } /// - /// 将 IPv4 的十进制表示按点分制拆分 + /// 判断 IPv4 地址是否为内部网络地址 /// - /// IPv4 的十进制表示 - /// 拆分 - internal static (int, int, int, int) IPv4_2_4Parts(string ip) + /// 网络地址 + /// 是否为内部网络地址 + internal static bool IsInterNetworkAddressV4(IPAddress address) { - string[] p = ip.Split('.'); - int a = int.Parse(p[0]), b = int.Parse(p[1]), c = int.Parse(p[2]), d = int.Parse(p[3]); - return (a, b, c, d); + var bytes = address.GetAddressBytes(); + + return bytes[0] switch + { + 10 => true, + 172 => bytes[1] <= 31 && bytes[1] >= 16, + 192 => bytes[1] == 168, + _ => false, + }; } /// @@ -67,23 +74,28 @@ internal static (int, int, int, int) IPv4_2_4Parts(string ip) /// 使用点分十进制表示法的本机内网IPv4地址 internal static string GetInterNetworkIPv4() { + var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv4)}"; + try { - return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList - where ip.AddressFamily == AddressFamily.InterNetwork - && !ip.ToString().Equals("127.0.0.1") - && (ip.ToString().StartsWith("192.168") // 192.168.x.x - || ip.ToString().StartsWith("10") // 10.x.x.x - || IPv4_2_4Parts(ip.ToString()).Item1 == 172 // 172.16-31.x.x - && IPv4_2_4Parts(ip.ToString()).Item2 >= 16 - && IPv4_2_4Parts(ip.ToString()).Item2 <= 31) - && ip.ToString().StartsWith(ConfigManager.AppConfig.Web.IPFilter) // 满足自定义规则 - select ip).First().ToString(); + var search = + from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList + where ip.AddressFamily == AddressFamily.InterNetwork + && IsInterNetworkAddressV4(ip) + && !ip.ToString().Equals("127.0.0.1") + && ip.ToString().StartsWith(ConfigManager.AppConfig.Web.IPFilter) + select ip; + + Log.Information($"IPv4 addresses: {search.Print(print: false)}"); + + var result = search.FirstOrDefault()?.ToString(); + + return result ?? string.Empty; } catch (Exception ex) { - var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv4)}"; Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; } } @@ -94,17 +106,26 @@ internal static string GetInterNetworkIPv4() /// IPv6 地址 internal static string GetInterNetworkIPv6() { + var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv6)}"; + try { - return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList - where ip.AddressFamily == AddressFamily.InterNetworkV6 - && !ip.ToString().Equals("::1") - select ip).First().ToString(); + var search = + from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList + where ip.AddressFamily == AddressFamily.InterNetworkV6 + && !ip.ToString().Equals("::1") + select ip; + + Log.Information($"IPv6 addresses: {search.Print(print: false)}"); + + var result = search.FirstOrDefault()?.ToString(); + + return result ?? string.Empty; } catch (Exception ex) { - var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv6)}"; Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; } } @@ -115,19 +136,26 @@ internal static string GetInterNetworkIPv6() /// MAC 地址 internal static string? TryGetDeviceMacAddress() { + var location = $"{nameof(NetworkHelper)}.{nameof(TryGetDeviceMacAddress)}"; + try { - var mac = NetworkInterface.GetAllNetworkInterfaces() - .Where(nic => nic.OperationalStatus == OperationalStatus.Up - && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault(); + var mac = + from nic in NetworkInterface.GetAllNetworkInterfaces() + where nic.OperationalStatus == OperationalStatus.Up + && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback + select nic.GetPhysicalAddress().ToString(); - return mac?.SeparateGroup(2, sb => sb.Append(':')); + var result = mac.FirstOrDefault()?.SeparateGroup( + 2, sb => sb.Append(':') + ); + + return result; } catch (Exception ex) { - var location = $"{nameof(NetworkHelper)}.{nameof(TryGetDeviceMacAddress)}"; Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; } } @@ -138,7 +166,10 @@ internal static string GetInterNetworkIPv6() /// 系统版本 internal static string? TryGetOSVersionString() { + var location = $"{nameof(NetworkHelper)}.{nameof(TryGetOSVersionString)}"; + var result = Environment.OSVersion.VersionString; + try { switch (OperatingSystem2Enum.GetOSType()) @@ -176,6 +207,7 @@ internal static string GetInterNetworkIPv6() } break; + case OperatingSystems.MacOS: var command = "sw_vers"; @@ -183,18 +215,20 @@ internal static string GetInterNetworkIPv6() var productVersion = command.ExecuteAsCommand("-productVersion"); var buildVersion = command.ExecuteAsCommand("-buildVersion"); - if (productName is not null && productVersion is not null && buildVersion is not null) - result = $"{productName} {productVersion} {buildVersion}" - .Replace("\n", ""); + if (productName is null || productVersion is null || buildVersion is null) + break; + + result = $"{productName} {productVersion} {buildVersion}" + .Replace("\n", ""); break; } } catch (Exception ex) { - var location = $"{nameof(NetworkHelper)}.{nameof(TryGetOSVersionString)}"; Log.Error(ex, $"In {location}: {ex.Message}"); } + return result; } From 1377eacd3462c22145a1e01443ca942b81ceb890 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 24 May 2023 19:54:36 +0800 Subject: [PATCH 05/21] =?UTF-8?q?=F0=9F=94=A7=20Fix(Settings=5FPerformence?= =?UTF-8?q?View):=20Fixed=20selected=20network=20interfaces=20display=20er?= =?UTF-8?q?ror.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ViewModels/Pages/Controls/Settings_PerformenceViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ViewModels/Pages/Controls/Settings_PerformenceViewModel.cs b/ViewModels/Pages/Controls/Settings_PerformenceViewModel.cs index e6c14ed9..d39d232f 100644 --- a/ViewModels/Pages/Controls/Settings_PerformenceViewModel.cs +++ b/ViewModels/Pages/Controls/Settings_PerformenceViewModel.cs @@ -232,7 +232,7 @@ internal static string AcceptedNetworkInterfacesNames if (userPointed is null) return "Auto"; else - return userPointed.ToCustomString(";")[..^1]; + return userPointed.ToCustomString(";"); } set { From 0fff38369cb66135bb2082738332234533365205 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 24 May 2023 20:00:31 +0800 Subject: [PATCH 06/21] =?UTF-8?q?=F0=9F=8E=87=20Style:=20Enhanced=20codes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Data/GlobalInfo.cs | 12 ++++++------ KitX Dashboard.csproj | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Data/GlobalInfo.cs b/Data/GlobalInfo.cs index bd5d35fe..388d679b 100644 --- a/Data/GlobalInfo.cs +++ b/Data/GlobalInfo.cs @@ -21,17 +21,17 @@ internal static class GlobalInfo internal const string UpdateSavePath = "./Update/"; - internal const string configFilePath = $"{ConfigPath}config.json"; + internal const string IconBase64FileName = "KitX.Base64.txt"; - internal const string pluginsListConfigFilePath = $"{ConfigPath}plugins.json"; + internal const int LastBreakAfterExit = 2000; - internal const string activitiesDataBaseFilePath = $"{DataPath}Activities.db"; + private const string configFilePath = $"{ConfigPath}config.json"; - internal const string thirdPartLicenseFilePath = $"{AssetsPath}ThirdPartLicense.md"; + private const string pluginsListConfigFilePath = $"{ConfigPath}plugins.json"; - internal const string IconBase64FileName = "KitX.Base64.txt"; + private const string activitiesDataBaseFilePath = $"{DataPath}Activities.db"; - internal const int LastBreakAfterExit = 2000; + private const string thirdPartLicenseFilePath = $"{AssetsPath}ThirdPartLicense.md"; internal static string ConfigFilePath => configFilePath.GetFullPath(); diff --git a/KitX Dashboard.csproj b/KitX Dashboard.csproj index e10412fe..f70fff1b 100644 --- a/KitX Dashboard.csproj +++ b/KitX Dashboard.csproj @@ -9,7 +9,9 @@ true Assets\KitX-Icon-256x.ico en + + $(Version) $(Version) 3.23.04.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2005-06-06"))).TotalDays) @@ -19,10 +21,6 @@ IsBuild4WindowsPlatform - - False @@ -57,6 +55,9 @@ + + + From ba2dd613bdfe19e0e1c24b251ee3c521e41f6806 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 24 May 2023 23:34:12 +0800 Subject: [PATCH 07/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(DeviceCard):=20Wider?= =?UTF-8?q?=20and=20larger.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/Pages/Controls/DeviceCard.axaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Views/Pages/Controls/DeviceCard.axaml b/Views/Pages/Controls/DeviceCard.axaml index 719fab72..176f204a 100644 --- a/Views/Pages/Controls/DeviceCard.axaml +++ b/Views/Pages/Controls/DeviceCard.axaml @@ -14,9 +14,9 @@ - private static void KeepCheckAndRemove() { + var location = $"{nameof(DevicesNetwork)}.{nameof(KeepCheckAndRemove)}"; + var timer = new Timer() { Interval = ConfigManager.AppConfig.Web.DevicesViewRefreshDelay, @@ -234,19 +207,14 @@ private static void KeepCheckAndRemove() { try { - var location = $"{nameof(DevicesServer)}.{nameof(KeepCheckAndRemove)}()"; if (KeepCheckAndRemoveTaskRunning) Log.Information($"In {location}: Timer elapsed and skip task."); else { - Log.Information($"In {location}: Timer elapsed and run task."); - KeepCheckAndRemoveTaskRunning = true; UpdateSourceAndAddCards(); - //RemoveDumplicatedCards(); - if (!ConfigManager.AppConfig.Web.DisableRemovingOfflineDeviceCard) RemoveOfflineCards(); @@ -257,7 +225,6 @@ private static void KeepCheckAndRemove() } catch (Exception ex) { - var location = $"{nameof(DevicesNetwork)}.{nameof(KeepCheckAndRemove)}()"; Log.Error(ex, $"In {location}: {ex.Message}"); } }; From 065b46d1ec000a7401aa9bc0df7cc7ee3435fa30 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 24 May 2023 23:38:02 +0800 Subject: [PATCH 09/21] =?UTF-8?q?=F0=9F=8E=87=20Style(DeviceCard):=20Short?= =?UTF-8?q?er=20string=20cutter=20for=20device=20name.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/Pages/Controls/DeviceCard.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Views/Pages/Controls/DeviceCard.axaml b/Views/Pages/Controls/DeviceCard.axaml index 176f204a..fa383841 100644 --- a/Views/Pages/Controls/DeviceCard.axaml +++ b/Views/Pages/Controls/DeviceCard.axaml @@ -27,7 +27,7 @@ FontFamily="{StaticResource SourceHanSans}" FontSize="20" FontWeight="ExtraBold" - Text="{Binding DeviceName, Converter={StaticResource WideString}, ConverterParameter=28}" + Text="{Binding DeviceName, Converter={StaticResource WideString}, ConverterParameter=20}" ToolTip.Tip="{Binding DeviceName}"/> Date: Fri, 26 May 2023 14:12:55 +0800 Subject: [PATCH 10/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginDetailWindow):?= =?UTF-8?q?=20If=20not=20running=20on=20`MS-Windows`,=20use=20theme=20colo?= =?UTF-8?q?r=20as=20background.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/PluginDetailWindow.axaml.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Views/PluginDetailWindow.axaml.cs b/Views/PluginDetailWindow.axaml.cs index 07e237a1..cc7dbe2a 100644 --- a/Views/PluginDetailWindow.axaml.cs +++ b/Views/PluginDetailWindow.axaml.cs @@ -1,8 +1,10 @@ using Avalonia.Controls; +using Avalonia.Media; using Common.BasicHelper.Graphics.Screen; using KitX.Web.Rules; using KitX_Dashboard.Services; using KitX_Dashboard.ViewModels; +using System; namespace KitX_Dashboard.Views; @@ -27,6 +29,11 @@ public PluginDetailWindow() Width = suggest.Width ?? 820; Height = suggest.Height ?? 500; + if (OperatingSystem.IsWindows() == false) + { + Background = Resources["ThemePrimaryAccent"] as SolidColorBrush; + } + KeyDown += (x, y) => { switch (y.Key) From ed9e47d07ef6be91e9cf1858d6b8d78559d4cf3f Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Fri, 26 May 2023 14:25:03 +0800 Subject: [PATCH 11/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginDetailWindow):?= =?UTF-8?q?=20Use=20`try-catch`=20to=20catch=20error=20in=20setting=20back?= =?UTF-8?q?ground.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/PluginDetailWindow.axaml.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Views/PluginDetailWindow.axaml.cs b/Views/PluginDetailWindow.axaml.cs index cc7dbe2a..5719406e 100644 --- a/Views/PluginDetailWindow.axaml.cs +++ b/Views/PluginDetailWindow.axaml.cs @@ -4,6 +4,7 @@ using KitX.Web.Rules; using KitX_Dashboard.Services; using KitX_Dashboard.ViewModels; +using Serilog; using System; namespace KitX_Dashboard.Views; @@ -14,6 +15,8 @@ public partial class PluginDetailWindow : Window public PluginDetailWindow() { + var location = $"{nameof(PluginDetailWindow)}.ctor"; + InitializeComponent(); Resources["ThisWindow"] = this; @@ -31,7 +34,14 @@ public PluginDetailWindow() if (OperatingSystem.IsWindows() == false) { - Background = Resources["ThemePrimaryAccent"] as SolidColorBrush; + try + { + Background = Resources["ThemePrimaryAccent"] as SolidColorBrush; + } + catch (Exception ex) + { + Log.Warning(ex, $"In {location}: {ex.Message}"); + } } KeyDown += (x, y) => From 5ebc5af041c89c67e032c921be4d2127948905de Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 28 May 2023 10:51:41 +0800 Subject: [PATCH 12/21] =?UTF-8?q?=F0=9F=8E=87=20Style(WebManager):=20Cut?= =?UTF-8?q?=20usings.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/WebManager.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index 2860f7b2..eaa6347c 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -1,5 +1,4 @@ -using Common.BasicHelper.Core.TaskSystem; -using KitX_Dashboard.Network; +using KitX_Dashboard.Network; using Serilog; using System; using System.Collections.ObjectModel; @@ -9,16 +8,16 @@ namespace KitX_Dashboard.Managers; public class WebManager : IDisposable { - public WebManager() - { - NetworkInterfaceRegistered = new(); - } - internal PluginsServer? pluginsServer; internal DevicesDiscoveryServer? devicesDiscoveryServer; internal ObservableCollection? NetworkInterfaceRegistered; + public WebManager() + { + NetworkInterfaceRegistered = new(); + } + /// /// 开始执行网络相关服务 /// From c430a52473316646fd24ba34281756fd12ea77f1 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 28 May 2023 10:53:32 +0800 Subject: [PATCH 13/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(Views,=20ViewModels):?= =?UTF-8?q?=20New=20window=20`PluginsLaunchWindow`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ViewModels/PluginsLaunchWindowViewModel.cs | 11 ++++++ Views/PluginsLaunchWindow.axaml | 24 ++++++++++++ Views/PluginsLaunchWindow.axaml.cs | 43 ++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 ViewModels/PluginsLaunchWindowViewModel.cs create mode 100644 Views/PluginsLaunchWindow.axaml create mode 100644 Views/PluginsLaunchWindow.axaml.cs diff --git a/ViewModels/PluginsLaunchWindowViewModel.cs b/ViewModels/PluginsLaunchWindowViewModel.cs new file mode 100644 index 00000000..954a4579 --- /dev/null +++ b/ViewModels/PluginsLaunchWindowViewModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KitX_Dashboard.ViewModels; + +internal class PluginsLaunchWindowViewModel : ViewModelBase +{ +} diff --git a/Views/PluginsLaunchWindow.axaml b/Views/PluginsLaunchWindow.axaml new file mode 100644 index 00000000..e6a768eb --- /dev/null +++ b/Views/PluginsLaunchWindow.axaml @@ -0,0 +1,24 @@ + + + + Developing ... + + + diff --git a/Views/PluginsLaunchWindow.axaml.cs b/Views/PluginsLaunchWindow.axaml.cs new file mode 100644 index 00000000..db590ea2 --- /dev/null +++ b/Views/PluginsLaunchWindow.axaml.cs @@ -0,0 +1,43 @@ +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using KitX_Dashboard.ViewModels; +using Serilog; +using System; + +namespace KitX_Dashboard.Views; + +public partial class PluginsLaunchWindow : Window +{ + private readonly PluginsLaunchWindowViewModel viewModel = new(); + + public PluginsLaunchWindow() + { + var location = $"{nameof(PluginsLaunchWindow)}.ctor"; + + InitializeComponent(); + + DataContext = viewModel; + + if (OperatingSystem.IsWindows() == false) + { + try + { + Background = Resources["ThemePrimaryAccent"] as SolidColorBrush; + } + catch (Exception ex) + { + Log.Warning(ex, $"In {location}: {ex.Message}"); + } + } + } + + private void PluginsLaunchWindow_PointerPressed(object? sender, PointerPressedEventArgs e) + => BeginMoveDrag(e); + + private void PluginsLaunchWindow_KeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + Close(); + } +} From c680dfbcebd27a373aa2f590085403b86e92dfb9 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 28 May 2023 10:54:33 +0800 Subject: [PATCH 14/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(HotKeyManager):=20Ena?= =?UTF-8?q?ble=20global=20hot=20key=20manager=20and=20init=20at=20startup.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helper.cs | 6 ++++ KitX Dashboard.csproj | 1 + Managers/HotKeyManager.cs | 62 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 Managers/HotKeyManager.cs diff --git a/Helper.cs b/Helper.cs index 308fcf08..7265f046 100644 --- a/Helper.cs +++ b/Helper.cs @@ -172,6 +172,12 @@ public static void StartUpCheck() #endregion + #region 初始化热键系统 + + HotKeyManager.RegisterPluginLaunchWindowHotKey(); + + #endregion + } /// diff --git a/KitX Dashboard.csproj b/KitX Dashboard.csproj index f70fff1b..b6251252 100644 --- a/KitX Dashboard.csproj +++ b/KitX Dashboard.csproj @@ -103,6 +103,7 @@ + diff --git a/Managers/HotKeyManager.cs b/Managers/HotKeyManager.cs new file mode 100644 index 00000000..ab8809d8 --- /dev/null +++ b/Managers/HotKeyManager.cs @@ -0,0 +1,62 @@ +using Avalonia.Threading; +using Common.BasicHelper.Utils.Extensions; +using KitX_Dashboard.Views; +using SharpHook; +using SharpHook.Native; +using System.Collections.Generic; + +namespace KitX_Dashboard.Managers; + +internal class HotKeyManager +{ + private const int keysLimitation = 5; + + private static readonly Queue keyPressed = new(); + + public static void RegisterPluginLaunchWindowHotKey() + { + var hook = new TaskPoolGlobalHook(); + + hook.KeyPressed += (_, args) => + { + keyPressed.Enqueue(args.Data.KeyCode); + + if (keyPressed.Count > keysLimitation) + _ = keyPressed.Dequeue(); + + VerifyKeys(); + }; + + hook.RunAsync(); + } + + public static void VerifyKeys() + { + // Ctrl + Win + C: fot test + + var index = 0; + + var count = keyPressed.Count; + + var tmpList = new KeyCode[keysLimitation]; + + keyPressed.ForEach(x => + { + tmpList[index] = x; + + ++index; + + }, true); + + if (count >= 3 && + tmpList[count - 3] == KeyCode.VcLeftControl && + tmpList[count - 2] == KeyCode.VcLeftMeta && + tmpList[count - 1] == KeyCode.VcC) + { + Dispatcher.UIThread.Post(() => + { + new PluginsLaunchWindow().Show(); + }); + } + } +} From 353bf834462842d13fbcb5d6e98ae5bfb15b6180 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:41:43 +0800 Subject: [PATCH 15/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(HotKeyManager):=20Dec?= =?UTF-8?q?oupling=20the=20view=20part.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/HotKeyManager.cs | 57 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/Managers/HotKeyManager.cs b/Managers/HotKeyManager.cs index ab8809d8..9cbc6980 100644 --- a/Managers/HotKeyManager.cs +++ b/Managers/HotKeyManager.cs @@ -1,8 +1,7 @@ -using Avalonia.Threading; -using Common.BasicHelper.Utils.Extensions; -using KitX_Dashboard.Views; +using Common.BasicHelper.Utils.Extensions; using SharpHook; using SharpHook.Native; +using System; using System.Collections.Generic; namespace KitX_Dashboard.Managers; @@ -11,36 +10,43 @@ internal class HotKeyManager { private const int keysLimitation = 5; - private static readonly Queue keyPressed = new(); + private readonly Queue? keyPressed; - public static void RegisterPluginLaunchWindowHotKey() + private readonly Dictionary>? hotKeyHandlers; + + public HotKeyManager() + { + keyPressed = new(); + + hotKeyHandlers = new(); + } + + public HotKeyManager Hook() { var hook = new TaskPoolGlobalHook(); hook.KeyPressed += (_, args) => { - keyPressed.Enqueue(args.Data.KeyCode); + keyPressed!.Enqueue(args.Data.KeyCode); - if (keyPressed.Count > keysLimitation) + if (keyPressed!.Count > keysLimitation) _ = keyPressed.Dequeue(); VerifyKeys(); }; hook.RunAsync(); + + return this; } - public static void VerifyKeys() + private void VerifyKeys() { - // Ctrl + Win + C: fot test - var index = 0; - var count = keyPressed.Count; - var tmpList = new KeyCode[keysLimitation]; - keyPressed.ForEach(x => + keyPressed!.ForEach(x => { tmpList[index] = x; @@ -48,15 +54,24 @@ public static void VerifyKeys() }, true); - if (count >= 3 && - tmpList[count - 3] == KeyCode.VcLeftControl && - tmpList[count - 2] == KeyCode.VcLeftMeta && - tmpList[count - 1] == KeyCode.VcC) + foreach (var handler in hotKeyHandlers!.Values) + handler.Invoke(tmpList); + } + + public HotKeyManager RegisterHotKeyHandler(string name, Action handler) + { + hotKeyHandlers!.Add(name, handler); + + return this; + } + + public HotKeyManager UnregisterHotKeyHandler(string name) + { + if (hotKeyHandlers!.TryGetValue(name, out _)) { - Dispatcher.UIThread.Post(() => - { - new PluginsLaunchWindow().Show(); - }); + hotKeyHandlers.Remove(name); } + + return this; } } From 2b6b3f6fcaf78d5a85cfc18d1e9ae91c28dd7b2c Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:43:35 +0800 Subject: [PATCH 16/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(Program):=20Init=20`H?= =?UTF-8?q?otKeyManager`=20instance=20app=20scope.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helper.cs | 2 +- Program.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Helper.cs b/Helper.cs index 7265f046..381d6bd9 100644 --- a/Helper.cs +++ b/Helper.cs @@ -174,7 +174,7 @@ public static void StartUpCheck() #region 初始化热键系统 - HotKeyManager.RegisterPluginLaunchWindowHotKey(); + Program.HotKeyManager = new HotKeyManager().Hook(); #endregion diff --git a/Program.cs b/Program.cs index 5684122c..41671b33 100644 --- a/Program.cs +++ b/Program.cs @@ -28,6 +28,8 @@ internal class Program internal static CacheManager? CacheManager; + internal static HotKeyManager? HotKeyManager; + internal static MainWindow? MainWindow; /// From 773ee0b57a1ef502f439f44a986cc77f8219cc26 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:46:15 +0800 Subject: [PATCH 17/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginsLaunchWindow):?= =?UTF-8?q?=20Accept=20on=20hide=20action,=20register=20hot=20key=20at=20c?= =?UTF-8?q?onstructor.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/PluginsLaunchWindow.axaml.cs | 53 +++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Views/PluginsLaunchWindow.axaml.cs b/Views/PluginsLaunchWindow.axaml.cs index db590ea2..1b357c5b 100644 --- a/Views/PluginsLaunchWindow.axaml.cs +++ b/Views/PluginsLaunchWindow.axaml.cs @@ -11,6 +11,10 @@ public partial class PluginsLaunchWindow : Window { private readonly PluginsLaunchWindowViewModel viewModel = new(); + private Action? OnHideAction; + + private bool pluginsLaunchWindowDisplayed = false; + public PluginsLaunchWindow() { var location = $"{nameof(PluginsLaunchWindow)}.ctor"; @@ -19,6 +23,8 @@ public PluginsLaunchWindow() DataContext = viewModel; + OnHide(() => pluginsLaunchWindowDisplayed = false); + if (OperatingSystem.IsWindows() == false) { try @@ -30,6 +36,47 @@ public PluginsLaunchWindow() Log.Warning(ex, $"In {location}: {ex.Message}"); } } + + RegisterGlobalHotKey(); + } + + private void RegisterGlobalHotKey() + { + Program.HotKeyManager?.RegisterHotKeyHandler("", codes => + { + var count = codes.Length; + + var tmpList = codes; + + if (count >= 3 && + tmpList[count - 3] == KeyCode.VcLeftControl && + tmpList[count - 2] == KeyCode.VcLeftMeta && + tmpList[count - 1] == KeyCode.VcC) + { + Dispatcher.UIThread.Post(() => + { + if (pluginsLaunchWindowDisplayed) + { + Activate(); + + Focus(); + } + else + { + Show(); + } + + pluginsLaunchWindowDisplayed = true; + }); + } + }); + } + + public PluginsLaunchWindow OnHide(Action onHideAction) + { + OnHideAction = onHideAction; + + return this; } private void PluginsLaunchWindow_PointerPressed(object? sender, PointerPressedEventArgs e) @@ -38,6 +85,10 @@ private void PluginsLaunchWindow_PointerPressed(object? sender, PointerPressedEv private void PluginsLaunchWindow_KeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.Escape) - Close(); + { + Hide(); + + OnHideAction?.Invoke(); + } } } From a211c395c5af9c4eacd2b96f67836668533437ea Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:46:42 +0800 Subject: [PATCH 18/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginsLaunchWindow):?= =?UTF-8?q?=20Close=20at=20app=20exiting.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/PluginsLaunchWindow.axaml.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Views/PluginsLaunchWindow.axaml.cs b/Views/PluginsLaunchWindow.axaml.cs index 1b357c5b..842eb45c 100644 --- a/Views/PluginsLaunchWindow.axaml.cs +++ b/Views/PluginsLaunchWindow.axaml.cs @@ -1,8 +1,11 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Threading; +using KitX_Dashboard.Services; using KitX_Dashboard.ViewModels; using Serilog; +using SharpHook.Native; using System; namespace KitX_Dashboard.Views; @@ -25,6 +28,8 @@ public PluginsLaunchWindow() OnHide(() => pluginsLaunchWindowDisplayed = false); + EventService.OnExiting += Close; + if (OperatingSystem.IsWindows() == false) { try @@ -70,7 +75,7 @@ private void RegisterGlobalHotKey() }); } }); - } + } public PluginsLaunchWindow OnHide(Action onHideAction) { From 4c68cd7e064652ceec65757ad72a4f889c09fde9 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:49:30 +0800 Subject: [PATCH 19/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(PluginsLaunchWindow):?= =?UTF-8?q?=20Default=20with=20top=20most=20set=20to=20true.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Views/PluginsLaunchWindow.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Views/PluginsLaunchWindow.axaml b/Views/PluginsLaunchWindow.axaml index e6a768eb..438c0941 100644 --- a/Views/PluginsLaunchWindow.axaml +++ b/Views/PluginsLaunchWindow.axaml @@ -13,6 +13,7 @@ Icon="avares://KitX.Assets/KitX-Icon-32x32.png" KeyDown="PluginsLaunchWindow_KeyDown" PointerPressed="PluginsLaunchWindow_PointerPressed" + Topmost="True" TransparencyLevelHint="AcrylicBlur" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> From e7fd002f42a2893d90a3732919ee333589a9aac3 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Tue, 30 May 2023 14:50:15 +0800 Subject: [PATCH 20/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(App):=20Global=20plug?= =?UTF-8?q?ins=20launch=20window=20instance=20in=20program,=20and=20init?= =?UTF-8?q?=20when=20main=20windows=20init.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helper.cs | 15 ++++++++++++++- Program.cs | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Helper.cs b/Helper.cs index 381d6bd9..8468083a 100644 --- a/Helper.cs +++ b/Helper.cs @@ -1,4 +1,5 @@ -using Common.BasicHelper.IO; +using Avalonia.Threading; +using Common.BasicHelper.IO; using Common.BasicHelper.Utils.Extensions; using KitX_Dashboard.Data; using KitX_Dashboard.Managers; @@ -178,6 +179,18 @@ public static void StartUpCheck() #endregion + #region 初始化持久的窗口 + + Program.SignalTasksManager.SignalRun(nameof(SignalsNames.MainWindowInitSignal), () => + { + Dispatcher.UIThread.Post(() => + { + Program.PluginsLaunchWindow = new(); + }); + }); + + #endregion + } /// diff --git a/Program.cs b/Program.cs index 41671b33..db658c01 100644 --- a/Program.cs +++ b/Program.cs @@ -32,6 +32,8 @@ internal class Program internal static MainWindow? MainWindow; + internal static PluginsLaunchWindow? PluginsLaunchWindow; + /// /// 主函数, 应用程序入口; 展开 summary 查看警告 /// From 26bd227545b0fbf838f48ae8b3aebd2105922111 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Wed, 14 Jun 2023 22:19:12 +0800 Subject: [PATCH 21/21] =?UTF-8?q?=F0=9F=92=BE=20Feat(MainWindow):=20More?= =?UTF-8?q?=20info=20in=20tray=20icon=20tip=20text.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ViewModels/MainWindowViewModel.cs | 51 +++++++++++++++++++++++++++++-- Views/MainWindow.axaml | 2 +- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index baed8615..f23f26d8 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -1,19 +1,26 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.Controls; using KitX_Dashboard.Data; using KitX_Dashboard.Managers; using KitX_Dashboard.Services; using KitX_Dashboard.Views; using ReactiveUI; +using System.ComponentModel; using System.Reactive; +using System.Reflection; +using System.Text; namespace KitX_Dashboard.ViewModels; -internal class MainWindowViewModel : ViewModelBase +internal class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged { + public new event PropertyChangedEventHandler? PropertyChanged; public MainWindowViewModel() { InitCommands(); + + InitEvents(); } internal void InitCommands() @@ -53,6 +60,46 @@ internal void InitCommands() }); } + private void InitEvents() + { + Program.DeviceCards.CollectionChanged += (_, _) => + { + PropertyChanged?.Invoke(this, new(nameof(TrayIconText))); + }; + } + + internal static string TrayIconText + { + get + { + var sb = new StringBuilder(); + + sb.AppendLine( + FetchStringFromResource(Application.Current, "Text_MainWindow_Title") ?? "KitX" + ); + + sb.AppendLine($"v{Assembly.GetEntryAssembly()?.GetName().Version}"); + + sb.AppendLine(); + + sb.AppendLine( + $"{Program.DeviceCards.Count} " + + $"{FetchStringFromResource(Application.Current, "Text_Device_Tip_Detected")}" + ); + + sb.AppendLine( + $"{Program.PluginCards.Count} " + + $"{FetchStringFromResource(Application.Current, "Text_Lib_Tip_Connected")}" + ); + + sb.AppendLine(); + + sb.Append("Hello, World!"); + + return sb.ToString(); + } + } + internal ReactiveCommand? TrayIconClickedCommand { get; set; } internal ReactiveCommand? ExitCommand { get; set; } diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml index b5f157e6..2f32efe3 100644 --- a/Views/MainWindow.axaml +++ b/Views/MainWindow.axaml @@ -42,7 +42,7 @@ CommandParameter="{DynamicResource MainWindow}" Icon="resm:KitX_Dashboard.Assets.KitX-Icon-256x256.ico" IsVisible="True" - ToolTipText="{DynamicResource Text_MainWindow_Title}"> + ToolTipText="{Binding TrayIconText}">