WPF application development for managing Windows context menu items with SOLID principles and MVVM architecture
Expert guidance for building a Windows WPF application that manages context menu items, following SOLID principles and MVVM architecture.
Provides comprehensive development instructions for creating a Windows Context Menu Editor - a WPF application that allows users to view, enable/disable, and delete Windows context menu items through registry manipulation. The skill emphasizes clean architecture, simplicity, and maintainable code.
For every class and method you write:
Before writing any code:
Organize code into clear layers:
```
ContextMenuEditor/
├── Models/ # Data models (ContextMenuItem, RegistryEntry)
├── ViewModels/ # MVVM ViewModels with INotifyPropertyChanged
├── Views/ # WPF Windows and UserControls
├── Services/ # Business logic (RegistryService, BackupService)
├── Utilities/ # Helpers and extensions
└── Resources/ # Icons, styles, themes
```
**General C# Guidelines:**
**Modern C# Features:**
```csharp
// Use pattern matching
if (item is ContextMenuItem { IsEnabled: true } menuItem)
{
// ...
}
// Use null-coalescing
var value = registry.GetValue() ?? defaultValue;
// Use string interpolation
var message = $"Deleted {count} items";
// Prefer readonly
private readonly IRegistryService _registryService;
// Use nullable reference types
public string? OptionalProperty { get; set; }
```
**ViewModels:**
**Views:**
**Example ViewModel Structure:**
```csharp
public class MainViewModel : INotifyPropertyChanged
{
private readonly IRegistryService _registryService;
private ObservableCollection<ContextMenuItem> _items;
public ICommand DeleteCommand { get; }
public ICommand ExportCommand { get; }
public MainViewModel(IRegistryService registryService)
{
_registryService = registryService;
DeleteCommand = new RelayCommand(ExecuteDelete, CanDelete);
}
}
```
**Always:**
**Example Safe Registry Access:**
```csharp
public async Task<bool> DeleteContextMenuItemAsync(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException("Path cannot be empty", nameof(path));
// Create backup first
await _backupService.CreateBackupAsync();
try
{
using var key = Registry.ClassesRoot.OpenSubKey(path, writable: true);
if (key != null)
{
key.DeleteSubKeyTree(path);
return true;
}
}
catch (UnauthorizedAccessException)
{
// Handle UAC elevation needed
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to delete registry key");
throw;
}
return false;
}
```
**Display Context Menus:**
**Enable/Disable Items:**
**Delete Items:**
**Export List:**
**Never:**
**Always:**
**Example:**
```csharp
try
{
await DeleteItemsAsync(selectedItems);
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Administrator privileges required.", "Permission Denied");
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during deletion");
MessageBox.Show("An error occurred. Please try again.", "Error");
}
```
**Critical:**
**Path Validation Example:**
```csharp
private bool IsValidRegistryPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return false;
// Prevent path traversal and injection
var invalidChars = new[] { '/', '\\', '<', '>', '|', '"', '?' };
return !path.Any(c => invalidChars.Contains(c));
}
```
**Write testable code:**
**Mock registry operations:**
```csharp
public interface IRegistryService
{
Task<IEnumerable<ContextMenuItem>> GetContextMenuItemsAsync();
Task<bool> DeleteItemAsync(string path);
Task<bool> ToggleItemAsync(string path, bool enabled);
}
```
**Test edge cases:**
```csharp
public class ContextMenuViewModel : ViewModelBase
{
private readonly IRegistryService _registry;
private readonly IBackupService _backup;
private ObservableCollection<ContextMenuItem> _items;
private ContextMenuItem _selectedItem;
public ObservableCollection<ContextMenuItem> Items
{
get => _items;
set => SetProperty(ref _items, value);
}
public ICommand DeleteCommand { get; }
public ICommand ToggleCommand { get; }
public ICommand RefreshCommand { get; }
public ContextMenuViewModel(IRegistryService registry, IBackupService backup)
{
_registry = registry;
_backup = backup;
DeleteCommand = new AsyncRelayCommand(DeleteSelectedAsync, CanDelete);
RefreshCommand = new AsyncRelayCommand(LoadItemsAsync);
}
private async Task DeleteSelectedAsync()
{
if (_selectedItem == null) return;
var result = MessageBox.Show(
$"Delete '{_selectedItem.Name}'?",
"Confirm Delete",
MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes) return;
await _backup.CreateBackupAsync();
await _registry.DeleteItemAsync(_selectedItem.Path);
await LoadItemsAsync();
}
private bool CanDelete() => _selectedItem != null;
}
```
```csharp
public class RegistryService : IRegistryService
{
private readonly ILogger<RegistryService> _logger;
public async Task<IEnumerable<ContextMenuItem>> GetContextMenuItemsAsync()
{
return await Task.Run(() =>
{
var items = new List<ContextMenuItem>();
try
{
using var key = Registry.ClassesRoot.OpenSubKey(@"*\shell");
if (key == null) return items;
foreach (var subKeyName in key.GetSubKeyNames())
{
using var subKey = key.OpenSubKey(subKeyName);
items.Add(CreateMenuItem(subKey));
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load context menu items");
}
return items;
});
}
private ContextMenuItem CreateMenuItem(RegistryKey key)
{
return new ContextMenuItem
{
Name = key.GetValue("") as string ?? key.Name,
Path = key.Name,
IsEnabled = key.GetValue("Disabled") == null,
Command = GetCommandFromKey(key)
};
}
}
```
1. **Admin Privileges**: Many registry operations require elevation - implement UAC prompts
2. **Backup First**: Always create registry backup before any destructive operation
3. **Read-Only Detection**: Some system context menus cannot be modified - detect and disable actions
4. **Windows Compatibility**: Test on Windows 10 and 11 - registry paths may differ
5. **PRD Reference**: Always check the PRD.md file (if present) for specific product requirements
6. **V1.0 Focus**: Implement core features first, avoid scope creep
When PRD.md is available in the project, refer to it for:
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/windows-context-menu-editor-development/raw