A:设置集成数据库

This commit is contained in:
jimmy committed 2025-08-07 13:51:18 +08:00
1 parent c999440973
commit e35eb3e103
8 files changed
+491 -77

No files matched your search

@@ -0,0 +1,88 @@
namespace Data.Models;
/// <summary>
/// 应用网络聚合数据基类
/// </summary>
public abstract class AppNetworkAggregatedBase
{
public int Id { get; set; }
/// <summary>
/// 软件 ID
/// </summary>
public string AppId { get; set; }
/// <summary>
/// 总上传字节数
/// </summary>
public long TotalUploadBytes { get; set; }
/// <summary>
/// 总下载字节数
/// </summary>
public long TotalDownloadBytes { get; set; }
/// <summary>
/// 连接数量
/// </summary>
public int ConnectionCount { get; set; }
/// <summary>
/// 唯一远程IP数量
/// </summary>
public int UniqueRemoteIPs { get; set; }
/// <summary>
/// 唯一远程端口数量
/// </summary>
public int UniqueRemotePorts { get; set; }
/// <summary>
/// 创建时间戳
/// </summary>
public long CreatedTimestamp { get; set; }
}
/// <summary>
/// 应用网络小时聚合数据
/// </summary>
public class AppNetworkHourly : AppNetworkAggregatedBase
{
/// <summary>
/// 小时时间戳(Unix时间戳,精确到小时)
/// </summary>
public long HourTimestamp { get; set; }
}
/// <summary>
/// 应用网络天聚合数据
/// </summary>
public class AppNetworkDaily : AppNetworkAggregatedBase
{
/// <summary>
/// 天时间戳(Unix时间戳,精确到天)
/// </summary>
public long DayTimestamp { get; set; }
}
/// <summary>
/// 应用网络周聚合数据
/// </summary>
public class AppNetworkWeekly : AppNetworkAggregatedBase
{
/// <summary>
/// 周时间戳(Unix时间戳,精确到周)
/// </summary>
public long WeekTimestamp { get; set; }
}
/// <summary>
/// 应用网络月聚合数据
/// </summary>
public class AppNetworkMonthly : AppNetworkAggregatedBase
{
/// <summary>
/// 月时间戳(Unix时间戳,精确到月)
/// </summary>
public long MonthTimestamp { get; set; }
}
+64 -11
View File
@@ -17,17 +17,70 @@ public class AppSetting
/// </summary>
public int WindowY { get; set; } = 0;
/*
*文本颜色
背景颜色
透明度
速度单位
布局方向(横向,纵向)
展示单位(true/false)
双击动作(1,2,3,)
默认以管理员身份启动(true/false)
*
*/
/// <summary>
/// 点击穿透功能
/// </summary>
public bool IsClickThrough { get; set; } = false;
/// <summary>
/// 位置锁定
/// </summary>
public bool IsPositionLocked { get; set; } = false;
/// <summary>
/// 对齐到屏幕
/// </summary>
public bool SnapToScreen { get; set; } = false;
/// <summary>
/// 显示详细信息
/// </summary>
public bool ShowDetailedInfo { get; set; } = false;
/// <summary>
/// 窗口置顶
/// </summary>
public bool IsTopmost { get; set; } = false;
/// <summary>
/// 文本颜色
/// </summary>
public string TextColor { get; set; } = "#FFFFFF";
/// <summary>
/// 背景颜色
/// </summary>
public string BackgroundColor { get; set; } = "#000000";
/// <summary>
/// 透明度 (0-100)
/// </summary>
public int Opacity { get; set; } = 100;
/// <summary>
/// 速度单位 (0: bps, 1: KB/s, 2: MB/s)
/// </summary>
public int SpeedUnit { get; set; } = 1;
/// <summary>
/// 布局方向 (0: 横向, 1: 纵向)
/// </summary>
public int LayoutDirection { get; set; } = 0;
/// <summary>
/// 展示单位
/// </summary>
public bool ShowUnit { get; set; } = true;
/// <summary>
/// 双击动作 (0: None, 1: TrafficAnalysis, 2: Settings)
/// </summary>
public int DoubleClickAction { get; set; } = 0;
/// <summary>
/// 默认以管理员身份启动
/// </summary>
public bool RunAsAdmin { get; set; } = false;
/// <summary>
/// 是否开机自启
@@ -0,0 +1,98 @@
namespace Data.Models;
/// <summary>
/// 全局网络聚合数据基类
/// </summary>
public abstract class GlobalNetworkAggregatedBase
{
public int Id { get; set; }
/// <summary>
/// 网卡 GUID
/// </summary>
public string NetworkCardGuid { get; set; }
/// <summary>
/// 总上传字节数
/// </summary>
public long TotalUpload { get; set; }
/// <summary>
/// 总下载字节数
/// </summary>
public long TotalDownload { get; set; }
/// <summary>
/// 平均上传速度
/// </summary>
public long AvgUpload { get; set; }
/// <summary>
/// 平均下载速度
/// </summary>
public long AvgDownload { get; set; }
/// <summary>
/// 最大上传速度
/// </summary>
public long MaxUpload { get; set; }
/// <summary>
/// 最大下载速度
/// </summary>
public long MaxDownload { get; set; }
/// <summary>
/// 记录数量(用于计算平均值)
/// </summary>
public int RecordCount { get; set; }
/// <summary>
/// 创建时间戳
/// </summary>
public long CreatedTimestamp { get; set; }
}
/// <summary>
/// 全局网络小时聚合数据
/// </summary>
public class GlobalNetworkHourly : GlobalNetworkAggregatedBase
{
/// <summary>
/// 小时时间戳(Unix时间戳,精确到小时)
/// </summary>
public long HourTimestamp { get; set; }
}
/// <summary>
/// 全局网络天聚合数据
/// </summary>
public class GlobalNetworkDaily : GlobalNetworkAggregatedBase
{
/// <summary>
/// 天时间戳(Unix时间戳,精确到天)
/// </summary>
public long DayTimestamp { get; set; }
}
/// <summary>
/// 全局网络周聚合数据
/// </summary>
public class GlobalNetworkWeekly : GlobalNetworkAggregatedBase
{
/// <summary>
/// 周时间戳(Unix时间戳,精确到周)
/// </summary>
public long WeekTimestamp { get; set; }
}
/// <summary>
/// 全局网络月聚合数据
/// </summary>
public class GlobalNetworkMonthly : GlobalNetworkAggregatedBase
{
/// <summary>
/// 月时间戳(Unix时间戳,精确到月)
/// </summary>
public long MonthTimestamp { get; set; }
}
+29 -2
View File
@@ -62,12 +62,25 @@ public class NetVizorDbContext : IDisposable
DeleteTime INTEGER DEFAULT 0
)");
// 创建AppSetting表 - 简化结构
// 创建AppSetting表 - 完整结构
await connection.ExecuteAsync(@"
CREATE TABLE IF NOT EXISTS AppSetting (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
WindowX INTEGER DEFAULT 0,
WindowY INTEGER DEFAULT 0,
IsClickThrough INTEGER DEFAULT 0,
IsPositionLocked INTEGER DEFAULT 0,
SnapToScreen INTEGER DEFAULT 0,
ShowDetailedInfo INTEGER DEFAULT 0,
IsTopmost INTEGER DEFAULT 0,
TextColor TEXT DEFAULT '#FFFFFF',
BackgroundColor TEXT DEFAULT '#000000',
Opacity INTEGER DEFAULT 100,
SpeedUnit INTEGER DEFAULT 1,
LayoutDirection INTEGER DEFAULT 0,
ShowUnit INTEGER DEFAULT 1,
DoubleClickAction INTEGER DEFAULT 0,
RunAsAdmin INTEGER DEFAULT 0,
AutoStart INTEGER DEFAULT 0,
UpdateTime INTEGER NOT NULL
)");
@@ -268,7 +281,8 @@ public class NetVizorDbContext : IDisposable
var columns = await GetTableColumnsAsync("AppSetting");
var needsMigration = columns.Contains("WindowWidth") || columns.Contains("WindowHeight") ||
columns.Contains("Theme") || columns.Contains("Language") ||
columns.Contains("MonitoringEnabled");
columns.Contains("MonitoringEnabled") ||
!columns.Contains("IsClickThrough") || !columns.Contains("TextColor");
if (needsMigration)
{
@@ -287,6 +301,19 @@ public class NetVizorDbContext : IDisposable
Id INTEGER PRIMARY KEY AUTOINCREMENT,
WindowX INTEGER DEFAULT 0,
WindowY INTEGER DEFAULT 0,
IsClickThrough INTEGER DEFAULT 0,
IsPositionLocked INTEGER DEFAULT 0,
SnapToScreen INTEGER DEFAULT 0,
ShowDetailedInfo INTEGER DEFAULT 0,
IsTopmost INTEGER DEFAULT 0,
TextColor TEXT DEFAULT '#FFFFFF',
BackgroundColor TEXT DEFAULT '#000000',
Opacity INTEGER DEFAULT 100,
SpeedUnit INTEGER DEFAULT 1,
LayoutDirection INTEGER DEFAULT 0,
ShowUnit INTEGER DEFAULT 1,
DoubleClickAction INTEGER DEFAULT 0,
RunAsAdmin INTEGER DEFAULT 0,
AutoStart INTEGER DEFAULT 0,
UpdateTime INTEGER NOT NULL
)");
@@ -30,8 +30,16 @@ public class AppSettingRepository : IAppSettingRepository
setting.UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
const string sql = @"
INSERT INTO AppSetting (WindowX, WindowY, AutoStart, UpdateTime)
VALUES (@WindowX, @WindowY, @AutoStart, @UpdateTime)";
INSERT INTO AppSetting (
WindowX, WindowY, IsClickThrough, IsPositionLocked, SnapToScreen, ShowDetailedInfo,
IsTopmost, TextColor, BackgroundColor, Opacity, SpeedUnit, LayoutDirection,
ShowUnit, DoubleClickAction, RunAsAdmin, AutoStart, UpdateTime
)
VALUES (
@WindowX, @WindowY, @IsClickThrough, @IsPositionLocked, @SnapToScreen, @ShowDetailedInfo,
@IsTopmost, @TextColor, @BackgroundColor, @Opacity, @SpeedUnit, @LayoutDirection,
@ShowUnit, @DoubleClickAction, @RunAsAdmin, @AutoStart, @UpdateTime
)";
var result = await _context.Connection.ExecuteAsync(sql, setting);
@@ -49,7 +57,13 @@ public class AppSettingRepository : IAppSettingRepository
const string sql = @"
UPDATE AppSetting
SET WindowX = @WindowX, WindowY = @WindowY, AutoStart = @AutoStart, UpdateTime = @UpdateTime
SET WindowX = @WindowX, WindowY = @WindowY, IsClickThrough = @IsClickThrough,
IsPositionLocked = @IsPositionLocked, SnapToScreen = @SnapToScreen,
ShowDetailedInfo = @ShowDetailedInfo, IsTopmost = @IsTopmost,
TextColor = @TextColor, BackgroundColor = @BackgroundColor, Opacity = @Opacity,
SpeedUnit = @SpeedUnit, LayoutDirection = @LayoutDirection, ShowUnit = @ShowUnit,
DoubleClickAction = @DoubleClickAction, RunAsAdmin = @RunAsAdmin,
AutoStart = @AutoStart, UpdateTime = @UpdateTime
WHERE Id = @Id";
return await _context.Connection.ExecuteAsync(sql, setting);
@@ -59,11 +59,24 @@ public class DataService : IDataService, IDisposable
{
WindowX = 100,
WindowY = 100,
IsClickThrough = false,
IsPositionLocked = false,
SnapToScreen = false,
ShowDetailedInfo = false,
IsTopmost = false,
TextColor = "#FFFFFF",
BackgroundColor = "#000000",
Opacity = 100,
SpeedUnit = 1, // KB/s
LayoutDirection = 0, // 横向
ShowUnit = true,
DoubleClickAction = 0, // None
RunAsAdmin = false,
AutoStart = false,
UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
setting.Id = await AppSettings.SaveSettingAsync(setting);
await AppSettings.SaveSettingAsync(setting);
}
return setting;
@@ -112,7 +112,11 @@ public class TrayIconManager : IDisposable
{
/* 菜单关闭 */
};
_contextMenu.LostFocus += (s, e) => _contextMenu.IsOpen = false;
_contextMenu.LostFocus += (s, e) =>
{
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
// 展示主窗口(可勾选)
var showMainWindowItem = new MenuItem
@@ -124,7 +128,8 @@ public class TrayIconManager : IDisposable
showMainWindowItem.Click += (s, e) =>
{
ToggleMainWindow();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
_contextMenu.Items.Add(showMainWindowItem);
@@ -140,7 +145,8 @@ public class TrayIconManager : IDisposable
resetPositionItem.Click += (s, e) =>
{
ResetNetViewPosition();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
_contextMenu.Items.Add(resetPositionItem);
@@ -162,7 +168,8 @@ public class TrayIconManager : IDisposable
detailedInfoItem.Click += (s, e) =>
{
ToggleDetailedInfo();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
_contextMenu.Items.Add(detailedInfoItem);
@@ -178,7 +185,8 @@ public class TrayIconManager : IDisposable
trafficAnalysisItem.Click += (s, e) =>
{
OpenTrafficAnalysis();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
_contextMenu.Items.Add(trafficAnalysisItem);
@@ -191,7 +199,8 @@ public class TrayIconManager : IDisposable
settingsItem.Click += (s, e) =>
{
OpenSettings();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
_contextMenu.IsOpen = false;
};
_contextMenu.Items.Add(settingsItem);
@@ -211,7 +220,10 @@ public class TrayIconManager : IDisposable
exitItem.Click += (s, e) =>
{
ExitApplication();
_contextMenu.IsOpen = false;
if (_contextMenu != null)
{
_contextMenu.IsOpen = false;
}
};
_contextMenu.Items.Add(exitItem);
}
@@ -299,8 +311,9 @@ public class TrayIconManager : IDisposable
// 检查ESC键是否被按下
if (System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.Escape))
{
_contextMenu.IsOpen = false;
_menuCloseTimer.Stop();
if (_contextMenu != null)
_contextMenu.IsOpen = false;
_menuCloseTimer?.Stop();
return;
}
@@ -311,8 +324,9 @@ public class TrayIconManager : IDisposable
// 如果鼠标不在菜单区域内且有按键按下,则关闭菜单
if (!IsMouseOverContextMenu())
{
_contextMenu.IsOpen = false;
_menuCloseTimer.Stop();
if (_contextMenu != null)
_contextMenu.IsOpen = false;
_menuCloseTimer?.Stop();
}
}
};
@@ -324,7 +338,7 @@ public class TrayIconManager : IDisposable
{
try
{
if (!_contextMenu.IsOpen) return false;
if (_contextMenu == null || !_contextMenu.IsOpen) return false;
// 获取屏幕上的鼠标位置
GetCursorPos(out POINT screenPoint);
@@ -356,12 +370,12 @@ public class TrayIconManager : IDisposable
private void UpdateContextMenuStates()
{
if (_mainWindow is Shell.Views.NetView netView)
if (_mainWindow != null && _mainWindow is Shell.Views.NetView netView)
{
// 获取各个菜单项并更新状态
foreach (var item in _contextMenu.Items.OfType<MenuItem>())
{
switch (item.Header.ToString())
switch (item.Header?.ToString())
{
case "展示主窗口":
item.IsChecked = netView.IsVisible;
@@ -420,7 +434,7 @@ public class TrayIconManager : IDisposable
private void ResetNetViewPosition()
{
if (_mainWindow is Shell.Views.NetView netView)
if (_mainWindow != null && _mainWindow is Shell.Views.NetView netView)
{
var workArea = SystemParameters.WorkArea;
netView.Left = workArea.Right - netView.Width - 20;
@@ -430,7 +444,7 @@ public class TrayIconManager : IDisposable
private void ToggleDetailedInfo()
{
if (_mainWindow is Shell.Views.NetView netView)
if (_mainWindow != null && _mainWindow is Shell.Views.NetView netView)
{
var contextMenu = netView.ContextMenu;
if (contextMenu?.Items != null)
@@ -502,14 +516,39 @@ public class TrayIconManager : IDisposable
{
netView.ForceClose();
}
else if (_mainWindow != null)
{
// 如果不是NetView类型,直接关闭应用程序
System.Windows.Application.Current?.Shutdown();
}
else
{
// _mainWindow为null时,直接关闭应用程序
System.Windows.Application.Current?.Shutdown();
}
}
}
public void Dispose()
{
_notifyIcon?.Dispose();
_contextMenu = null;
_clickTimer?.Stop();
_menuCloseTimer?.Stop();
try
{
_clickTimer?.Stop();
_menuCloseTimer?.Stop();
if (_contextMenu != null)
{
_contextMenu.IsOpen = false;
_contextMenu = null;
}
_notifyIcon?.Dispose();
_mainWindow = null;
}
catch (Exception ex)
{
// 记录异常但不抛出,避免在Dispose中出现异常
System.Diagnostics.Debug.WriteLine($"Error in TrayIconManager.Dispose: {ex.Message}");
}
}
}
+122 -40
View File
@@ -6,16 +6,15 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using Shell.Utils;
using Data;
using Data.Models;
namespace Shell.Views;
public partial class NetView : Window
{
private TrayIconManager _trayIconManager;
private bool _isClickThrough = false;
private bool _isPositionLocked = false;
private bool _snapToScreen = false;
private bool _showDetailedInfo = false;
private AppSetting _appSettings;
// Double-click detection
private DispatcherTimer _doubleClickTimer;
@@ -50,22 +49,22 @@ public partial class NetView : Window
// Initialize double-click timer
InitializeDoubleClickTimer();
// Initialize settings - will be loaded asynchronously in OnLoaded
_appSettings = new AppSetting();
}
private void OnLoaded(object sender, RoutedEventArgs e)
private async void OnLoaded(object sender, RoutedEventArgs e)
{
// 重置位置
ResetToDefaultPosition();
// 从数据库加载设置
await LoadSettingsFromDatabase();
// 应用设置到窗口
ApplySettingsToWindow();
}
private void NetView_Loaded(object sender, RoutedEventArgs e)
{
// Initialize default position if needed
if (this.Left == 0 && this.Top == 0)
{
ResetToDefaultPosition();
}
// 设置窗口为工具窗口,隐藏在Alt+Tab中
HideFromAltTab();
@@ -76,6 +75,74 @@ public partial class NetView : Window
PopulateNetworkMenu();
}
private async Task LoadSettingsFromDatabase()
{
try
{
_appSettings = await DatabaseManager.GetUserSettingsAsync();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error loading settings from database: {ex.Message}");
// 使用默认设置
_appSettings = new AppSetting();
}
}
private void ApplySettingsToWindow()
{
// 应用窗口位置
if (_appSettings.WindowX != 0 || _appSettings.WindowY != 0)
{
this.Left = _appSettings.WindowX;
this.Top = _appSettings.WindowY;
}
else
{
ResetToDefaultPosition();
}
// 应用其他窗口设置
this.Topmost = _appSettings.IsTopmost;
SetClickThrough(_appSettings.IsClickThrough);
// 更新菜单项的选中状态
if (LockPositionMenuItem != null)
LockPositionMenuItem.IsChecked = _appSettings.IsPositionLocked;
if (ClickThroughMenuItem != null)
ClickThroughMenuItem.IsChecked = _appSettings.IsClickThrough;
if (TopmostMenuItem != null)
TopmostMenuItem.IsChecked = _appSettings.IsTopmost;
if (SnapToScreenMenuItem != null)
SnapToScreenMenuItem.IsChecked = _appSettings.SnapToScreen;
if (DetailedInfoMenuItem != null)
DetailedInfoMenuItem.IsChecked = _appSettings.ShowDetailedInfo;
}
private async Task SaveSettingsToDatabase()
{
try
{
// 更新窗口位置
_appSettings.WindowX = (int)this.Left;
_appSettings.WindowY = (int)this.Top;
// 保存到数据库
if (_appSettings.Id == 0)
{
await DatabaseManager.Instance.AppSettings.SaveSettingAsync(_appSettings);
}
else
{
await DatabaseManager.Instance.AppSettings.UpdateSettingAsync(_appSettings);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error saving settings to database: {ex.Message}");
}
}
private void PopulateNetworkMenu()
{
if (NetworkSelectionMenuItem != null)
@@ -213,16 +280,15 @@ public partial class NetView : Window
{
try
{
var settings = Shell.Models.NetViewSettings.Instance;
switch (settings.DoubleClickAction)
switch (_appSettings.DoubleClickAction)
{
case Shell.Models.DoubleClickAction.TrafficAnalysis:
case 1: // TrafficAnalysis
TrafficAnalysisWindow.ShowWindow();
break;
case Shell.Models.DoubleClickAction.Settings:
case 2: // Settings
SettingsWindow.ShowWindow();
break;
case Shell.Models.DoubleClickAction.None:
case 0: // None
default:
// 无动作
break;
@@ -296,20 +362,23 @@ public partial class NetView : Window
this.Top = workArea.Top + 20;
}
private void LockPosition_Click(object sender, RoutedEventArgs e)
private async void LockPosition_Click(object sender, RoutedEventArgs e)
{
_isPositionLocked = !_isPositionLocked;
LockPositionMenuItem.IsChecked = _isPositionLocked;
_appSettings.IsPositionLocked = !_appSettings.IsPositionLocked;
LockPositionMenuItem.IsChecked = _appSettings.IsPositionLocked;
// Don't change ResizeMode to maintain consistent window behavior
// The dragging logic will check _isPositionLocked instead
// 保存设置到数据库
await SaveSettingsToDatabase();
}
private void ToggleClickThrough_Click(object sender, RoutedEventArgs e)
private async void ToggleClickThrough_Click(object sender, RoutedEventArgs e)
{
_isClickThrough = !_isClickThrough;
ClickThroughMenuItem.IsChecked = _isClickThrough;
SetClickThrough(_isClickThrough);
_appSettings.IsClickThrough = !_appSettings.IsClickThrough;
ClickThroughMenuItem.IsChecked = _appSettings.IsClickThrough;
SetClickThrough(_appSettings.IsClickThrough);
// 保存设置到数据库
await SaveSettingsToDatabase();
}
private void SetClickThrough(bool enabled)
@@ -329,21 +398,28 @@ public partial class NetView : Window
}
}
private void ToggleTopmost_Click(object sender, RoutedEventArgs e)
private async void ToggleTopmost_Click(object sender, RoutedEventArgs e)
{
this.Topmost = !this.Topmost;
TopmostMenuItem.IsChecked = this.Topmost;
_appSettings.IsTopmost = !_appSettings.IsTopmost;
this.Topmost = _appSettings.IsTopmost;
TopmostMenuItem.IsChecked = _appSettings.IsTopmost;
// 保存设置到数据库
await SaveSettingsToDatabase();
}
private void ToggleSnapToScreen_Click(object sender, RoutedEventArgs e)
private async void ToggleSnapToScreen_Click(object sender, RoutedEventArgs e)
{
_snapToScreen = !_snapToScreen;
SnapToScreenMenuItem.IsChecked = _snapToScreen;
_appSettings.SnapToScreen = !_appSettings.SnapToScreen;
SnapToScreenMenuItem.IsChecked = _appSettings.SnapToScreen;
if (_snapToScreen)
if (_appSettings.SnapToScreen)
{
SnapToScreen();
}
// 保存设置到数据库
await SaveSettingsToDatabase();
}
private void SnapToScreen()
@@ -356,10 +432,10 @@ public partial class NetView : Window
this.Top = top;
}
private void ToggleDetailedInfo_Click(object sender, RoutedEventArgs e)
private async void ToggleDetailedInfo_Click(object sender, RoutedEventArgs e)
{
_showDetailedInfo = !_showDetailedInfo;
DetailedInfoMenuItem.IsChecked = _showDetailedInfo;
_appSettings.ShowDetailedInfo = !_appSettings.ShowDetailedInfo;
DetailedInfoMenuItem.IsChecked = _appSettings.ShowDetailedInfo;
// The window will automatically resize based on content
// No need to manually set height anymore due to SizeToContent="WidthAndHeight"
@@ -367,6 +443,9 @@ public partial class NetView : Window
// You can trigger a re-layout if needed
this.InvalidateMeasure();
this.UpdateLayout();
// 保存设置到数据库
await SaveSettingsToDatabase();
}
private void HideToTray_Click(object sender, RoutedEventArgs e)
@@ -431,7 +510,7 @@ public partial class NetView : Window
}
// Only allow dragging if position is not locked and click-through is disabled
if (!_isPositionLocked && !_isClickThrough)
if (!_appSettings.IsPositionLocked && !_appSettings.IsClickThrough)
{
try
{
@@ -450,16 +529,19 @@ public partial class NetView : Window
base.OnLocationChanged(e);
// Only check snap to screen if that option is enabled
if (_snapToScreen && !_isPositionLocked)
if (_appSettings.SnapToScreen && !_appSettings.IsPositionLocked)
{
SnapToScreen();
}
// Perform boundary check after drag is complete (with delay to avoid flicker)
if (!_isPositionLocked)
if (!_appSettings.IsPositionLocked)
{
CheckBoundariesDelayed();
}
// 自动保存位置变化
_ = Task.Run(async () => await SaveSettingsToDatabase());
}
private System.Windows.Threading.DispatcherTimer? _boundaryCheckTimer;