A:增加网络占用TOP展示
This commit is contained in:
1 parent
1f06ca6cac
commit
1c56007afd
11 files changed
+891
-114
No files matched your search
@@ -87,6 +87,15 @@ public class AppSetting
|
||||
/// </summary>
|
||||
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>
|
||||
/// 更新时间
|
||||
|
||||
@@ -82,6 +82,8 @@ public class NetVizorDbContext : IDisposable
|
||||
DoubleClickAction INTEGER DEFAULT 0,
|
||||
RunAsAdmin INTEGER DEFAULT 0,
|
||||
AutoStart INTEGER DEFAULT 0,
|
||||
ShowNetworkTopList INTEGER DEFAULT 0,
|
||||
NetworkTopListCount INTEGER DEFAULT 3,
|
||||
UpdateTime INTEGER NOT NULL
|
||||
)");
|
||||
|
||||
@@ -282,7 +284,8 @@ public class NetVizorDbContext : IDisposable
|
||||
var needsMigration = columns.Contains("WindowWidth") || columns.Contains("WindowHeight") ||
|
||||
columns.Contains("Theme") || columns.Contains("Language") ||
|
||||
columns.Contains("MonitoringEnabled") ||
|
||||
!columns.Contains("IsClickThrough") || !columns.Contains("TextColor");
|
||||
!columns.Contains("IsClickThrough") || !columns.Contains("TextColor") ||
|
||||
!columns.Contains("ShowNetworkTopList") || !columns.Contains("NetworkTopListCount");
|
||||
|
||||
if (needsMigration)
|
||||
{
|
||||
@@ -315,6 +318,8 @@ public class NetVizorDbContext : IDisposable
|
||||
DoubleClickAction INTEGER DEFAULT 0,
|
||||
RunAsAdmin INTEGER DEFAULT 0,
|
||||
AutoStart INTEGER DEFAULT 0,
|
||||
ShowNetworkTopList INTEGER DEFAULT 0,
|
||||
NetworkTopListCount INTEGER DEFAULT 3,
|
||||
UpdateTime INTEGER NOT NULL
|
||||
)");
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ public class DataService : IDataService, IDisposable
|
||||
DoubleClickAction = 0, // None
|
||||
RunAsAdmin = false,
|
||||
AutoStart = false,
|
||||
ShowNetworkTopList = false, // 默认关闭网速Top榜
|
||||
NetworkTopListCount = 3, // 默认显示3个
|
||||
UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ namespace Shell.Models;
|
||||
public class NetViewSettings : INotifyPropertyChanged
|
||||
{
|
||||
private static NetViewSettings? _instance;
|
||||
|
||||
private static readonly string SettingsFilePath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"NetVizor", "settings.json");
|
||||
|
||||
public static NetViewSettings Instance
|
||||
@@ -21,60 +22,187 @@ public class NetViewSettings : INotifyPropertyChanged
|
||||
{
|
||||
_instance = LoadSettings();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
// 外观设置
|
||||
private string _textColor = "#333333";
|
||||
|
||||
public string TextColor
|
||||
{
|
||||
get => _textColor;
|
||||
set { _textColor = value; OnPropertyChanged(); }
|
||||
set
|
||||
{
|
||||
_textColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private string _backgroundColor = "#F0FFFFFF";
|
||||
|
||||
public string BackgroundColor
|
||||
{
|
||||
get => _backgroundColor;
|
||||
set { _backgroundColor = value; OnPropertyChanged(); }
|
||||
set
|
||||
{
|
||||
_backgroundColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _backgroundOpacity = 94.0;
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set { _backgroundOpacity = value; OnPropertyChanged(); }
|
||||
set
|
||||
{
|
||||
_backgroundOpacity = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示设置
|
||||
private SpeedUnit _speedUnit = SpeedUnit.Auto;
|
||||
|
||||
public SpeedUnit SpeedUnit
|
||||
{
|
||||
get => _speedUnit;
|
||||
set { _speedUnit = value; OnPropertyChanged(); }
|
||||
set
|
||||
{
|
||||
_speedUnit = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _showUnit = true;
|
||||
|
||||
public bool ShowUnit
|
||||
{
|
||||
get => _showUnit;
|
||||
set { _showUnit = value; OnPropertyChanged(); }
|
||||
set
|
||||
{
|
||||
_showUnit = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private LayoutDirection _layoutDirection = LayoutDirection.Vertical;
|
||||
|
||||
public LayoutDirection 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;
|
||||
|
||||
public DoubleClickAction 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;
|
||||
@@ -102,7 +230,7 @@ public class NetViewSettings : INotifyPropertyChanged
|
||||
|
||||
var json = JsonSerializer.Serialize(this, options);
|
||||
File.WriteAllText(SettingsFilePath, json);
|
||||
|
||||
|
||||
Log.Info("设置已保存");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -122,7 +250,7 @@ public class NetViewSettings : INotifyPropertyChanged
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() }
|
||||
};
|
||||
|
||||
|
||||
var settings = JsonSerializer.Deserialize<NetViewSettings>(json, options);
|
||||
if (settings != null)
|
||||
{
|
||||
@@ -148,8 +276,15 @@ public class NetViewSettings : INotifyPropertyChanged
|
||||
SpeedUnit = SpeedUnit.Auto;
|
||||
ShowUnit = true;
|
||||
LayoutDirection = LayoutDirection.Vertical;
|
||||
ShowNetworkTopList = false;
|
||||
NetworkTopListCount = 3;
|
||||
DoubleClickAction = DoubleClickAction.None;
|
||||
|
||||
LockPosition = false;
|
||||
ClickThrough = false;
|
||||
Topmost = true;
|
||||
SnapToScreen = false;
|
||||
ShowDetailedInfo = false;
|
||||
|
||||
Log.Info("设置已恢复默认值");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Assets\Icons\"/>
|
||||
<Folder Include="Services\"/>
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<d:UserControl.DataContext>
|
||||
<viewModel:NetworkMonitorViewModel />
|
||||
</d:UserControl.DataContext>
|
||||
|
||||
|
||||
<Border
|
||||
x:Name="MainBorder"
|
||||
CornerRadius="8"
|
||||
@@ -29,57 +29,136 @@
|
||||
Opacity="0.3"
|
||||
BlurRadius="8" />
|
||||
</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 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>
|
||||
<Grid Margin="10,8">
|
||||
<StackPanel x:Name="MainPanel" Orientation="Vertical">
|
||||
<!-- Global Speed Panel -->
|
||||
<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 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>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -7,6 +7,7 @@ using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using Shell.Views;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Shell.Utils;
|
||||
|
||||
@@ -18,6 +19,8 @@ public class TrayIconManager : IDisposable
|
||||
private DispatcherTimer _clickTimer;
|
||||
private bool _isDoubleClick = false;
|
||||
private DispatcherTimer _menuCloseTimer;
|
||||
private bool _lastLeftButtonState = false;
|
||||
private bool _lastRightButtonState = false;
|
||||
|
||||
// Win32 API 用于获取鼠标位置和显示器信息
|
||||
[DllImport("user32.dll")]
|
||||
@@ -29,6 +32,13 @@ public class TrayIconManager : IDisposable
|
||||
[DllImport("user32.dll")]
|
||||
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
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetDpiForWindow(IntPtr hwnd);
|
||||
@@ -108,16 +118,21 @@ public class TrayIconManager : IDisposable
|
||||
_contextMenu = new ContextMenu();
|
||||
|
||||
// 添加失去焦点时关闭事件
|
||||
_contextMenu.Closed += (s, e) =>
|
||||
{
|
||||
/* 菜单关闭 */
|
||||
};
|
||||
_contextMenu.Closed += (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
|
||||
{
|
||||
@@ -282,8 +297,13 @@ public class TrayIconManager : IDisposable
|
||||
_contextMenu.Placement = PlacementMode.MousePoint;
|
||||
_contextMenu.StaysOpen = true; // 保持打开,我们手动控制关闭
|
||||
|
||||
// 禁用默认的关闭行为
|
||||
_contextMenu.IsOpen = true;
|
||||
|
||||
// 强制设置,确保菜单不会因为失去焦点而关闭
|
||||
_contextMenu.UpdateLayout();
|
||||
|
||||
|
||||
// 启动定时器来检测点击外部
|
||||
StartMenuCloseTimer();
|
||||
}
|
||||
@@ -297,9 +317,10 @@ public class TrayIconManager : IDisposable
|
||||
|
||||
_menuCloseTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(100) // 每100ms检查一次
|
||||
Interval = TimeSpan.FromMilliseconds(50) // 改为每50ms检查一次,提高响应速度
|
||||
};
|
||||
|
||||
|
||||
_menuCloseTimer.Tick += (s, e) =>
|
||||
{
|
||||
if (!_contextMenu.IsOpen)
|
||||
@@ -317,12 +338,23 @@ public class TrayIconManager : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有鼠标按键按下
|
||||
if (System.Windows.Input.Mouse.LeftButton == System.Windows.Input.MouseButtonState.Pressed ||
|
||||
System.Windows.Input.Mouse.RightButton == System.Windows.Input.MouseButtonState.Pressed)
|
||||
// 使用Win32 API检测鼠标按键状态变化
|
||||
bool currentLeftState = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
|
||||
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)
|
||||
_contextMenu.IsOpen = false;
|
||||
@@ -332,13 +364,20 @@ public class TrayIconManager : IDisposable
|
||||
};
|
||||
|
||||
_menuCloseTimer.Start();
|
||||
|
||||
// 重置按键状态,避免误检测
|
||||
_lastLeftButtonState = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
|
||||
_lastRightButtonState = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
private bool IsMouseOverContextMenu()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_contextMenu == null || !_contextMenu.IsOpen) return false;
|
||||
if (_contextMenu == null || !_contextMenu.IsOpen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取屏幕上的鼠标位置
|
||||
GetCursorPos(out POINT screenPoint);
|
||||
@@ -346,10 +385,17 @@ public class TrayIconManager : IDisposable
|
||||
|
||||
// 获取菜单的屏幕位置和大小
|
||||
var contextMenuElement = _contextMenu as FrameworkElement;
|
||||
if (contextMenuElement == null) return false;
|
||||
if (contextMenuElement == null)
|
||||
{
|
||||
return true; // 改为返回true,避免意外关闭
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
@@ -357,14 +403,25 @@ public class TrayIconManager : IDisposable
|
||||
contextMenuElement.PointToScreen(new System.Windows.Point(contextMenuElement.ActualWidth,
|
||||
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 Common.Logger;
|
||||
using Common.Utils;
|
||||
using Utils.ETW.Services;
|
||||
using Shell.Models;
|
||||
using Data;
|
||||
using Utils.ETW.Models;
|
||||
|
||||
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
|
||||
|
||||
#region Commands
|
||||
@@ -126,6 +155,7 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
|
||||
|
||||
// 初始化数据
|
||||
LoadNetworkInterfaces();
|
||||
LoadTopListSettings();
|
||||
_updateTimer.Start();
|
||||
}
|
||||
}
|
||||
@@ -369,6 +399,9 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
|
||||
DownloadSpeed = speed.DownloadSpeedText;
|
||||
UploadSpeed = speed.UploadSpeedText;
|
||||
TotalSpeed = speed.TotalSpeedText;
|
||||
|
||||
// 更新Top榜
|
||||
UpdateTopList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -403,6 +436,145 @@ public class NetworkMonitorViewModel : INotifyPropertyChanged
|
||||
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()
|
||||
{
|
||||
@@ -480,4 +652,4 @@ public class RelayCommand : ICommand
|
||||
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"
|
||||
mc:Ignorable="d"
|
||||
Title="NetVizor - 设置"
|
||||
Height="480"
|
||||
Height="620"
|
||||
Width="470"
|
||||
MinHeight="480"
|
||||
MinHeight="620"
|
||||
MinWidth="470"
|
||||
MaxHeight="480"
|
||||
MaxHeight="620"
|
||||
MaxWidth="470"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="NoResize">
|
||||
@@ -108,14 +108,30 @@
|
||||
|
||||
<!-- 显示单位 -->
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="显示选项" VerticalAlignment="Center" />
|
||||
<CheckBox Grid.Row="4" Grid.Column="1" x:Name="ShowUnitCheckBox" Content="显示单位"
|
||||
IsChecked="True" Checked="ShowUnitCheckBox_Changed"
|
||||
Unchecked="ShowUnitCheckBox_Changed" />
|
||||
<StackPanel Grid.Row="4" Grid.Column="1">
|
||||
<CheckBox x:Name="ShowUnitCheckBox" Content="显示单位"
|
||||
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>
|
||||
|
||||
<!-- 交互 -->
|
||||
<TextBlock Text="交互" FontSize="14" FontWeight="Bold" Margin="0,0,0,12" />
|
||||
<Grid>
|
||||
<Grid Margin="0,0,0,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@@ -129,6 +145,56 @@
|
||||
<ComboBoxItem Content="打开设置" Tag="Settings" />
|
||||
</ComboBox>
|
||||
</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>
|
||||
|
||||
<!-- 右侧预览区 -->
|
||||
|
||||
@@ -21,6 +21,7 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
_instance = new SettingsWindow();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
@@ -31,10 +32,10 @@ public partial class SettingsWindow : Window
|
||||
_settings = NetViewSettings.Instance;
|
||||
this.Loaded += SettingsWindow_Loaded;
|
||||
this.Closing += SettingsWindow_Closing;
|
||||
|
||||
|
||||
LoadCurrentSettings();
|
||||
UpdatePreview();
|
||||
|
||||
|
||||
Log.Info("SettingsWindow 初始化");
|
||||
}
|
||||
|
||||
@@ -82,6 +83,19 @@ public partial class SettingsWindow : Window
|
||||
VerticalLayoutRadio.IsChecked = _settings.LayoutDirection == LayoutDirection.Vertical;
|
||||
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)
|
||||
{
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -99,10 +120,13 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -116,17 +140,22 @@ public partial class SettingsWindow : Window
|
||||
var dialog = new System.Windows.Forms.ColorDialog();
|
||||
try
|
||||
{
|
||||
var currentColor = (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);
|
||||
var currentColor =
|
||||
(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)
|
||||
{
|
||||
var color = dialog.Color;
|
||||
var colorString = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -136,10 +165,14 @@ public partial class SettingsWindow : Window
|
||||
var dialog = new System.Windows.Forms.ColorDialog();
|
||||
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);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
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 colorString = $"#{alpha:X2}{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -158,7 +192,7 @@ public partial class SettingsWindow : Window
|
||||
if (OpacityText != null)
|
||||
{
|
||||
OpacityText.Text = $"{(int)e.NewValue}%";
|
||||
|
||||
|
||||
// 更新背景颜色的透明度
|
||||
try
|
||||
{
|
||||
@@ -167,12 +201,14 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
colorWithoutAlpha = "#" + colorWithoutAlpha.Substring(3); // 移除透明度部分
|
||||
}
|
||||
|
||||
|
||||
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}";
|
||||
BackgroundColorBox.Text = newColorString;
|
||||
|
||||
|
||||
UpdatePreview();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -194,14 +230,87 @@ public partial class SettingsWindow : Window
|
||||
|
||||
private void LayoutDirection_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UpdateTopListAvailability();
|
||||
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 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()
|
||||
{
|
||||
if (PreviewBorder == null || PreviewPanel == null) return;
|
||||
@@ -209,23 +318,27 @@ public partial class SettingsWindow : Window
|
||||
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);
|
||||
|
||||
// 更新文字颜色
|
||||
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);
|
||||
PreviewUploadText.Foreground = textBrush;
|
||||
PreviewDownloadText.Foreground = textBrush;
|
||||
|
||||
// 更新布局方向
|
||||
PreviewPanel.Orientation = HorizontalLayoutRadio.IsChecked == true ?
|
||||
System.Windows.Controls.Orientation.Horizontal : System.Windows.Controls.Orientation.Vertical;
|
||||
PreviewPanel.Orientation = HorizontalLayoutRadio.IsChecked == true
|
||||
? System.Windows.Controls.Orientation.Horizontal
|
||||
: System.Windows.Controls.Orientation.Vertical;
|
||||
|
||||
// 更新显示文本(根据单位设置)
|
||||
var showUnit = ShowUnitCheckBox.IsChecked == true;
|
||||
var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto";
|
||||
|
||||
|
||||
string uploadText, downloadText;
|
||||
switch (selectedUnit)
|
||||
{
|
||||
@@ -279,12 +392,24 @@ public partial class SettingsWindow : Window
|
||||
var selectedUnit = ((ComboBoxItem)SpeedUnitComboBox.SelectedItem)?.Tag?.ToString() ?? "Auto";
|
||||
_settings.SpeedUnit = Enum.Parse<SpeedUnit>(selectedUnit);
|
||||
|
||||
_settings.LayoutDirection = HorizontalLayoutRadio.IsChecked == true ?
|
||||
LayoutDirection.Horizontal : LayoutDirection.Vertical;
|
||||
_settings.LayoutDirection = HorizontalLayoutRadio.IsChecked == true
|
||||
? 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";
|
||||
_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();
|
||||
|
||||
@@ -300,9 +425,9 @@ public partial class SettingsWindow : Window
|
||||
|
||||
private void RestoreDefaults_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var result = System.Windows.MessageBox.Show("确定要恢复默认设置吗?", "确认",
|
||||
var result = System.Windows.MessageBox.Show("确定要恢复默认设置吗?", "确认",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
_settings.RestoreDefaults();
|
||||
|
||||
Reference in new issue
Block a user