Skip to content

Commit 32411fb

Browse files
committed
添加托盘图标支持及相关功能
在项目中引入 `H.NotifyIcon.WinUI` 和 `System.Private.Uri` 包,更新 `LocalDrop.json` 文件以支持新的配置选项。重构 `MainWindow` 界面,添加托盘图标及其菜单,确保在窗口最小化时托盘图标可见。修改历史记录的加载和保存路径,支持开机自启功能,并在设置界面中添加相关选项。更新 `Package.appxmanifest` 文件以声明应用能力,确保文件传输完成后托盘图标状态正确更新。同时新增 `RelayCommand` 类以支持 MVVM 模式的命令绑定。
1 parent dd635bb commit 32411fb

10 files changed

Lines changed: 379 additions & 52 deletions

LocalDrop.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@
9696
</ItemGroup>
9797
<ItemGroup>
9898
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.1.240916" />
99+
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
99100
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
100101
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
101102
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
103+
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
102104
</ItemGroup>
103105
<ItemGroup>
104106
<Content Update="Assets\LocalDrop.png">

LocalDrop.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
22
"save_path": "D:\\LocalDrop",
3-
"port": 27431
3+
"port": 27431,
4+
"default_close": true,
5+
"start_open": false,
6+
"start_open_quiet": false
47
}

MainWindow.xaml

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,32 @@
55
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
66
xmlns:local="using:LocalDrop"
77
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
8-
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tb="using:H.NotifyIcon"
99
mc:Ignorable="d"
1010
Title="LocalDrop">
11-
12-
<NavigationView PaneDisplayMode="Left" SelectionChanged="NavigationView_SelectionChanged" IsSettingsVisible="false" IsBackButtonVisible="Collapsed">
13-
<NavigationView.MenuItems>
14-
<NavigationViewItem Content="接收" Icon="Read" Tag="NavItemReceiver"/>
15-
<NavigationViewItem Content="发送" Icon="Send" Tag="NavItemSend"/>
16-
<NavigationViewItem Content="设置" Icon="Setting" Tag="NavItemSetting"/>
17-
</NavigationView.MenuItems>
18-
<Frame x:Name="ContentFrame"/>
19-
</NavigationView>
20-
21-
11+
<Grid>
12+
<Grid.Resources>
13+
<BitmapImage x:Key="TrayIcon" UriSource="Assets/LocalDrop.ico" />
14+
<MenuFlyout x:Name="TrayMenu" >
15+
<MenuFlyoutItem Text="打开" x:Name="OpenMenuItem" Command="{x:Bind ShowWindowCommand}"/>
16+
<MenuFlyoutSeparator/>
17+
<MenuFlyoutItem Text="退出" x:Name="ExitMenuItem" Command="{x:Bind CloseWindowCommand}"/>
18+
</MenuFlyout>
19+
</Grid.Resources>
20+
<NavigationView PaneDisplayMode="Left" SelectionChanged="NavigationView_SelectionChanged" IsSettingsVisible="false" IsBackButtonVisible="Collapsed">
21+
<NavigationView.MenuItems>
22+
<NavigationViewItem Content="接收" Icon="Read" Tag="NavItemReceiver"/>
23+
<NavigationViewItem Content="发送" Icon="Send" Tag="NavItemSend"/>
24+
<NavigationViewItem Content="设置" Icon="Setting" Tag="NavItemSetting"/>
25+
</NavigationView.MenuItems>
26+
<Frame x:Name="ContentFrame"/>
27+
</NavigationView>
28+
<tb:TaskbarIcon x:Name="TrayIcon"
29+
IconSource="{StaticResource TrayIcon}"
30+
ToolTipText="我的应用"
31+
ContextFlyout="{StaticResource TrayMenu}"
32+
Visibility="Collapsed"
33+
DoubleClickCommand="{x:Bind ShowWindowCommand}"/>
34+
</Grid>
2235
</Window>
36+

