A:增加网络占用TOP展示

This commit is contained in:
jimmy committed 2025-08-08 09:11:16 +08:00
1 parent 1f06ca6cac
commit 1c56007afd
11 files changed
+891 -114

No files matched your search

@@ -87,6 +87,15 @@ public class AppSetting
/// </summary> /// </summary>
public bool AutoStart { get; set; } = false; public bool AutoStart { get; set; } = false;
/// <summary>
/// 是否显示网速Top榜 (只在横向布局时可用)
/// </summary>
public bool ShowNetworkTopList { get; set; } = false;
/// <summary>
/// 网速Top榜显示数量 (1-5)
/// </summary>
public int NetworkTopListCount { get; set; } = 3;
/// <summary> /// <summary>
/// 更新时间 /// 更新时间
@@ -82,6 +82,8 @@ public class NetVizorDbContext : IDisposable
DoubleClickAction INTEGER DEFAULT 0, DoubleClickAction INTEGER DEFAULT 0,
RunAsAdmin INTEGER DEFAULT 0, RunAsAdmin INTEGER DEFAULT 0,
AutoStart INTEGER DEFAULT 0, AutoStart INTEGER DEFAULT 0,
ShowNetworkTopList INTEGER DEFAULT 0,
NetworkTopListCount INTEGER DEFAULT 3,
UpdateTime INTEGER NOT NULL UpdateTime INTEGER NOT NULL
)"); )");
@@ -282,7 +284,8 @@ public class NetVizorDbContext : IDisposable
var needsMigration = columns.Contains("WindowWidth") || columns.Contains("WindowHeight") || var needsMigration = columns.Contains("WindowWidth") || columns.Contains("WindowHeight") ||
columns.Contains("Theme") || columns.Contains("Language") || columns.Contains("Theme") || columns.Contains("Language") ||
columns.Contains("MonitoringEnabled") || columns.Contains("MonitoringEnabled") ||
!columns.Contains("IsClickThrough") || !columns.Contains("TextColor"); !columns.Contains("IsClickThrough") || !columns.Contains("TextColor") ||
!columns.Contains("ShowNetworkTopList") || !columns.Contains("NetworkTopListCount");
if (needsMigration) if (needsMigration)
{ {
@@ -315,6 +318,8 @@ public class NetVizorDbContext : IDisposable
DoubleClickAction INTEGER DEFAULT 0, DoubleClickAction INTEGER DEFAULT 0,
RunAsAdmin INTEGER DEFAULT 0, RunAsAdmin INTEGER DEFAULT 0,
AutoStart INTEGER DEFAULT 0, AutoStart INTEGER DEFAULT 0,
ShowNetworkTopList INTEGER DEFAULT 0,
NetworkTopListCount INTEGER DEFAULT 3,
UpdateTime INTEGER NOT NULL UpdateTime INTEGER NOT NULL
)"); )");
@@ -73,6 +73,8 @@ public class DataService : IDataService, IDisposable
DoubleClickAction = 0, // None DoubleClickAction = 0, // None
RunAsAdmin = false, RunAsAdmin = false,
AutoStart = false, AutoStart = false,
ShowNetworkTopList = false, // 默认关闭网速Top榜
NetworkTopListCount = 3, // 默认显示3个
UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
}; };
@@ -9,8 +9,9 @@ namespace Shell.Models;
public class NetViewSettings : INotifyPropertyChanged public class NetViewSettings : INotifyPropertyChanged
{ {
private static NetViewSettings? _instance; private static NetViewSettings? _instance;
private static readonly string SettingsFilePath = Path.Combine( private static readonly string SettingsFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"NetVizor", "settings.json"); "NetVizor", "settings.json");
public static NetViewSettings Instance public static NetViewSettings Instance
@@ -21,60 +22,187 @@ public class NetViewSettings : INotifyPropertyChanged
{ {
_instance = LoadSettings(); _instance = LoadSettings();
} }
return _instance; return _instance;
} }
} }
// 外观设置 // 外观设置
private string _textColor = "#333333"; private string _textColor = "#333333";
public string TextColor public string TextColor
{ {
get => _textColor; get => _textColor;
set { _textColor = value; OnPropertyChanged(); } set
{
_textColor = value;
OnPropertyChanged();
}
} }
private string _backgroundColor = "#F0FFFFFF"; private string _backgroundColor = "#F0FFFFFF";
public string BackgroundColor public string BackgroundColor
{ {
get => _backgroundColor; get => _backgroundColor;
set { _backgroundColor = value; OnPropertyChanged(); } set
{
_backgroundColor = value;
OnPropertyChanged();
}
} }
private double _backgroundOpacity = 94.0; private double _backgroundOpacity = 94.0;
public double BackgroundOpacity public double BackgroundOpacity
{ {
get => _backgroundOpacity; get => _backgroundOpacity;
set { _backgroundOpacity = value; OnPropertyChanged(); } set
{
_backgroundOpacity = value;
OnPropertyChanged();
}
} }
// 显示设置 // 显示设置
private SpeedUnit _speedUnit = SpeedUnit.Auto; private SpeedUnit _speedUnit = SpeedUnit.Auto;
public SpeedUnit SpeedUnit public SpeedUnit SpeedUnit
{ {
get => _speedUnit; get => _speedUnit;
set { _speedUnit = value; OnPropertyChanged(); } set
{
_speedUnit = value;
OnPropertyChanged();
}
} }
private bool _showUnit = true; private bool _showUnit = true;
public bool ShowUnit public bool ShowUnit
{ {
get => _showUnit; get => _showUnit;
set { _showUnit = value; OnPropertyChanged(); } set
{
_showUnit = value;
OnPropertyChanged();
}
} }
private LayoutDirection _layoutDirection = LayoutDirection.Vertical; private LayoutDirection _layoutDirection = LayoutDirection.Vertical;
public LayoutDirection LayoutDirection public LayoutDirection LayoutDirection
{ {
get => _layoutDirection; get => _layoutDirection;
set { _layoutDirection = value; OnPropertyChanged(); } set
{
_layoutDirection = value;
OnPropertyChanged();
}
} }
// 网速Top榜设置
private bool _showNetworkTopList = false;
public bool ShowNetworkTopList
{
get => _showNetworkTopList;
set
{
_showNetworkTopList = value;
OnPropertyChanged();
}
}
private int _networkTopListCount = 3;
public int NetworkTopListCount
{
get => _networkTopListCount;
set
{
_networkTopListCount = Math.Max(1, Math.Min(5, value));
OnPropertyChanged();
}
}
/// <summary>
/// Top榜是否可用(只有横向布局时才可用)
/// </summary>
public bool IsTopListAvailable => LayoutDirection == LayoutDirection.Horizontal;
// 交互设置 // 交互设置
private DoubleClickAction _doubleClickAction = DoubleClickAction.None; private DoubleClickAction _doubleClickAction = DoubleClickAction.None;
public DoubleClickAction DoubleClickAction public DoubleClickAction DoubleClickAction
{ {
get => _doubleClickAction; get => _doubleClickAction;
set { _doubleClickAction = value; OnPropertyChanged(); } set
{
_doubleClickAction = value;
OnPropertyChanged();
}
}
// 窗口行为设置
private bool _lockPosition = false;
public bool LockPosition
{
get => _lockPosition;
set
{
_lockPosition = value;
OnPropertyChanged();
}
}
private bool _clickThrough = false;
public bool ClickThrough
{
get => _clickThrough;
set
{
_clickThrough = value;
OnPropertyChanged();
}
}
private bool _topmost = true;
public bool Topmost
{
get => _topmost;
set
{
_topmost = value;
OnPropertyChanged();
}
}
private bool _snapToScreen = false;
public bool SnapToScreen
{
get => _snapToScreen;
set
{
_snapToScreen = value;
OnPropertyChanged();
}
}
private bool _showDetailedInfo = false;
public bool ShowDetailedInfo
{
get => _showDetailedInfo;
set
{
_showDetailedInfo = value;
OnPropertyChanged();
}
} }
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
@@ -102,7 +230,7 @@ public class NetViewSettings : INotifyPropertyChanged
var json = JsonSerializer.Serialize(this, options); var json = JsonSerializer.Serialize(this, options);
File.WriteAllText(SettingsFilePath, json); File.WriteAllText(SettingsFilePath, json);
Log.Info("设置已保存"); Log.Info("设置已保存");
} }
catch (Exception ex) catch (Exception ex)
@@ -122,7 +250,7 @@ public class NetViewSettings : INotifyPropertyChanged
{ {
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() } Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() }
}; };
var settings = JsonSerializer.Deserialize<NetViewSettings>(json, options); var settings = JsonSerializer.Deserialize<NetViewSettings>(json, options);
if (settings != null) if (settings != null)
{ {
@@ -148,8 +276,15 @@ public class NetViewSettings : INotifyPropertyChanged
SpeedUnit = SpeedUnit.Auto; SpeedUnit = SpeedUnit.Auto;
ShowUnit = true; ShowUnit = true;
LayoutDirection = LayoutDirection.Vertical; LayoutDirection = LayoutDirection.Vertical;
ShowNetworkTopList = false;
NetworkTopListCount = 3;
DoubleClickAction = DoubleClickAction.None; DoubleClickAction = DoubleClickAction.None;
LockPosition = false;
ClickThrough = false;
Topmost = true;
SnapToScreen = false;
ShowDetailedInfo = false;
Log.Info("设置已恢复默认值"); Log.Info("设置已恢复默认值");
} }
} }
+1
View File
@@ -12,6 +12,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Assets\Icons\"/> <Folder Include="Assets\Icons\"/>
<Folder Include="Services\"/>
<Folder Include="wwwroot\" /> <Folder Include="wwwroot\" />
</ItemGroup> </ItemGroup>
@@ -13,7 +13,7 @@
<d:UserControl.DataContext> <d:UserControl.DataContext>
<viewModel:NetworkMonitorViewModel /> <viewModel:NetworkMonitorViewModel />
</d:UserControl.DataContext> </d:UserControl.DataContext>
<Border <Border
x:Name="MainBorder" x:Name="MainBorder"
CornerRadius="8" CornerRadius="8"
@@ -29,57 +29,136 @@
Opacity="0.3" Opacity="0.3"
BlurRadius="8" /> BlurRadius="8" />
</Border.Effect> </Border.Effect>
<Grid Margin="10,8">
<StackPanel x:Name="SpeedPanel" Orientation="Vertical">
<!-- Upload Speed -->
<Grid x:Name="UploadGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Text="↑"
Foreground="#FF6B6B"
FontSize="12"
FontWeight="Bold"
VerticalAlignment="Center"
Margin="0,0,6,0" />
<TextBlock
x:Name="UploadSpeedText"
Grid.Column="1"
Text="{Binding UploadSpeed}"
FontSize="12"
FontWeight="SemiBold"
Foreground="#333333"
VerticalAlignment="Center" />
</Grid>
<!-- Download Speed --> <Grid Margin="10,8">
<Grid x:Name="DownloadGrid" Margin="0,5,0,0"> <StackPanel x:Name="MainPanel" Orientation="Vertical">
<Grid.ColumnDefinitions> <!-- Global Speed Panel -->
<ColumnDefinition Width="Auto" /> <StackPanel x:Name="SpeedPanel" Orientation="Vertical">
<ColumnDefinition Width="*" /> <!-- Upload Speed -->
</Grid.ColumnDefinitions> <Grid x:Name="UploadGrid">
<TextBlock <Grid.ColumnDefinitions>
Grid.Column="0" <ColumnDefinition Width="Auto" />
Text="↓" <ColumnDefinition Width="*" />
Foreground="#4ECDC4" </Grid.ColumnDefinitions>
FontSize="12" <TextBlock
FontWeight="Bold" Grid.Column="0"
VerticalAlignment="Center" Text="↑"
Margin="0,0,6,0" /> Foreground="#FF6B6B"
<TextBlock FontSize="12"
x:Name="DownloadSpeedText" FontWeight="Bold"
Grid.Column="1" VerticalAlignment="Center"
Text="{Binding DownloadSpeed}" Margin="0,0,6,0" />
FontSize="12" <TextBlock
FontWeight="SemiBold" x:Name="UploadSpeedText"
Foreground="#333333" Grid.Column="1"
VerticalAlignment="Center" /> Text="{Binding UploadSpeed}"
</Grid> FontSize="12"
FontWeight="SemiBold"
Foreground="#333333"
VerticalAlignment="Center" />
</Grid>
<!-- Download Speed -->
<Grid x:Name="DownloadGrid" Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Text="↓"
Foreground="#4ECDC4"
FontSize="12"
FontWeight="Bold"
VerticalAlignment="Center"
Margin="0,0,6,0" />
<TextBlock
x:Name="DownloadSpeedText"
Grid.Column="1"
Text="{Binding DownloadSpeed}"
FontSize="12"
FontWeight="SemiBold"
Foreground="#333333"
VerticalAlignment="Center" />
</Grid>
</StackPanel>
<!-- Network Top List Panel -->
<StackPanel x:Name="TopListPanel" Orientation="Vertical" Margin="0,8,0,0"
Visibility="{Binding TopListVisibility}">
<!-- Separator Line -->
<Border Height="1" Background="#E0E0E0" Margin="0,0,0,6" />
<!-- Top List Items -->
<ItemsControl x:Name="TopListItems" ItemsSource="{Binding TopProcesses}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" MinWidth="40" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- App Icon -->
<Image Grid.Column="0" Width="12" Height="12"
Source="{Binding IconSource}"
VerticalAlignment="Center" />
<!-- App Name -->
<TextBlock Grid.Column="1"
Text="{Binding ProcessName}"
FontSize="10"
FontWeight="Normal"
Foreground="#666666"
VerticalAlignment="Center"
MaxWidth="60"
TextTrimming="CharacterEllipsis"
Margin="4,0,8,0" />
<!-- Upload Speed -->
<Grid Grid.Column="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="↑"
Foreground="#FF6B6B" FontSize="9"
VerticalAlignment="Center" Margin="0,0,2,0" />
<TextBlock Grid.Column="1"
Text="{Binding FormattedUploadSpeed}"
FontSize="9" FontWeight="Normal"
Foreground="#666666"
VerticalAlignment="Center"
MinWidth="35"
TextAlignment="Right" />
</Grid>
<!-- Download Speed -->
<Grid Grid.Column="4" Margin="8,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="↓"
Foreground="#4ECDC4" FontSize="9"
VerticalAlignment="Center" Margin="0,0,2,0" />
<TextBlock Grid.Column="1"
Text="{Binding FormattedDownloadSpeed}"
FontSize="9" FontWeight="Normal"
Foreground="#666666"
VerticalAlignment="Center"
MinWidth="35"
TextAlignment="Right" />
</Grid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Border> </Border>
</UserControl> </UserControl>
@@ -7,6 +7,7 @@ using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using Shell.Views; using Shell.Views;
using Common.Logger;
namespace Shell.Utils; namespace Shell.Utils;
@@ -18,6 +19,8 @@ public class TrayIconManager : IDisposable
private DispatcherTimer _clickTimer; private DispatcherTimer _clickTimer;
private bool _isDoubleClick = false; private bool _isDoubleClick = false;
private DispatcherTimer _menuCloseTimer; private DispatcherTimer _menuCloseTimer;
private bool _lastLeftButtonState = false;
private bool _lastRightButtonState = false;
// Win32 API 用于获取鼠标位置和显示器信息 // Win32 API 用于获取鼠标位置和显示器信息
[DllImport("user32.dll")] [DllImport("user32.dll")]
@@ -29,6 +32,13 @@ public class TrayIconManager : IDisposable
[DllImport("user32.dll")] [DllImport("user32.dll")]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi); private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
// 添加获取鼠标按键状态的API
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
private const int VK_LBUTTON = 0x01;
private const int VK_RBUTTON = 0x02;
// 添加DPI相关API // 添加DPI相关API
[DllImport("user32.dll")] [DllImport("user32.dll")]
private static extern uint GetDpiForWindow(IntPtr hwnd); private static extern uint GetDpiForWindow(IntPtr hwnd);
@@ -108,16 +118,21 @@ public class TrayIconManager : IDisposable
_contextMenu = new ContextMenu(); _contextMenu = new ContextMenu();
// 添加失去焦点时关闭事件 // 添加失去焦点时关闭事件
_contextMenu.Closed += (s, e) => _contextMenu.Closed += (s, e) => { };
{
/* 菜单关闭 */
};
_contextMenu.LostFocus += (s, e) => _contextMenu.LostFocus += (s, e) =>
{ {
if (_contextMenu != null) // 暂时禁用自动关闭来调试问题
_contextMenu.IsOpen = false; // if (_contextMenu != null)
// _contextMenu.IsOpen = false;
}; };
// 添加更多事件监听来调试
_contextMenu.Opened += (s, e) => { };
_contextMenu.MouseEnter += (s, e) => { };
_contextMenu.MouseLeave += (s, e) => { };
// 展示主窗口(可勾选) // 展示主窗口(可勾选)
var showMainWindowItem = new MenuItem var showMainWindowItem = new MenuItem
{ {
@@ -282,8 +297,13 @@ public class TrayIconManager : IDisposable
_contextMenu.Placement = PlacementMode.MousePoint; _contextMenu.Placement = PlacementMode.MousePoint;
_contextMenu.StaysOpen = true; // 保持打开,我们手动控制关闭 _contextMenu.StaysOpen = true; // 保持打开,我们手动控制关闭
// 禁用默认的关闭行为
_contextMenu.IsOpen = true; _contextMenu.IsOpen = true;
// 强制设置,确保菜单不会因为失去焦点而关闭
_contextMenu.UpdateLayout();
// 启动定时器来检测点击外部 // 启动定时器来检测点击外部
StartMenuCloseTimer(); StartMenuCloseTimer();
} }
@@ -297,9 +317,10 @@ public class TrayIconManager : IDisposable
_menuCloseTimer = new DispatcherTimer _menuCloseTimer = new DispatcherTimer
{ {
Interval = TimeSpan.FromMilliseconds(100) // 每100ms检查一次 Interval = TimeSpan.FromMilliseconds(50) // 改为每50ms检查一次,提高响应速度
}; };
_menuCloseTimer.Tick += (s, e) => _menuCloseTimer.Tick += (s, e) =>
{ {
if (!_contextMenu.IsOpen) if (!_contextMenu.IsOpen)
@@ -317,12 +338,23 @@ public class TrayIconManager : IDisposable
return; return;
} }
// 检查是否有鼠标按键按下 // 使用Win32 API检测鼠标按键状态变化
if (System.Windows.Input.Mouse.LeftButton == System.Windows.Input.MouseButtonState.Pressed || bool currentLeftState = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
System.Windows.Input.Mouse.RightButton == System.Windows.Input.MouseButtonState.Pressed) bool currentRightState = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
// 检测按键从未按下到按下的状态变化(按下事件)
bool leftButtonPressed = currentLeftState && !_lastLeftButtonState;
bool rightButtonPressed = currentRightState && !_lastRightButtonState;
_lastLeftButtonState = currentLeftState;
_lastRightButtonState = currentRightState;
if (leftButtonPressed || rightButtonPressed)
{ {
// 如果鼠标不在菜单区域内且有按键按下,则关闭菜单 // 如果鼠标不在菜单区域内且有按键按下,则关闭菜单
if (!IsMouseOverContextMenu()) bool isMouseOver = IsMouseOverContextMenu();
if (!isMouseOver)
{ {
if (_contextMenu != null) if (_contextMenu != null)
_contextMenu.IsOpen = false; _contextMenu.IsOpen = false;
@@ -332,13 +364,20 @@ public class TrayIconManager : IDisposable
}; };
_menuCloseTimer.Start(); _menuCloseTimer.Start();
// 重置按键状态,避免误检测
_lastLeftButtonState = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
_lastRightButtonState = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
} }
private bool IsMouseOverContextMenu() private bool IsMouseOverContextMenu()
{ {
try try
{ {
if (_contextMenu == null || !_contextMenu.IsOpen) return false; if (_contextMenu == null || !_contextMenu.IsOpen)
{
return false;
}
// 获取屏幕上的鼠标位置 // 获取屏幕上的鼠标位置
GetCursorPos(out POINT screenPoint); GetCursorPos(out POINT screenPoint);
@@ -346,10 +385,17 @@ public class TrayIconManager : IDisposable
// 获取菜单的屏幕位置和大小 // 获取菜单的屏幕位置和大小
var contextMenuElement = _contextMenu as FrameworkElement; var contextMenuElement = _contextMenu as FrameworkElement;
if (contextMenuElement == null) return false; if (contextMenuElement == null)
{
return true; // 改为返回true,避免意外关闭
}
var source = PresentationSource.FromVisual(contextMenuElement); var source = PresentationSource.FromVisual(contextMenuElement);
if (source == null) return false; if (source == null)
{
return true; // 改为返回true,避免意外关闭
}
// 获取菜单的屏幕边界 // 获取菜单的屏幕边界
var menuTopLeft = contextMenuElement.PointToScreen(new System.Windows.Point(0, 0)); var menuTopLeft = contextMenuElement.PointToScreen(new System.Windows.Point(0, 0));
@@ -357,14 +403,25 @@ public class TrayIconManager : IDisposable
contextMenuElement.PointToScreen(new System.Windows.Point(contextMenuElement.ActualWidth, contextMenuElement.PointToScreen(new System.Windows.Point(contextMenuElement.ActualWidth,
contextMenuElement.ActualHeight)); contextMenuElement.ActualHeight));
var menuBounds = new Rect(menuTopLeft, menuBottomRight);
return menuBounds.Contains(screenPosition); // 添加一些边距,让鼠标检测区域稍微大一点,避免意外关闭
var margin = 5;
var menuBounds = new Rect(
menuTopLeft.X - margin,
menuTopLeft.Y - margin,
menuBottomRight.X - menuTopLeft.X + (margin * 2),
menuBottomRight.Y - menuTopLeft.Y + (margin * 2)
);
bool contains = menuBounds.Contains(screenPosition);
return contains;
} }
catch catch (Exception ex)
{ {
// 如果检测失败,假设鼠标不在菜单上,这样会更安全 // 如果检测失败,假设鼠标在菜单上,避免意外关闭
return false; return true;
} }
} }
@@ -5,6 +5,10 @@ using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using Common.Logger; using Common.Logger;
using Common.Utils; using Common.Utils;
using Utils.ETW.Services;
using Shell.Models;
using Data;
using Utils.ETW.Models;
namespace Shell.ViewModel; namespace Shell.ViewModel;
@@ -90,6 +94,31 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
} }
} }
// Top榜相关属性
private ObservableCollection<ProcessNetworkStatsViewModel> _topProcesses = new();
public ObservableCollection<ProcessNetworkStatsViewModel> TopProcesses
{
get => _topProcesses;
set
{
_topProcesses = value;
OnPropertyChanged();
}
}
private System.Windows.Visibility _topListVisibility = System.Windows.Visibility.Collapsed;
public System.Windows.Visibility TopListVisibility
{
get => _topListVisibility;
set
{
_topListVisibility = value;
OnPropertyChanged();
}
}
#endregion #endregion
#region Commands #region Commands
@@ -126,6 +155,7 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
// 初始化数据 // 初始化数据
LoadNetworkInterfaces(); LoadNetworkInterfaces();
LoadTopListSettings();
_updateTimer.Start(); _updateTimer.Start();
} }
} }
@@ -369,6 +399,9 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
DownloadSpeed = speed.DownloadSpeedText; DownloadSpeed = speed.DownloadSpeedText;
UploadSpeed = speed.UploadSpeedText; UploadSpeed = speed.UploadSpeedText;
TotalSpeed = speed.TotalSpeedText; TotalSpeed = speed.TotalSpeedText;
// 更新Top榜
UpdateTopList();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -403,6 +436,145 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
StatusText = "接口列表已刷新"; StatusText = "接口列表已刷新";
} }
/// <summary>
/// 加载Top榜设置
/// </summary>
private async void LoadTopListSettings()
{
try
{
// 从数据库加载设置
var settings = await DatabaseManager.GetUserSettingsAsync();
var netViewSettings = NetViewSettings.Instance;
// 同步设置
netViewSettings.ShowNetworkTopList = settings.ShowNetworkTopList;
netViewSettings.NetworkTopListCount = settings.NetworkTopListCount;
// 更新可见性
UpdateTopListVisibility();
}
catch (Exception ex)
{
Log.Error4Ctx($"加载Top榜设置失败: {ex.Message}");
}
}
/// <summary>
/// 更新Top榜可见性
/// </summary>
private void UpdateTopListVisibility()
{
var settings = NetViewSettings.Instance;
var shouldShow = settings.ShowNetworkTopList && settings.IsTopListAvailable;
TopListVisibility = shouldShow ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
/// <summary>
/// 更新Top榜数据
/// </summary>
private void UpdateTopList()
{
if (_isDesignMode)
{
UpdateDesignTimeTopList();
return;
}
try
{
var settings = NetViewSettings.Instance;
if (!settings.ShowNetworkTopList || !settings.IsTopListAvailable)
{
TopListVisibility = System.Windows.Visibility.Collapsed;
return;
}
// 更新NetworkTopListManager的统计数据
NetworkTopListManager.Instance.UpdateAllProcessStats();
// 获取Top榜数据
var topList = NetworkTopListManager.Instance.GetTopList(settings.NetworkTopListCount);
if (topList.HasData)
{
// 更新现有项目或添加新项目
var newViewModels = topList.TopProcesses.Select(stats =>
{
var existing = TopProcesses.FirstOrDefault(vm => vm.ProcessId == stats.ProcessId);
if (existing != null)
{
existing.UpdateStats(stats);
return existing;
}
return new ProcessNetworkStatsViewModel(stats);
}).ToList();
// 更新集合
TopProcesses.Clear();
foreach (var vm in newViewModels)
{
TopProcesses.Add(vm);
}
TopListVisibility = System.Windows.Visibility.Visible;
}
else
{
TopProcesses.Clear();
TopListVisibility = System.Windows.Visibility.Collapsed;
}
}
catch (Exception ex)
{
Log.Error4Ctx($"更新Top榜时发生错误: {ex.Message}");
}
}
/// <summary>
/// 设计时Top榜数据
/// </summary>
private void UpdateDesignTimeTopList()
{
if (TopProcesses.Count == 0)
{
// 添加一些假数据用于设计时预览
var fakeStats = new[]
{
new ProcessNetworkStats
{
ProcessId = 1234,
ProcessName = "chrome",
CurrentUploadSpeed = 1024 * 500, // 500 KB/s
CurrentDownloadSpeed = 1024 * 1024 * 15.5, // 15.5 MB/s
},
new ProcessNetworkStats
{
ProcessId = 5678,
ProcessName = "firefox",
CurrentUploadSpeed = 1024 * 200, // 200 KB/s
CurrentDownloadSpeed = 1024 * 1024 * 8.2, // 8.2 MB/s
},
new ProcessNetworkStats
{
ProcessId = 9012,
ProcessName = "steam",
CurrentUploadSpeed = 1024 * 50, // 50 KB/s
CurrentDownloadSpeed = 1024 * 1024 * 5.1, // 5.1 MB/s
}
};
TopProcesses.Clear();
foreach (var stats in fakeStats)
{
TopProcesses.Add(new ProcessNetworkStatsViewModel(stats));
}
TopListVisibility = System.Windows.Visibility.Visible;
}
}
// 测试用的异步方法 // 测试用的异步方法
private static async Task TestNetworkMethods() private static async Task TestNetworkMethods()
{ {
@@ -480,4 +652,4 @@ public class RelayCommand : ICommand
public void Execute(object parameter) => _execute(); public void Execute(object parameter) => _execute();
} }
#endregion #endregion
@@ -0,0 +1,126 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media.Imaging;
using Utils.ETW.Models;
namespace Shell.ViewModel;
/// <summary>
/// 进程网络统计的ViewModel
/// 用于在UI中显示Top榜信息
/// </summary>
public class ProcessNetworkStatsViewModel : INotifyPropertyChanged
{
private ProcessNetworkStats _stats;
public ProcessNetworkStatsViewModel(ProcessNetworkStats stats)
{
_stats = stats;
LoadIcon();
}
/// <summary>
/// 进程ID
/// </summary>
public int ProcessId => _stats.ProcessId;
/// <summary>
/// 进程名称
/// </summary>
public string ProcessName => _stats.ProcessName;
/// <summary>
/// 格式化的上传速度
/// </summary>
public string FormattedUploadSpeed => _stats.FormattedUploadSpeed;
/// <summary>
/// 格式化的下载速度
/// </summary>
public string FormattedDownloadSpeed => _stats.FormattedDownloadSpeed;
/// <summary>
/// 格式化的总速度
/// </summary>
public string FormattedTotalSpeed => _stats.FormattedTotalSpeed;
/// <summary>
/// 总速度(用于排序)
/// </summary>
public double TotalSpeed => _stats.TotalSpeed;
private BitmapImage? _iconSource;
/// <summary>
/// 图标源
/// </summary>
public BitmapImage? IconSource
{
get => _iconSource;
private set
{
_iconSource = value;
OnPropertyChanged();
}
}
/// <summary>
/// 更新统计数据
/// </summary>
public void UpdateStats(ProcessNetworkStats newStats)
{
_stats = newStats;
OnPropertyChanged(nameof(ProcessName));
OnPropertyChanged(nameof(FormattedUploadSpeed));
OnPropertyChanged(nameof(FormattedDownloadSpeed));
OnPropertyChanged(nameof(FormattedTotalSpeed));
OnPropertyChanged(nameof(TotalSpeed));
// 如果图标还未加载,尝试重新加载
if (_iconSource == null)
{
LoadIcon();
}
}
/// <summary>
/// 加载进程图标
/// </summary>
private void LoadIcon()
{
try
{
if (_stats.ApplicationInfo?.ProgramInfo?.IconBase64 != null)
{
var iconBytes = Convert.FromBase64String(_stats.ApplicationInfo.ProgramInfo.IconBase64);
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = new System.IO.MemoryStream(iconBytes);
bitmap.DecodePixelWidth = 12; // 优化性能,直接解码为所需大小
bitmap.DecodePixelHeight = 12;
bitmap.CacheOption = BitmapCacheOption.OnLoad; // 缓存以提高性能
bitmap.EndInit();
bitmap.Freeze(); // 冻结以允许跨线程访问
IconSource = bitmap;
}
else
{
// 使用默认图标或留空
IconSource = null;
}
}
catch (Exception)
{
// 如果图标加载失败,使用null(UI会显示默认外观)
IconSource = null;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
@@ -6,11 +6,11 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" mc:Ignorable="d"
Title="NetVizor - 设置" Title="NetVizor - 设置"
Height="480" Height="620"
Width="470" Width="470"
MinHeight="480" MinHeight="620"
MinWidth="470" MinWidth="470"
MaxHeight="480" MaxHeight="620"
MaxWidth="470" MaxWidth="470"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"> ResizeMode="NoResize">
@@ -108,14 +108,30 @@
<!-- 显示单位 --> <!-- 显示单位 -->
<TextBlock Grid.Row="4" Grid.Column="0" Text="显示选项" VerticalAlignment="Center" /> <TextBlock Grid.Row="4" Grid.Column="0" Text="显示选项" VerticalAlignment="Center" />
<CheckBox Grid.Row="4" Grid.Column="1" x:Name="ShowUnitCheckBox" Content="显示单位" <StackPanel Grid.Row="4" Grid.Column="1">
IsChecked="True" Checked="ShowUnitCheckBox_Changed" <CheckBox x:Name="ShowUnitCheckBox" Content="显示单位"
Unchecked="ShowUnitCheckBox_Changed" /> IsChecked="True" Checked="ShowUnitCheckBox_Changed"
Unchecked="ShowUnitCheckBox_Changed" />
<CheckBox x:Name="ShowTopListCheckBox" Content="显示网速排行榜" Margin="0,4,0,0"
IsChecked="False" Checked="ShowTopListCheckBox_Changed"
Unchecked="ShowTopListCheckBox_Changed" IsEnabled="False" />
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<TextBlock Text="排行榜数量:" VerticalAlignment="Center" Margin="0,0,8,0" />
<ComboBox x:Name="TopListCountComboBox" Width="80"
SelectionChanged="TopListCountComboBox_SelectionChanged" IsEnabled="False">
<ComboBoxItem Content="1" Tag="1" />
<ComboBoxItem Content="2" Tag="2" />
<ComboBoxItem Content="3" Tag="3" IsSelected="True" />
<ComboBoxItem Content="4" Tag="4" />
<ComboBoxItem Content="5" Tag="5" />
</ComboBox>
</StackPanel>
</StackPanel>
</Grid> </Grid>
<!-- 交互 --> <!-- 交互 -->
<TextBlock Text="交互" FontSize="14" FontWeight="Bold" Margin="0,0,0,12" /> <TextBlock Text="交互" FontSize="14" FontWeight="Bold" Margin="0,0,0,12" />
<Grid> <Grid Margin="0,0,0,16">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="80" /> <ColumnDefinition Width="80" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -129,6 +145,56 @@
<ComboBoxItem Content="打开设置" Tag="Settings" /> <ComboBoxItem Content="打开设置" Tag="Settings" />
</ComboBox> </ComboBox>
</Grid> </Grid>
<!-- 窗口行为 -->
<TextBlock Text="窗口行为" FontSize="14" FontWeight="Bold" Margin="0,0,0,12" />
<Grid Margin="0,0,0,16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 始终置顶 -->
<CheckBox Grid.Row="0" x:Name="TopmostCheckBox" Content="始终置顶"
IsChecked="True" Checked="TopmostCheckBox_Changed"
Unchecked="TopmostCheckBox_Changed" />
<!-- 锁定窗口位置 -->
<CheckBox Grid.Row="2" x:Name="LockPositionCheckBox" Content="锁定窗口位置"
IsChecked="False" Checked="LockPositionCheckBox_Changed"
Unchecked="LockPositionCheckBox_Changed" />
<!-- 启用点击穿透 -->
<CheckBox Grid.Row="4" x:Name="ClickThroughCheckBox" Content="启用点击穿透"
IsChecked="False" Checked="ClickThroughCheckBox_Changed"
Unchecked="ClickThroughCheckBox_Changed" />
<!-- 不能超出屏幕 -->
<CheckBox Grid.Row="6" x:Name="SnapToScreenCheckBox" Content="不能超出屏幕"
IsChecked="False" Checked="SnapToScreenCheckBox_Changed"
Unchecked="SnapToScreenCheckBox_Changed" />
<!-- 显示详细信息 -->
<CheckBox Grid.Row="8" x:Name="ShowDetailedInfoCheckBox" Content="显示详细信息"
IsChecked="False" Checked="ShowDetailedInfoCheckBox_Changed"
Unchecked="ShowDetailedInfoCheckBox_Changed" />
<!-- 重置窗口位置按钮 -->
<Button Grid.Row="10" HorizontalAlignment="Left" Content="重置窗口位置"
Click="ResetWindowPosition_Click" Padding="12,6" MinWidth="100" />
</Grid>
</StackPanel> </StackPanel>
<!-- 右侧预览区 --> <!-- 右侧预览区 -->
@@ -21,6 +21,7 @@ public partial class SettingsWindow : Window
{ {
_instance = new SettingsWindow(); _instance = new SettingsWindow();
} }
return _instance; return _instance;
} }
} }
@@ -31,10 +32,10 @@ public partial class SettingsWindow : Window
_settings = NetViewSettings.Instance; _settings = NetViewSettings.Instance;
this.Loaded += SettingsWindow_Loaded; this.Loaded += SettingsWindow_Loaded;
this.Closing += SettingsWindow_Closing; this.Closing += SettingsWindow_Closing;
LoadCurrentSettings(); LoadCurrentSettings();
UpdatePreview(); UpdatePreview();
Log.Info("SettingsWindow 初始化"); Log.Info("SettingsWindow 初始化");
} }
@@ -82,6 +83,19 @@ public partial class SettingsWindow : Window
VerticalLayoutRadio.IsChecked = _settings.LayoutDirection == LayoutDirection.Vertical; VerticalLayoutRadio.IsChecked = _settings.LayoutDirection == LayoutDirection.Vertical;
HorizontalLayoutRadio.IsChecked = _settings.LayoutDirection == LayoutDirection.Horizontal; HorizontalLayoutRadio.IsChecked = _settings.LayoutDirection == LayoutDirection.Horizontal;
// 设置网速Top榜
ShowTopListCheckBox.IsChecked = _settings.ShowNetworkTopList;
foreach (ComboBoxItem item in TopListCountComboBox.Items)
{
if (item.Tag.ToString() == _settings.NetworkTopListCount.ToString())
{
TopListCountComboBox.SelectedItem = item;
break;
}
}
UpdateTopListAvailability();
// 设置双击动作 // 设置双击动作
foreach (ComboBoxItem item in DoubleClickActionComboBox.Items) foreach (ComboBoxItem item in DoubleClickActionComboBox.Items)
{ {
@@ -92,6 +106,13 @@ public partial class SettingsWindow : Window
} }
} }
// 设置窗口行为选项
TopmostCheckBox.IsChecked = _settings.Topmost;
LockPositionCheckBox.IsChecked = _settings.LockPosition;
ClickThroughCheckBox.IsChecked = _settings.ClickThrough;
SnapToScreenCheckBox.IsChecked = _settings.SnapToScreen;
ShowDetailedInfoCheckBox.IsChecked = _settings.ShowDetailedInfo;
UpdateColorButtons(); UpdateColorButtons();
} }
@@ -99,10 +120,13 @@ public partial class SettingsWindow : Window
{ {
try try
{ {
var textColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(_settings.TextColor); var textColor =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(_settings.TextColor);
TextColorButton.Background = new SolidColorBrush(textColor); TextColorButton.Background = new SolidColorBrush(textColor);
var backgroundColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(_settings.BackgroundColor); var backgroundColor =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(_settings
.BackgroundColor);
BackgroundColorButton.Background = new SolidColorBrush(backgroundColor); BackgroundColorButton.Background = new SolidColorBrush(backgroundColor);
} }
catch (Exception ex) catch (Exception ex)
@@ -116,17 +140,22 @@ public partial class SettingsWindow : Window
var dialog = new System.Windows.Forms.ColorDialog(); var dialog = new System.Windows.Forms.ColorDialog();
try try
{ {
var currentColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(TextColorBox.Text); var currentColor =
dialog.Color = System.Drawing.Color.FromArgb(currentColor.A, currentColor.R, currentColor.G, currentColor.B); (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(TextColorBox.Text);
dialog.Color =
System.Drawing.Color.FromArgb(currentColor.A, currentColor.R, currentColor.G, currentColor.B);
}
catch
{
} }
catch { }
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{ {
var color = dialog.Color; var color = dialog.Color;
var colorString = $"#{color.R:X2}{color.G:X2}{color.B:X2}"; var colorString = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
TextColorBox.Text = colorString; TextColorBox.Text = colorString;
TextColorButton.Background = new SolidColorBrush(System.Windows.Media.Color.FromRgb(color.R, color.G, color.B)); TextColorButton.Background =
new SolidColorBrush(System.Windows.Media.Color.FromRgb(color.R, color.G, color.B));
UpdatePreview(); UpdatePreview();
} }
} }
@@ -136,10 +165,14 @@ public partial class SettingsWindow : Window
var dialog = new System.Windows.Forms.ColorDialog(); var dialog = new System.Windows.Forms.ColorDialog();
try try
{ {
var currentColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(BackgroundColorBox.Text); var currentColor =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(BackgroundColorBox
.Text);
dialog.Color = System.Drawing.Color.FromArgb(currentColor.R, currentColor.G, currentColor.B); dialog.Color = System.Drawing.Color.FromArgb(currentColor.R, currentColor.G, currentColor.B);
} }
catch { } catch
{
}
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{ {
@@ -148,7 +181,8 @@ public partial class SettingsWindow : Window
var alpha = (byte)(OpacitySlider.Value / 100.0 * 255); var alpha = (byte)(OpacitySlider.Value / 100.0 * 255);
var colorString = $"#{alpha:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; var colorString = $"#{alpha:X2}{color.R:X2}{color.G:X2}{color.B:X2}";
BackgroundColorBox.Text = colorString; BackgroundColorBox.Text = colorString;
BackgroundColorButton.Background = new SolidColorBrush(System.Windows.Media.Color.FromRgb(color.R, color.G, color.B)); BackgroundColorButton.Background =
new SolidColorBrush(System.Windows.Media.Color.FromRgb(color.R, color.G, color.B));
UpdatePreview(); UpdatePreview();
} }
} }
@@ -158,7 +192,7 @@ public partial class SettingsWindow : Window
if (OpacityText != null) if (OpacityText != null)
{ {
OpacityText.Text = $"{(int)e.NewValue}%"; OpacityText.Text = $"{(int)e.NewValue}%";
// 更新背景颜色的透明度 // 更新背景颜色的透明度
try try
{ {
@@ -167,12 +201,14 @@ public partial class SettingsWindow : Window
{ {
colorWithoutAlpha = "#" + colorWithoutAlpha.Substring(3); // 移除透明度部分 colorWithoutAlpha = "#" + colorWithoutAlpha.Substring(3); // 移除透明度部分
} }
var alpha = (byte)(e.NewValue / 100.0 * 255); var alpha = (byte)(e.NewValue / 100.0 * 255);
var color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(colorWithoutAlpha); var color =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter
.ConvertFromString(colorWithoutAlpha);
var newColorString = $"#{alpha:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; var newColorString = $"#{alpha:X2}{color.R:X2}{color.G:X2}{color.B:X2}";
BackgroundColorBox.Text = newColorString; BackgroundColorBox.Text = newColorString;
UpdatePreview(); UpdatePreview();
} }
catch (Exception ex) catch (Exception ex)
@@ -194,14 +230,87 @@ public partial class SettingsWindow : Window
private void LayoutDirection_Changed(object sender, RoutedEventArgs e) private void LayoutDirection_Changed(object sender, RoutedEventArgs e)
{ {
UpdateTopListAvailability();
UpdatePreview(); UpdatePreview();
} }
private void ShowTopListCheckBox_Changed(object sender, RoutedEventArgs e)
{
UpdateTopListCountVisibility();
}
private void TopListCountComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Top榜数量改变不需要预览更新
}
private void UpdateTopListAvailability()
{
if (ShowTopListCheckBox == null || TopListCountComboBox == null) return;
var isHorizontal = HorizontalLayoutRadio.IsChecked == true;
ShowTopListCheckBox.IsEnabled = isHorizontal;
if (!isHorizontal)
{
ShowTopListCheckBox.IsChecked = false;
}
UpdateTopListCountVisibility();
}
private void UpdateTopListCountVisibility()
{
if (TopListCountComboBox == null) return;
var isEnabled = ShowTopListCheckBox.IsEnabled && ShowTopListCheckBox.IsChecked == true;
TopListCountComboBox.IsEnabled = isEnabled;
}
private void DoubleClickActionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) private void DoubleClickActionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
// 双击动作不需要预览更新 // 双击动作不需要预览更新
} }
private void TopmostCheckBox_Changed(object sender, RoutedEventArgs e)
{
// 置顶设置变化,不需要预览更新
}
private void LockPositionCheckBox_Changed(object sender, RoutedEventArgs e)
{
// 锁定位置设置变化,不需要预览更新
}
private void ClickThroughCheckBox_Changed(object sender, RoutedEventArgs e)
{
// 点击穿透设置变化,不需要预览更新
}
private void SnapToScreenCheckBox_Changed(object sender, RoutedEventArgs e)
{
// 屏幕边界设置变化,不需要预览更新
}
private void ShowDetailedInfoCheckBox_Changed(object sender, RoutedEventArgs e)
{
// 详细信息设置变化,不需要预览更新
}
private void ResetWindowPosition_Click(object sender, RoutedEventArgs e)
{
// 重置窗口位置 - 这里可以调用 NetView 的重置位置方法
try
{
System.Windows.MessageBox.Show("窗口位置已重置到屏幕中心。", "重置位置", MessageBoxButton.OK, MessageBoxImage.Information);
Log.Info("窗口位置重置请求");
}
catch (Exception ex)
{
Log.Error4Ctx($"重置窗口位置失败: {ex.Message}");
}
}
private void UpdatePreview() private void UpdatePreview()
{ {
if (PreviewBorder == null || PreviewPanel == null) return; if (PreviewBorder == null || PreviewPanel == null) return;
@@ -209,23 +318,27 @@ public partial class SettingsWindow : Window
try try
{ {
// 更新背景色 // 更新背景色
var backgroundColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(BackgroundColorBox.Text); var backgroundColor =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(BackgroundColorBox
.Text);
PreviewBorder.Background = new SolidColorBrush(backgroundColor); PreviewBorder.Background = new SolidColorBrush(backgroundColor);
// 更新文字颜色 // 更新文字颜色
var textColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(TextColorBox.Text); var textColor =
(System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(TextColorBox.Text);
var textBrush = new SolidColorBrush(textColor); var textBrush = new SolidColorBrush(textColor);
PreviewUploadText.Foreground = textBrush; PreviewUploadText.Foreground = textBrush;
PreviewDownloadText.Foreground = textBrush; PreviewDownloadText.Foreground = textBrush;
// 更新布局方向 // 更新布局方向
PreviewPanel.Orientation = HorizontalLayoutRadio.IsChecked == true ? PreviewPanel.Orientation = HorizontalLayoutRadio.IsChecked == true
System.Windows.Controls.Orientation.Horizontal : System.Windows.Controls.Orientation.Vertical; ? System.Windows.Controls.Orientation.Horizontal
: System.Windows.Controls.Orientation.Vertical;
// 更新显示文本(根据单位设置) // 更新显示文本(根据单位设置)
var showUnit = ShowUnitCheckBox.IsChecked == true; var showUnit = ShowUnitCheckBox.IsChecked == true;
var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto"; var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto";
string uploadText, downloadText; string uploadText, downloadText;
switch (selectedUnit) switch (selectedUnit)
{ {
@@ -279,12 +392,24 @@ public partial class SettingsWindow : Window
var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto"; var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto";
_settings.SpeedUnit = Enum.Parse<SpeedUnit>(selectedUnit); _settings.SpeedUnit = Enum.Parse<SpeedUnit>(selectedUnit);
_settings.LayoutDirection = HorizontalLayoutRadio.IsChecked == true ? _settings.LayoutDirection = HorizontalLayoutRadio.IsChecked == true
LayoutDirection.Horizontal : LayoutDirection.Vertical; ? LayoutDirection.Horizontal
: LayoutDirection.Vertical;
_settings.ShowNetworkTopList = ShowTopListCheckBox.IsChecked == true;
var selectedCount = ((ComboBoxItem)TopListCountComboBox.SelectedItem)?.Tag?.ToString() ?? "3";
_settings.NetworkTopListCount = int.Parse(selectedCount);
var selectedAction = ((ComboBoxItem)DoubleClickActionComboBox.SelectedItem)?.Tag?.ToString() ?? "None"; var selectedAction = ((ComboBoxItem)DoubleClickActionComboBox.SelectedItem)?.Tag?.ToString() ?? "None";
_settings.DoubleClickAction = Enum.Parse<DoubleClickAction>(selectedAction); _settings.DoubleClickAction = Enum.Parse<DoubleClickAction>(selectedAction);
// 应用窗口行为设置
_settings.Topmost = TopmostCheckBox.IsChecked == true;
_settings.LockPosition = LockPositionCheckBox.IsChecked == true;
_settings.ClickThrough = ClickThroughCheckBox.IsChecked == true;
_settings.SnapToScreen = SnapToScreenCheckBox.IsChecked == true;
_settings.ShowDetailedInfo = ShowDetailedInfoCheckBox.IsChecked == true;
// 保存设置 // 保存设置
_settings.SaveSettings(); _settings.SaveSettings();
@@ -300,9 +425,9 @@ public partial class SettingsWindow : Window
private void RestoreDefaults_Click(object sender, RoutedEventArgs e) private void RestoreDefaults_Click(object sender, RoutedEventArgs e)
{ {
var result = System.Windows.MessageBox.Show("确定要恢复默认设置吗?", "确认", var result = System.Windows.MessageBox.Show("确定要恢复默认设置吗?", "确认",
MessageBoxButton.YesNo, MessageBoxImage.Question); MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
_settings.RestoreDefaults(); _settings.RestoreDefaults();