编辑
2025-02-03
WinForm到WPF转型
00
请注意,本文编写于 92 天前,最后修改于 92 天前,其中某些信息可能已经过时。

目录

项目结构
1. 核心文件
2. 资源文件
MVVM设计模式
Helper/RelayCommand.cs
Models/TaskModel.cs
ViewModels
BaseViewModel.cs
MainViewModel.cs
MainWindow.xaml
结论

Windows Presentation Foundation (WPF) 是一个用于构建 Windows 应用程序的强大框架。它支持丰富的用户界面和数据绑定,采用了 MVVM(Model-View-ViewModel)设计模式,使得应用程序的结构更加清晰和易于维护。本文将详细解析一个标准 WPF 项目的基础结构,并提供完整的示例代码。

项目结构

一个典型的 WPF 项目结构如下所示:

C#
MyWpfApp/ ├── Properties/ │ └── AssemblyInfo.cs # 程序集信息 ├── References/ # 项目引用 ├── App.xaml # 应用程序入口和全局资源 ├── App.xaml.cs # 应用程序代码隐藏 ├── MainWindow.xaml # 主窗口XAML └── MainWindow.xaml.cs # 主窗口代码隐藏 ├── Assets/ # 资源文件 │ ├── Images/ # 图片资源 │ ├── Icons/ # 图标资源 │ └── Styles/ # 样式资源 ├── Models/ # 数据模型 ├── ViewModels/ # 视图模型 ├── Views/ # 视图 ├── Services/ # 服务层 ├── Helpers/ # 辅助类 ├── Controls/ # 自定义控件 └── Converters/ # 值转换器

1. 核心文件

  • App.xaml: 应用程序的入口点,定义全局资源和启动窗口。
  • App.xaml.cs: 应用程序的代码隐藏文件,包含应用程序的启动逻辑。
  • MainWindow.xaml: 主窗口的 XAML 文件,定义用户界面的布局。
  • MainWindow.xaml.cs: 主窗口的代码隐藏文件,处理窗口的逻辑。

2. 资源文件

  • Assets/: 存放应用程序所需的资源,如图片、图标和样式。
  • Models/: 定义数据模型,通常实现 INotifyPropertyChanged 接口以支持数据绑定。
  • ViewModels/: 视图模型,包含应用程序的业务逻辑和状态。
  • Views/: 视图,定义用户界面的具体实现。
  • Services/: 提供应用程序的服务,如导航和数据访问。
  • Helpers/: 辅助类,提供通用功能。
  • Controls/: 自定义控件,扩展 WPF 控件的功能。
  • Converters/: 值转换器,用于在绑定时转换数据类型。

MVVM设计模式

WPF 应用程序通常采用 MVVM 设计模式。以下是 MVVM 的基本组成部分:

  • Model: 表示应用程序的数据和业务逻辑。
  • View: 用户界面的定义,通常是 XAML 文件。
  • ViewModel: 连接 Model 和 View 的桥梁,处理用户输入并更新 Model。

Helper/RelayCommand.cs

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace AppWpf003.Helpers { public class RelayCommand<T> : ICommand { private readonly Action<T> _execute; private readonly Predicate<T> _canExecute; public RelayCommand(Action<T> execute, Predicate<T> canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public bool CanExecute(object parameter) => _canExecute == null || _canExecute((T)parameter); public void Execute(object parameter) => _execute((T)parameter); public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } } }

Models/TaskModel.cs

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppWpf003.Models { public class TaskModel { public string TaskName { get; set; } } }

ViewModels

BaseViewModel.cs

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppWpf003.ViewModels { public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }

MainViewModel.cs

C#
using AppWpf003.Helpers; using AppWpf003.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace AppWpf003.ViewModels { public class MainViewModel : BaseViewModel { public ObservableCollection<TaskModel> Tasks { get; set; } public ICommand AddTaskCommand { get; set; } public ICommand DeleteTaskCommand { get; set; } private string _newTaskName; public string NewTaskName { get { return _newTaskName; } set { _newTaskName = value; OnPropertyChanged(nameof(NewTaskName)); } } public MainViewModel() { Tasks = new ObservableCollection<TaskModel>(); AddTaskCommand = new RelayCommand<object>(AddTask); DeleteTaskCommand = new RelayCommand<TaskModel>(DeleteTask); } private void AddTask(object parameter) { if (!string.IsNullOrEmpty(NewTaskName)) { Tasks.Add(new TaskModel { TaskName = NewTaskName }); NewTaskName = string.Empty; // Clear input } } private void DeleteTask(TaskModel task) { if (Tasks.Contains(task)) { Tasks.Remove(task); } } } }

MainWindow.xaml

C#
<Window x:Class="AppWpf003.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:AppWpf003.ViewModels" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <!-- 引入 Styles --> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Assets/Styles/Colors.xaml"/> <ResourceDictionary Source="Assets/Styles/TextStyles.xaml"/> <ResourceDictionary Source="Assets/Styles/Buttons.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> <Window.DataContext> <local:MainViewModel/> </Window.DataContext> <Grid> <StackPanel Margin="10"> <TextBox Width="300" Margin="0,0,0,10" Text="{Binding NewTaskName, UpdateSourceTrigger=PropertyChanged}"/> <Button Content="Add Task" Command="{Binding AddTaskCommand}" Width="100" Margin="0,0,0,10" Style="{StaticResource DefaultButtonStyle}"/> <ListBox x:Name="TaskList" ItemsSource="{Binding Tasks}" SelectedItem="{Binding SelectedTask}" DisplayMemberPath="TaskName" Margin="0,0,0,10"/> <Button Content="Delete Task" Command="{Binding DeleteTaskCommand}" CommandParameter="{Binding SelectedItem, ElementName=TaskList}" Width="100"/> </StackPanel> </Grid> </Window>

image.png

结论

通过遵循上述结构和最佳实践,可以创建一个易于维护、扩展和测试的 WPF 应用程序。MVVM 模式的使用使得代码的组织更加清晰,便于团队协作和后期维护。希望本文能为您在 WPF 开发中提供有价值的参考。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!