Skip to content

Commit db0d415

Browse files
author
user123
committed
add
1 parent e9edc1a commit db0d415

File tree

7 files changed

+239
-49
lines changed

7 files changed

+239
-49
lines changed

Services.App/App.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
x:Class="Services.App.App"
33
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
44
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5-
xmlns:local="using:Services.App">
5+
xmlns:local="using:Services.App"
6+
xmlns:tb="using:H.NotifyIcon">
67
<Application.Resources>
78
<ResourceDictionary>
89
<ResourceDictionary.MergedDictionaries>

Services.App/App.xaml.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
1919
m_window.Activate();
2020
}
2121

22+
23+
2224
private Window? m_window;
2325
}
2426
}

Services.App/MainWindow.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
66
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
77
xmlns:local="using:Services.App"
8+
xmlns:tb="using:H.NotifyIcon"
89
xmlns:models="using:Services.Core.Models"
910
mc:Ignorable="d">
1011

Services.App/MainWindow.xaml.cs

Lines changed: 179 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ namespace Services.App
1818
{
1919
public sealed partial class MainWindow : Window
2020
{
21-
private System.Windows.Forms.NotifyIcon? _notifyIcon;
22-
21+
private H.NotifyIcon.TaskbarIcon? TrayIcon;
2322
private readonly WindowsServiceManager _serviceManager;
2423
private readonly EnvironmentManager _envManager;
2524
private readonly LogManager _logManager;
@@ -36,94 +35,127 @@ public MainWindow()
3635
ExtendsContentIntoTitleBar = true;
3736
SetTitleBar(AppTitleBar);
3837

39-
InitializeTrayIcon();
40-
4138
var hWnd = WindowNative.GetWindowHandle(this);
4239
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
4340
_appWindow = AppWindow.GetFromWindowId(windowId);
44-
_appWindow.Closing += OnAppWindowClosing;
4541
_appWindow.Resize(new Windows.Graphics.SizeInt32(1800, 1200));
4642

43+
// Hide window instead of closing
44+
_appWindow.Closing += (s, args) =>
45+
{
46+
if (!_isRealExit)
47+
{
48+
args.Cancel = true;
49+
_appWindow.Hide();
50+
}
51+
};
52+
4753
_serviceManager = new WindowsServiceManager();
4854
_serviceManager.ServiceUpdated += OnServiceUpdated;
4955

5056
_envManager = new EnvironmentManager();
5157
_logManager = new LogManager();
5258

59+
InitializeTrayIcon();
60+
5361
LoadServices();
5462
Title = "ServicesApp";
5563

5664
this.Closed += (s, e) => _serviceManager.Dispose();
65+
66+
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
67+
timer.Tick += (s, e) => LoadServices(true);
68+
timer.Start();
5769
}
5870

5971
private void InitializeTrayIcon()
6072
{
6173
try
6274
{
63-
_notifyIcon = new System.Windows.Forms.NotifyIcon();
64-
_notifyIcon.Text = "ServicesApp";
75+
TrayIcon = new H.NotifyIcon.TaskbarIcon();
76+
TrayIcon.ToolTipText = "ServicesApp";
6577

78+
// Use absolute path for icon
6679
var iconPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Assets", "icon.ico");
6780
if (System.IO.File.Exists(iconPath))
6881
{
69-
_notifyIcon.Icon = new System.Drawing.Icon(iconPath);
70-
}
71-
else
72-
{
73-
_notifyIcon.Icon = System.Drawing.SystemIcons.Application;
82+
TrayIcon.IconSource = new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri(iconPath));
7483
}
75-
76-
_notifyIcon.Visible = true;
77-
_notifyIcon.DoubleClick += (s, e) => OnTrayOpenClick(null, null);
78-
79-
var contextMenu = new System.Windows.Forms.ContextMenuStrip();
80-
var showItem = new System.Windows.Forms.ToolStripMenuItem("显示窗口");
81-
showItem.Click += (s, e) => OnTrayOpenClick(null, null);
8284

83-
var exitItem = new System.Windows.Forms.ToolStripMenuItem("退出");
84-
exitItem.Click += (s, e) =>
85-
{
86-
_isRealExit = true;
87-
_notifyIcon.Visible = false;
88-
_notifyIcon.Dispose();
89-
Application.Current.Exit();
90-
};
85+
// Important: Add to visual tree FIRST to ensure it has a XamlRoot and Dispatcher
86+
RootGrid.Children.Add(TrayIcon);
9187

92-
contextMenu.Items.Add(showItem);
93-
contextMenu.Items.Add(new System.Windows.Forms.ToolStripSeparator());
94-
contextMenu.Items.Add(exitItem);
88+
// Use SecondWindow mode to support context menu in unpackaged apps
89+
TrayIcon.ContextMenuMode = H.NotifyIcon.ContextMenuMode.SecondWindow;
90+
TrayIcon.NoLeftClickDelay = true; // Improve responsiveness
9591

96-
_notifyIcon.ContextMenuStrip = contextMenu;
92+
// Setup events
93+
TrayIcon.DoubleTapped += (s, e) => ShowWindow();
94+
95+
// Initialize Flyout
96+
var flyout = new MenuFlyout();
97+
flyout.AreOpenCloseAnimationsEnabled = false; // Disable animations for better performance
98+
99+
// Use standard style with necessary adjustments only
100+
var presenterStyle = new Style(typeof(MenuFlyoutPresenter));
101+
presenterStyle.Setters.Add(new Setter(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled));
102+
presenterStyle.Setters.Add(new Setter(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled));
103+
presenterStyle.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(4)));
104+
presenterStyle.Setters.Add(new Setter(Control.CornerRadiusProperty, new CornerRadius(4)));
105+
presenterStyle.Setters.Add(new Setter(FrameworkElement.MaxWidthProperty, 240));
106+
107+
flyout.MenuFlyoutPresenterStyle = presenterStyle;
108+
flyout.Placement = Microsoft.UI.Xaml.Controls.Primitives.FlyoutPlacementMode.Top; // Prefer top placement
109+
110+
var showItem = new MenuFlyoutItem { Text = "显示窗口", Icon = new FontIcon { Glyph = "\uE7F4" } };
111+
showItem.Click += OnShowWindowClick;
112+
113+
var exitItem = new MenuFlyoutItem { Text = "退出", Icon = new FontIcon { Glyph = "\uE711" } };
114+
exitItem.Click += OnExitClick;
115+
116+
flyout.Items.Add(showItem);
117+
flyout.Items.Add(new MenuFlyoutSeparator());
118+
flyout.Items.Add(exitItem);
119+
120+
TrayIcon.ContextFlyout = flyout;
121+
122+
// Ensure icon is created
123+
TrayIcon.ForceCreate();
124+
125+
// Ensure icon is visible
126+
TrayIcon.Visibility = Visibility.Visible;
97127
}
98128
catch (Exception ex)
99129
{
100130
System.Diagnostics.Debug.WriteLine($"Tray Icon Init Failed: {ex}");
101131
}
102132
}
103133

