🪓 一句话结论(先给你最残酷的)
WinForm = 过时但简单粗暴的快刀。适合老项目、快速工具、成本最低。
WPF = 正式企业级 UI 的底层技术核心。更现代、扩展强,学习成本高。
如果你还想往中长期延展、想跟上 .NET 的生态趋势:
👉 WPF 是唯一值得选的。
WinForm ≈ 技术债,只能在不得不用时继续用。
🔥 核心对比:你真正关心的就 5 件事
1️⃣ 开发效率:初期 WinForm 快,长期 WPF 反超
| 项目 | WinForm | WPF |
|---|---|---|
| 上手速度 | 极快(拖控件) | 明显慢(XAML 思维转变) |
| 界面复杂度 | 越复杂越崩 | 越复杂越能展示优势 |
| 可维护性 | 复杂 UI = 地狱 | MVVM + Binding,长期稳 |
**结论:**你做内部小工具 → WinForm
你做企业级产品、长期维护 → WPF
2️⃣ UI 能力:WPF 完胜
| 能力 | WinForm | WPF |
|---|---|---|
| 自定义 UI | 几乎做不了 | 天花板级,可无限自定义 |
| 动画 | 基本不存在 | 内置动画系统 |
| DPI 适配 | 很差 | 高 DPI 友好 |
| 响应式布局 | 没戏 | XAML 天然支持 |
如果你想做现代 UI:
👉 WinForm 基本不具备任何竞争力。
3️⃣ 架构能力:WPF 是现代桌面架构的唯一选择
WinForm:
- 全是事件回调
- UI 与逻辑耦死
- 测试困难
- 重构困难
- 多线程 UI 调用坑死你
WPF:
- MVVM
- 数据绑定
- 命令系统
- 样式、模板分离
- UI 与逻辑天然解耦
- 测试与自动化非常友好
如果你的系统涉及"流程复杂 + 状态多",WinForm 的事件流会直接把你拖进泥潭。
4️⃣ 性能:不是你想的那样
WinForm
- 轻量、快
- 但控件绘制能力极其有限
- 碰到大量数据 UI 会卡死
WPF
- GPU 加速(DirectX)
- 能处理大量控件、动态 UI、不规则 UI
- 性能天花板远高于 WinForm
如果你的 UI 有大量动态更新、动画、图形渲染:
👉 WinForm 做不到,WPF 是唯一路线。
5️⃣ 生态未来:WinForm 正在退出历史舞台
- 微软不再主推 WinForm
- 新控件、新组件、新框架都以 WPF 为主
- WinUI / MAUI 技术栈对 WPF 更友好
- 跨平台方向 WinForm 完全没戏
如果你的项目生命周期≥3年:
👉 WPF 是更稳的资产。
⚡ 总结成一句你能立即做决策的表格
| 场景 | 该选谁 |
|---|---|
| 做一个内部小工具、脚本壳 | WinForm |
| 需要快速做一个 CRUD 工具 | WinForm |
| 企业级正式客户端 | WPF |
| 复杂 UI(图表、动画、Dashboard) | WPF |
| 希望保持长期技术可维护性 | WPF |
| 想要现代 UI 和用户体验 | WPF |
| 想要跨平台未来(MAUI/WinUI 迁移) | WPF |
🎯 你真实应该问自己的关键问题是:
**你的软件生命周期有多长?
你的 UI 会不会变复杂?
你未来想不想减少技术债?**
如果你无法肯定 2 年后仍维护这个软件,那 WinForm 就还能用。
如果你想避免未来重写,那选 WinForm = 埋地雷。
1. 布局技巧(Grid / StackPanel / DockPanel / UniformGrid)
Grid:最常用、最灵活
xml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="菜单" Grid.Row="0" Grid.Column="0" />
<ListBox Grid.Row="1" Grid.Column="0">
<ListBoxItem Content="项目 1"/>
<ListBoxItem Content="项目 2"/>
</ListBox>
<Border Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Background="#eee">
<TextBlock Text="内容区域" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</Grid>
要点:
Auto= 根据内容自动高度*= 自适应填满RowSpan/ColumnSpan很常用
StackPanel:线性布局(简单列表)
xml
<StackPanel Orientation="Vertical" Margin="10">
<Button Content="按钮 A"/>
<Button Content="按钮 B"/>
</StackPanel>
适合小组件,不适合大界面(因为不能自动换行)。
DockPanel:左/右/上/下 贴边布局
xml
<DockPanel>
<StackPanel DockPanel.Dock="Left" Width="150" Background="#ddd">
<TextBlock Text="侧边栏"/>
</StackPanel>
<TextBlock Text="主内容"/>
</DockPanel>
非常适合做企业后台系统的框架布局。
UniformGrid:所有元素平均分布
xml
<UniformGrid Rows="2" Columns="3">
<Button Content="1"/>
<Button Content="2"/>
<Button Content="3"/>
<Button Content="4"/>
<Button Content="5"/>
</UniformGrid>
2. 数据绑定(Binding)核心技巧
绑定一个对象
xml
<TextBlock Text="{Binding UserName}"/>
如果 DataContext 是
csharp
public class ViewModel : INotifyPropertyChanged
{
public string UserName { get; set; } = "Json Mike";
}
TwoWay 双向绑定
xml
<TextBox Text="{Binding UserInput, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
没有写 INotifyPropertyChanged?那就别怪更新不了。
标准写法:
csharp
private string _userInput;
public string UserInput
{
get => _userInput;
set { _userInput = value; OnPropertyChanged(); }
}
绑定集合 + 列表 UI(核心!)
XAML
xml
<ListView ItemsSource="{Binding Users}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Width="100"/>
<TextBlock Text="{Binding Age}" Width="50"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
csharp
public ObservableCollection<User> Users { get; set; }
= new ObservableCollection<User>
{
new User { Name = "Alice", Age = 20 },
new User { Name = "Bob", Age = 25 }
};
⚠️ 必须用 ObservableCollection,不然界面不会自动更新。
3. 命令绑定(避免按钮写 Click 事件)
XAML:
xml
<Button Content="加载数据" Command="{Binding LoadCommand}" />
ViewModel:
csharp
public ICommand LoadCommand { get; }
public ViewModel()
{
LoadCommand = new RelayCommand(LoadData);
}
void LoadData()
{
// 执行逻辑
}
RelayCommand:
csharp
public class RelayCommand : ICommand
{
private readonly Action _execute;
public RelayCommand(Action execute) => _execute = execute;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execute();
}
4. 绑定转换(IValueConverter)
XAML:
xml
<TextBlock Text="{Binding Score, Converter={StaticResource ScoreConverter}}"/>
Converter:
csharp
public class ScoreConverter : IValueConverter
{
public object Convert(object value, Type t, object p, CultureInfo c)
=> (int)value >= 60 ? "及格" : "不及格";
public object ConvertBack(object value, Type t, object p, CultureInfo c)
=> throw new NotImplementedException();
}
5. 样式统一管理(企业级项目必做)
xml
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="5"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Background" Value="#3399ff"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</Window.Resources>
全局样式请写在 App.xaml。