MainWindow.xaml.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
using H.NotifyIcon;
2+
using H.NotifyIcon.Core;
13
using Microsoft.UI.Xaml;
24
using Microsoft.UI.Xaml.Controls;
5+
using System;
6+
using System.Windows.Input;
37

48

59
namespace LocalDrop
@@ -9,6 +13,8 @@ namespace LocalDrop
913
/// </summary>
1014
public sealed partial class MainWindow : Window
1115
{
16+
17+
private bool is_human_close = false; // 是否人为关闭
1218
private static string GetCurrentDirectory()
1319
{
1420
return System.AppDomain.CurrentDomain.BaseDirectory;
@@ -19,13 +25,65 @@ public static string GetCurrentFile(string filename)
1925
return GetCurrentDirectory() + filename;
2026
}
2127

28+
29+
public ICommand ShowWindowCommand => new RelayCommand(() =>
30+
{
31+
this.Show();
32+
this.Activate();
33+
TrayIcon.Visibility = Visibility.Visible;
34+
});
35+
public ICommand CloseWindowCommand => new RelayCommand(() =>
36+
{
37+
is_human_close = true;
38+
TrayIcon.Dispose(); // 必须释放资源
39+
Application.Current.Exit();
40+
});
41+
2242
public MainWindow()
2343
{
2444
this.InitializeComponent();
2545
ContentFrame.Navigate(typeof(NavItemReceiver));
2646
this.AppWindow.SetIcon(GetCurrentFile("Assets\\LocalDrop.ico"));
2747

28-
//ContentFrame.Navigate(typeof(NavItemSend));
48+
TrayIcon.ContextFlyout = TrayMenu;
49+
this.TrayIcon.Visibility = Visibility.Visible;
50+
// 处理窗口关闭事件
51+
this.Closed += (sender, args) =>
52+
{
53+
bool default_close = Convert.ToBoolean((MySettings.ReadJsonToDictionary()["default_close"].ToString()));
54+
if (default_close || is_human_close)
55+
{
56+
// 直接关闭应用
57+
TrayIcon.Dispose();
58+
Application.Current.Exit();
59+
}
60+
else
61+
{
62+
// 隐藏窗口但保持托盘图标
63+
args.Handled = true;
64+
this.Hide();
65+
TrayIcon.Visibility = Visibility.Visible;
66+
ContentFrame.Navigate(typeof(NavItemReceiver));
67+
//显示通知
68+
TrayIcon.ShowNotification("应用程序仍在运行",
69+
"已最小化到系统托盘",
70+
NotificationIcon.Info);
71+
}
72+
};
73+
74+
// 使用 Activated 事件
75+
this.Activated += OnWindowActivated;
76+
77+
}
78+
public void EnsureTrayIconVisible()
79+
{
80+
this.DispatcherQueue.TryEnqueue(() =>
81+
{
82+
if (!this.Visible && TrayIcon != null)
83+
{
84+
TrayIcon.Visibility = Visibility.Visible;
85+
}
86+
});
2987
}
3088

3189
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
@@ -35,6 +93,13 @@ private void NavigationView_SelectionChanged(NavigationView sender, NavigationVi
3593
else if ((string)selectedItem.Tag == "NavItemSend") ContentFrame.Navigate(typeof(NavItemSend));
3694
else if ((string)selectedItem.Tag == "NavItemSetting") ContentFrame.Navigate(typeof(NavItemSetting));
3795
}
96+
private void OnWindowActivated(object sender, WindowActivatedEventArgs args)
97+
{
98+
if (args.WindowActivationState != WindowActivationState.Deactivated)
99+
{
100+
TrayIcon.Visibility = Visibility.Visible;
101+
}
102+
}
38103

39104

40105
}

NavItemReceiver.xaml.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using System.Threading;
1212
using Windows.Devices.Enumeration;
1313
using Windows.Devices.WiFiDirect;
14-
using Windows.Storage;
1514

1615

