
在WPF中使用MVVM模式开发时,合理管理文件和代码结构非常重要。以下是一个常见的文件组织和代码安排建议:
1. 项目结构
假设项目包括主窗口、菜单、工具栏、状态栏和3个嵌入式窗口,建议按功能或模块组织项目结构,例如:
MyWpfApp/
├── Models/
│ ├── SomeModel.cs
├── ViewModels/
│ ├── MainWindowViewModel.cs
│ ├── MenuViewModel.cs
│ ├── ToolbarViewModel.cs
│ ├── StatusBarViewModel.cs
│ ├── EmbeddedWindow1ViewModel.cs
│ ├── EmbeddedWindow2ViewModel.cs
│ ├── EmbeddedWindow3ViewModel.cs
├── Views/
│ ├── MainWindow.xaml
│ ├── MenuView.xaml
│ ├── ToolbarView.xaml
│ ├── StatusBarView.xaml
│ ├── EmbeddedWindow1View.xaml
│ ├── EmbeddedWindow2View.xaml
│ ├── EmbeddedWindow3View.xaml
├── Services/
│ ├── SomeService.cs
├── Resources/
│ ├── Styles.xaml
│ ├── Icons/
├── App.xaml
├── App.xaml.cs
2. View和ViewModel的绑定
通常通过DataContext将View与ViewModel绑定,可以在XAML或代码中完成。
XAML绑定
代码绑定
using System.Windows;
using MyWpfApp.ViewModels;
namespace MyWpfApp.Views
{
public partial class MainWindow : Window
{
// 通过构造函数注入 MainWindowViewModel。
public MainWindow(MainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel; // 设置 DataContext
}
}
}
3. 主窗口与嵌入式窗口的协调
主窗口的ViewModel可以管理其他ViewModel,并通过命令或事件协调它们。
public class MainWindowViewModel : INotifyPropertyChanged
{
public MenuViewModel MenuViewModel { get; set; }
public ToolbarViewModel ToolbarViewModel { get; set; }
public StatusBarViewModel StatusBarViewModel { get; set; }
public EmbeddedWindow1ViewModel EmbeddedWindow1ViewModel { get; set; }
public EmbeddedWindow2ViewModel EmbeddedWindow2ViewModel { get; set; }
public EmbeddedWindow3ViewModel EmbeddedWindow3ViewModel { get; set; }
public MainWindowViewModel()
{
MenuViewModel = new MenuViewModel();
ToolbarViewModel = new ToolbarViewModel();
StatusBarViewModel = new StatusBarViewModel();
EmbeddedWindow1ViewModel = new EmbeddedWindow1ViewModel();
EmbeddedWindow2ViewModel = new EmbeddedWindow2ViewModel();
EmbeddedWindow3ViewModel = new EmbeddedWindow3ViewModel();
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
4. 依赖注入
使用依赖注入(如
Microsoft.Extensions.DependencyInjection)可以更好地管理ViewModel和服务。在 App.xaml.cs 中配置依赖注入容器,并注册你的 ViewModel 和服务。
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Windows;
using MyWpfApp.ViewModels;
using MyWpfApp.Services;
namespace MyWpfApp
{
public partial class App : Application
{
private IServiceProvider _serviceProvider;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 配置服务
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
// 构建 ServiceProvider
_serviceProvider = serviceCollection.BuildServiceProvider();
// 显示主窗口
var mainWindow = _serviceProvider.GetRequiredService();
mainWindow.Show();
}
private void ConfigureServices(IServiceCollection services)
{
// 注册 ViewModel
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
// 注册服务
services.AddSingleton();
// 注册 View
services.AddTransient();
}
}
}
在需要依赖服务的ViewModel中,通过构造函数注入。
示例:MenuViewModel 使用 IDataService
using MyWpfApp.Services;
namespace MyWpfApp.ViewModels
{
public class MenuViewModel
{
private readonly IDataService _dataService;
public MenuViewModel(IDataService dataService)
{
_dataService = dataService;
}
public void SomeMethod()
{
var data = _dataService.GetData();
// 执行逻辑
}
}
}
5. 资源管理
将样式、模板等放在Resources/Styles.xaml中,并在App.xaml中引用。
6. 命令和事件
使用ICommand实现命令模式,处理用户交互。
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func _canExecute;
public RelayCommand(Action execute, Func canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
public void Execute(object parameter) => _execute();
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
7. 测试
为ViewModel编写单元测试,确保逻辑正确。
[TestClass]
public class MainWindowViewModelTests
{
[TestMethod]
public void TestSomeMethod()
{
var vm = new MainWindowViewModel();
vm.SomeMethod();
Assert.IsTrue(vm.SomeProperty);
}
}
总结
通过合理的文件组织、依赖注入、命令模式等,可以更好地管理WPF MVVM项目,确保代码的可维护性和可扩展性。