添加项目文件。

This commit is contained in:
ShaoHua
2025-12-30 10:29:28 +08:00
parent 2989e660b3
commit 57cfd16e7b
21 changed files with 2348 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using TodoList.Models;
namespace TodoList.Services
{
public class FileDataService : IDataService
{
private readonly string _filePath;
public FileDataService()
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var folder = Path.Combine(appData, "TodoListApp");
Directory.CreateDirectory(folder);
_filePath = Path.Combine(folder, "tasks.json");
}
public async Task<List<TodoItem>> LoadTasksAsync()
{
if (!File.Exists(_filePath))
{
return new List<TodoItem>();
}
try
{
using var stream = File.OpenRead(_filePath);
var items = await JsonSerializer.DeserializeAsync<List<TodoItem>>(stream);
return items ?? new List<TodoItem>();
}
catch
{
return new List<TodoItem>();
}
}
public async Task SaveTaskAsync(TodoItem task)
{
var tasks = await LoadTasksAsync();
var existing = tasks.Find(t => t.Id == task.Id);
if (existing != null)
{
tasks.Remove(existing);
}
tasks.Add(task);
await SaveAllAsync(tasks);
}
public async Task SaveAllAsync(List<TodoItem> tasks)
{
using var stream = File.Create(_filePath);
await JsonSerializer.SerializeAsync(stream, tasks, new JsonSerializerOptions { WriteIndented = true });
}
public async Task DeleteTaskAsync(string id)
{
var tasks = await LoadTasksAsync();
var existing = tasks.Find(t => t.Id == id);
if (existing != null)
{
tasks.Remove(existing);
await SaveAllAsync(tasks);
}
}
}
}
+127
View File
@@ -0,0 +1,127 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.Windows.Interop;
namespace TodoList.Services
{
public class GlobalShortcutService : IDisposable
{
private const int HOTKEY_ID = 9000;
public const uint MOD_ALT = 0x0001;
public const uint MOD_CONTROL = 0x0002;
public const uint MOD_SHIFT = 0x0004;
public const uint MOD_WIN = 0x0008;
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private IntPtr _windowHandle;
private HwndSource _source;
private Action _onHotKeyPressed;
private bool _isRegistered;
public void Register(IntPtr windowHandle, Action onHotKeyPressed, uint modifiers, uint key)
{
// If already registered, unregister first (to support updating)
if (_isRegistered)
{
UnregisterHotKey(_windowHandle, HOTKEY_ID);
_source?.RemoveHook(HwndHook);
_isRegistered = false;
}
_windowHandle = windowHandle;
_onHotKeyPressed = onHotKeyPressed;
_source = HwndSource.FromHwnd(_windowHandle);
if (_source == null) return; // Should not happen if handle is valid
_source.AddHook(HwndHook);
if (RegisterHotKey(_windowHandle, HOTKEY_ID, modifiers, key))
{
_isRegistered = true;
}
else
{
System.Diagnostics.Debug.WriteLine("Failed to register hotkey.");
}
}
public void UpdateShortcut(uint modifiers, uint key)
{
if (_windowHandle != IntPtr.Zero && _onHotKeyPressed != null)
{
// Re-register with new keys
Register(_windowHandle, _onHotKeyPressed, modifiers, key);
}
}
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
if (msg == WM_HOTKEY)
{
if (wParam.ToInt32() == HOTKEY_ID)
{
_onHotKeyPressed?.Invoke();
handled = true;
}
}
return IntPtr.Zero;
}
public void Unregister()
{
if (_isRegistered)
{
_source?.RemoveHook(HwndHook);
UnregisterHotKey(_windowHandle, HOTKEY_ID);
_isRegistered = false;
}
}
public void Dispose()
{
Unregister();
}
public static uint GetModifier(string modifiers)
{
uint mod = 0;
if (string.IsNullOrEmpty(modifiers)) return mod;
var parts = modifiers.Split(',');
foreach (var part in parts)
{
var p = part.Trim();
if (p.Equals("Control", StringComparison.OrdinalIgnoreCase)) mod |= MOD_CONTROL;
if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase)) mod |= MOD_ALT;
if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase)) mod |= MOD_SHIFT;
if (p.Equals("Windows", StringComparison.OrdinalIgnoreCase)) mod |= MOD_WIN;
}
return mod;
}
public static uint GetKey(string key)
{
if (Enum.TryParse<Key>(key, out var k))
{
return (uint)KeyInterop.VirtualKeyFromKey(k);
}
// Fallback for simple letters if Key enum doesn't match directly (though it should for A-Z)
if (key.Length == 1)
{
char c = char.ToUpper(key[0]);
if (c >= 'A' && c <= 'Z') return (uint)c;
if (c >= '0' && c <= '9') return (uint)c;
}
return 0x41; // Default 'A'
}
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using TodoList.Models;
namespace TodoList.Services
{
public interface IDataService
{
Task<List<TodoItem>> LoadTasksAsync();
Task SaveTaskAsync(TodoItem task);
Task SaveAllAsync(List<TodoItem> tasks);
Task DeleteTaskAsync(string id);
}
}
+56
View File
@@ -0,0 +1,56 @@
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
namespace TodoList.Services
{
public partial class AppSettings : ObservableObject
{
[ObservableProperty]
private string shortcutModifiers = "Control,Alt"; // Comma separated
[ObservableProperty]
private string shortcutKey = "A";
}
public class SettingsService
{
private readonly string _filePath;
public AppSettings Settings { get; private set; }
public SettingsService()
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var folder = Path.Combine(appData, "TodoListApp");
Directory.CreateDirectory(folder);
_filePath = Path.Combine(folder, "settings.json");
Settings = LoadSettings();
}
private AppSettings LoadSettings()
{
if (!File.Exists(_filePath)) return new AppSettings();
try
{
var json = File.ReadAllText(_filePath);
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
}
catch
{
return new AppSettings();
}
}
public void SaveSettings()
{
try
{
var json = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(_filePath, json);
}
catch { }
}
}
}