1716
namespace LocalDrop
@@ -28,6 +27,7 @@ public sealed partial class NavItemReceiver : Page
2827
private ObservableCollection<FileTransferItem> ActiveTransfers { get; } = new ObservableCollection<FileTransferItem>();
2928
private SemaphoreSlim _dialogLock = new SemaphoreSlim(1);
3029
private bool _isDialogShowing;
30+
3131
public NavItemReceiver()
3232
{
3333
this.InitializeComponent();
@@ -245,8 +245,8 @@ private void LoadHistory()
245245
{
246246
try
247247
{
248-
var localFolder = ApplicationData.Current.LocalFolder;
249-
var historyFile = Path.Combine(localFolder.Path, HistoryFileName);
248+
var localFolder = Environment.CurrentDirectory;
249+
var historyFile = Path.Combine(localFolder, HistoryFileName);
250250

251251
if (File.Exists(historyFile))
252252
{
@@ -277,8 +277,13 @@ private void OnFileReceived(string filePath)
277277
};
278278

279279
ReceivedFiles.Insert(0, historyItem);
280-
SaveHistory();
281280
});
281+
SaveHistory();
282+
// 确保主窗口和托盘图标状态正确
283+
if (Window.Current is MainWindow mainWindow)
284+
{
285+
mainWindow.EnsureTrayIconVisible();
286+
}
282287

283288
}
284289

@@ -288,10 +293,14 @@ private void SaveHistory()
288293
{
289294
DispatcherQueue.TryEnqueue(() =>
290295
{
291-
var localFolder = ApplicationData.Current.LocalFolder;
292-
var historyFile = Path.Combine(localFolder.Path, HistoryFileName);
296+
var localFolder = Environment.CurrentDirectory;
297+
var historyFile = Path.Combine(localFolder, HistoryFileName);
293298
var json = JsonConvert.SerializeObject(ReceivedFiles);
294299
File.WriteAllText(historyFile, json);
300+
if (Window.Current is MainWindow mainWindow)
301+
{
302+
mainWindow.EnsureTrayIconVisible();
303+
}
295304
});
296305
}
297306
catch (Exception ex)

NavItemSetting.xaml

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,115 @@
77
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
88
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
99
mc:Ignorable="d"
10-
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
11-
>
10+
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
1211

