添加项目文件。
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user