104-
private void OnAppWindowClosing(AppWindow sender, AppWindowClosingEventArgs args)
105-
{
106-
if (!_isRealExit)
107-
{
108-
args.Cancel = true;
109-
_appWindow.Hide();
110-
}
111-
}
112-
113-
private void OnTrayOpenClick(object? sender, object? e)
134+
public void ShowWindow()
114135
{
115136
_appWindow.Show();
137+
_appWindow.MoveInZOrderAtTop();
116138
this.Activate();
117139
}
118140

119-
private void OnTrayExitClick(object sender, RoutedEventArgs e)
141+
public void RealExit()
120142
{
121143
_isRealExit = true;
122-
_notifyIcon?.Dispose();
123-
144+
TrayIcon?.Dispose();
124145
Application.Current.Exit();
125146
}
126147

148+
private void OnShowWindowClick(object sender, RoutedEventArgs e)
149+
{
150+
ShowWindow();
151+
}
152+
153+
private void OnExitClick(object sender, RoutedEventArgs e)
154+
{
155+
RealExit();
156+
}
157+
158+
127159
private void OnServiceUpdated(object? sender, Service service)
128160
{
129161
this.DispatcherQueue.TryEnqueue(() =>
@@ -457,7 +489,109 @@ private async void OnEnvVarsClick(object sender, RoutedEventArgs e)
457489

458490
private async void OnEditClick(object sender, RoutedEventArgs e)
459491
{
460-
await ShowDialog("提示", "编辑功能暂未实现。");
492+
if (sender is Button btn && btn.Tag is string serviceId)
493+
{
494+
var service = Services.FirstOrDefault(s => s.Id == serviceId);
495+
if (service == null) return;
496+
497+
var dialog = new ContentDialog
498+
{
499+
Title = $"编辑服务 - {service.Name}",
500+
PrimaryButtonText = "保存",
501+
CloseButtonText = "取消",
502+
XamlRoot = this.Content.XamlRoot
503+
};
504+
505+
var stack = new StackPanel { Spacing = 10 };
506+
var nameBox = new TextBox { Header = "服务名称 (不可修改)", Text = service.Name, IsReadOnly = true };
507+
var exeBox = new TextBox { Header = "可执行文件路径", Text = service.ExePath, PlaceholderText = "C:\\Path\\To\\App.exe" };
508+
var argsBox = new TextBox { Header = "启动参数 (可选)", Text = service.Args ?? "" };
509+
var workDirBox = new TextBox { Header = "工作目录 (可选)", Text = service.WorkingDir ?? "" };
510+
511+
var browseBtn = new Button { Content = "浏览..." };
512+
browseBtn.Click += async (s, args) => {
513+
string? pickedPath = null;
514+
try
515+
{
516+
var picker = new FileOpenPicker();
517+
picker.ViewMode = PickerViewMode.List;
518+
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
519+
picker.FileTypeFilter.Add(".exe");
520+
picker.FileTypeFilter.Add(".bat");
521+
picker.FileTypeFilter.Add(".cmd");
522+
523+
var hwnd = WindowNative.GetWindowHandle(this);
524+
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
525+
526+
var file = await picker.PickSingleFileAsync();
527+
if (file != null) pickedPath = file.Path;
528+
}
529+
catch (Exception ex)
530+
{
531+
System.Diagnostics.Debug.WriteLine($"WinUI Picker failed: {ex}");
532+
pickedPath = await PickFileWithPowerShell();
533+
}
534+
535+
if (pickedPath != null)
536+
{
537+
exeBox.Text = pickedPath;
538+
if (string.IsNullOrWhiteSpace(workDirBox.Text))
539+
{
540+
workDirBox.Text = System.IO.Path.GetDirectoryName(pickedPath) ?? "";
541+
}
542+
}
543+
};
544+
545+
stack.Children.Add(nameBox);
546+
stack.Children.Add(exeBox);
547+
stack.Children.Add(browseBtn);
548+
stack.Children.Add(argsBox);
549+
stack.Children.Add(workDirBox);
550+
dialog.Content = stack;
551+
552+
var result = await dialog.ShowAsync();
553+
if (result == ContentDialogResult.Primary)
554+
{
555+
if (string.IsNullOrWhiteSpace(exeBox.Text))
556+
{
557+
await ShowDialog("验证错误", "可执行文件路径不能为空。");
558+
return;
559+
}
560+
561+
try
562+
{
563+
var config = new ServiceConfig
564+
{
565+
Name = service.Name,
566+
ExePath = exeBox.Text,
567+
Args = argsBox.Text,
568+
WorkingDir = workDirBox.Text
569+
};
570+
571+
if (service.Status == "运行中" || service.Status == "启动中")
572+
{
573+
var confirm = await ShowConfirmDialog("需要重启", "修改服务配置需要重启服务才能生效。是否立即重启?");
574+
await _serviceManager.UpdateServiceAsync(service.Id, config);
575+
if (confirm)
576+
{
577+
await _serviceManager.StopServiceAsync(service.Id);
578+
await _serviceManager.StartServiceAsync(service.Id);
579+
}
580+
}
581+
else
582+
{
583+
await _serviceManager.UpdateServiceAsync(service.Id, config);
584+
}
585+
586+
LoadServices();
587+
UpdateStatus($"服务 {config.Name} 配置已更新。");
588+
}
589+
catch (Exception ex)
590+
{
591+
await ShowDialog("错误", $"更新服务失败: {ex.Message}");
592+
}
593+
}
594+
}
461595
}
462596

463597
private async Task ShowDialog(string title, string content)

Services.App/Services.App.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<PropertyGroup>
44
<OutputType>WinExe</OutputType>
5-
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
6-
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
5+
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
6+
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
77
<WindowsSdkPackageVersion>10.0.19041.38</WindowsSdkPackageVersion>
88
<RootNamespace>Services.App</RootNamespace>
99
<ApplicationManifest>app.manifest</ApplicationManifest>
@@ -29,6 +29,7 @@
2929
</ItemGroup>
3030

3131
<ItemGroup>
32+
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.1.0" />
3233
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.240829007" />
3334
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.756" />
3435
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />

Services.Core/Services.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
4+
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<Platforms>x64</Platforms>

0 commit comments

Comments
 (0)