1312
<Grid>
14-
15-
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
16-
<StackPanel Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
17-
<TextBlock Text="默认保存路径:" VerticalAlignment="Center" FontSize="16"/>
18-
<TextBox x:Name="SavePathTextBox" Width="300" Height="30"
19-
Text=""/>
20-
<Button Content="浏览..." Width="80" Height="30" Click="BrowseFolderButton_Click"/>
13+
<StackPanel VerticalAlignment="Center"
14+
HorizontalAlignment="Center">
15+
<StackPanel Orientation="Horizontal"
16+
Spacing="10"
17+
VerticalAlignment="Center">
18+
<TextBlock Text="默认保存路径:"
19+
VerticalAlignment="Center"
20+
FontSize="16"/>
21+
<TextBox x:Name="SavePathTextBox"
22+
Width="300"
23+
Height="30"
24+
Text=""/>
25+
<Button Content="浏览..."
26+
Width="80"
27+
Height="30"
28+
Click="BrowseFolderButton_Click"/>
29+
</StackPanel>
30+
<StackPanel Orientation="Horizontal"
31+
Spacing="10"
32+
VerticalAlignment="Center">
33+
<TextBlock Text="端口:"
34+
VerticalAlignment="Center"
35+
FontSize="16"/>
36+
<TextBox x:Name="SocketTextBox"
37+
Width="300"
38+
Height="30"
39+
Text=""/>
40+
<Button Content="保存"
41+
Width="80"
42+
Height="30"
43+
Click="SocketButton_Click"/>
2144
</StackPanel>
22-
<StackPanel Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
23-
<TextBlock Text="端口:" VerticalAlignment="Center" FontSize="16"/>
24-
<TextBox x:Name="SocketTextBox" Width="300" Height="30"
25-
Text=""/>
26-
<Button Content="保存" Width="80" Height="30" Click="SocketButton_Click"/>
45+
<StackPanel Orientation="Horizontal"
46+
Spacing="10"
47+
VerticalAlignment="Center"
48+
Margin="0,15,0,0">
49+
<TextBlock Text="关闭时最小化到托盘:"
50+
VerticalAlignment="Center"
51+
FontSize="16"/>
52+
<CheckBox x:Name="MinimizeToTrayToggle"
53+
Checked="MinimizeToTrayToggle_Checked"
54+
Unchecked="MinimizeToTrayToggle_Unchecked"
55+
Margin="10,0,0,0"/>
2756
</StackPanel>
28-
<StackPanel Orientation="Horizontal" Spacing="15">
57+
<StackPanel Orientation="Horizontal"
58+
Spacing="10"
59+
VerticalAlignment="Center"
60+
Margin="0,15,0,0">
61+
<TextBlock Text="开机自启(需要以管理员模式启动):"
62+
VerticalAlignment="Center"
63+
FontSize="16"/>
64+
<CheckBox x:Name="cb_start_open"
65+
Checked="cb_start_open_Checked"
66+
Unchecked="cb_start_open_Unchecked"
67+
Margin="10,0,0,0"/>
68+
</StackPanel>
69+
<!--<StackPanel Orientation="Horizontal"
70+
Spacing="10"
71+
VerticalAlignment="Center"
72+
Margin="0,15,0,0">
73+
<TextBlock Text="开机静默自启:"
74+
VerticalAlignment="Center"
75+
FontSize="16"/>
76+
<CheckBox x:Name="cb_start_open_quiet"
77+
Checked="cb_start_open_quiet_Checked"
78+
Unchecked="cb_start_open_quiet_Unchecked"
79+
Margin="10,0,0,0"/>
80+
</StackPanel>-->
81+
82+
<StackPanel Orientation="Horizontal"
83+
Spacing="15">
2984
<!-- GitHub 按钮 -->
30-
<Button Content="GitHub 主页"
31-
Width="200" Height="40"
32-
Background="#24292E" Foreground="White"
33-
FontSize="16" FontWeight="SemiBold"
34-
Click="GitHubButton_Click"
35-
>
85+
<Button Content="GitHub 主页"
86+
Width="200"
87+
Height="40"
88+
Background="#24292E"
89+
Foreground="White"
90+
FontSize="16"
91+
FontWeight="SemiBold"
92+
Click="GitHubButton_Click">
3693
<Button.Template>
3794
<ControlTemplate TargetType="Button">
3895
<Border Background="{TemplateBinding Background}"
39-
CornerRadius="5">
96+
CornerRadius="5">
4097
<ContentPresenter HorizontalAlignment="Center"
41-
VerticalAlignment="Center"/>
98+
VerticalAlignment="Center"/>
4299
</Border>
43100
</ControlTemplate>
44101
</Button.Template>
45102
</Button>
46103

47104
<!-- 哔哩哔哩 按钮 -->
48-
<Button Content="哔哩哔哩 主页"
49-
Width="200" Height="40"
50-
Background="#00A1D6" Foreground="White"
51-
FontSize="16" FontWeight="SemiBold"
52-
Click="BilibiliButton_Click"
53-
>
105+
<Button Content="哔哩哔哩 主页"
106+
Width="200"
107+
Height="40"
108+
Background="#00A1D6"
109+
Foreground="White"
110+
FontSize="16"
111+
FontWeight="SemiBold"
112+
Click="BilibiliButton_Click">
54113
<Button.Template>
55114
<ControlTemplate TargetType="Button">
56115
<Border Background="{TemplateBinding Background}"
57-
CornerRadius="5">
116+
CornerRadius="5">
58117
<ContentPresenter HorizontalAlignment="Center"
59-
VerticalAlignment="Center"/>
118+
VerticalAlignment="Center"/>
60119
</Border>
61120
</ControlTemplate>
62121
</Button.Template>

0 commit comments

Comments
 (0)