WPF 通用确认对话框组件:支持同步和异步调用
WPF 原生的 MessageBox 长得丑且不可定制样式。本文分享一个自定义确认对话框组件,圆角白色卡片、关闭按钮、确认/取消按钮,还支持同步和异步两种调用方式。
效果预览
- 居中弹出的模态对话框,圆角白色卡片 + 阴影
- 标题 + 内容 + 确认/取消按钮
- 右上角关闭按钮(等同取消)
- 支持自定义按钮文字
- 自动绑定到当前活跃窗口
MConfirmDialog.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <Window x:Class="WpfApp.Component.MConfirmDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="确认" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" AllowsTransparency="True" Background="Transparent" SizeToContent="WidthAndHeight" MinWidth="300" MaxWidth="450">
<Border CornerRadius="8" Background="#F5F5F5" BorderBrush="#CCCCCC" BorderThickness="1" Padding="20"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions>
<Button Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top" Width="24" Height="24" Background="Transparent" BorderBrush="Transparent" Content="✕" FontSize="12" Foreground="#999999" Cursor="Hand" Click="CloseButton_Click"/>
<TextBlock x:Name="TitleText" Grid.Row="0" Text="确认" FontSize="18" FontWeight="Medium" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,30,0"/>
<TextBlock x:Name="ContentText" Grid.Row="1" Text="确定要执行此操作吗?" FontSize="14" Foreground="#666666" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,20"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center"> <Button x:Name="CancelButton" Content="取消" MinWidth="80" Height="32" Background="Transparent" BorderBrush="#CCCCCC" BorderThickness="1" Foreground="#666666" Margin="0,0,10,0" Click="CancelButton_Click"/> <Button x:Name="ConfirmButton" Content="确认" MinWidth="80" Height="32" Background="#2196F3" BorderBrush="Transparent" Foreground="White" Cursor="Hand" Click="ConfirmButton_Click"/> </StackPanel> </Grid> </Border> </Window>
|
MConfirmDialog.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| using System.Windows;
namespace WpfApp.Component { public partial class MConfirmDialog : Window { #region 属性
public new bool DialogResult { get; private set; }
#endregion
#region 构造
public MConfirmDialog() { InitializeComponent(); }
#endregion
#region 内容设置
public void Setup(string title, string content, string confirmText = "确认", string cancelText = "取消") { TitleText.Text = title; ContentText.Text = content; ConfirmButton.Content = confirmText; CancelButton.Content = cancelText; }
#endregion
#region 事件处理
private void CloseButton_Click(object sender, RoutedEventArgs e) { DialogResult = false; Close(); }
private void CancelButton_Click(object sender, RoutedEventArgs e) { DialogResult = false; Close(); }
private void ConfirmButton_Click(object sender, RoutedEventArgs e) { DialogResult = true; Close(); }
#endregion
#region 静态调用
public static Task<bool> ShowAsync(string title, string content, string confirmText = "确认", string cancelText = "取消") { var dialog = new MConfirmDialog(); dialog.Setup(title, content, confirmText, cancelText);
var owner = Application.Current?.Windows.OfType<Window>() .FirstOrDefault(w => w.IsActive); if (owner != null) { dialog.Owner = owner; }
var tcs = new TaskCompletionSource<bool>(); dialog.Closed += (s, e) => tcs.TrySetResult(dialog.DialogResult); dialog.ShowDialog();
return tcs.Task; }
public static bool Show(string title, string content, string confirmText = "确认", string cancelText = "取消") { var dialog = new MConfirmDialog(); dialog.Setup(title, content, confirmText, cancelText);
var owner = Application.Current?.Windows.OfType<Window>() .FirstOrDefault(w => w.IsActive); if (owner != null) { dialog.Owner = owner; }
dialog.ShowDialog(); return dialog.DialogResult; }
#endregion } }
|
使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if (MConfirmDialog.Show("确认删除", "确定要删除此项吗?")) { DeleteItem(); }
var result = await MConfirmDialog.ShowAsync("保存确认", "有未保存的修改,是否保存?"); if (result) { await SaveAsync(); }
MConfirmDialog.Show("提示", "确定要清空购物车吗?", confirmText: "清空", cancelText: "再想想");
|
设计要点
1. new DialogResult 巧妙遮蔽
WPF 的 Window 自带一个 DialogResult 属性(类型是 bool?),用起来不方便。这里用 public new bool DialogResult 遮蔽了父类属性,改成非空 bool,调用方不需要处理 null。
2. 同步 vs 异步双模式
- 同步:
Show() 内部调 ShowDialog() 天然阻塞,返回 bool
- 异步:
ShowAsync() 用 TaskCompletionSource 包装,Closed 事件时 TrySetResult,支持 await
两种方式覆盖所有使用场景。
3. 自动绑定 Owner
通过 Application.Current.Windows 找到当前活跃窗口作为 Owner,确保对话框始终在父窗口之上,且关闭父窗口时子窗口跟着关闭。
4. 三种关闭方式等同取消
点击 ✕ 关闭按钮、点击取消按钮、按 Esc 键(如果绑定了),都返回 false。只有点击确认按钮才返回 true。