zoukankan      html  css  js  c++  java
  • WPF MVVM 模式下的弹窗

    WPF MVVM 模式下的弹窗

    独立观察员 2020 年 7 月 15 日

    一、总体展示

    首先看看用户控件在设计页面的大致效果:

    中间部分自然就是确认弹框了,由标题、内容、确认按钮、取消按钮、倒计时、关闭按钮组成,指定了大小范围:

    外层还有个 Grid,没有指定大小,所以使用时会铺满容器,再配上带透明度的背景色,可以当作蒙版,避免用户继续操作后面的界面,达到模态弹窗的效果:

    确认弹框,手动关闭、点击取消按钮、超时关闭这三种情况下会输出相关信息(需传入记录信息的委托方法),点击确认按钮则可以继续执行业务方法。

    还有一种是信息弹框,区别是不用于执行业务方法,也不输出信息 (操作结果),只是用于提示用户,且默认标题和默认超时时间不同(可修改):

    二、用户控件前端

    新建 WPF 用户控件后,贴入以下代码:

    <Grid Background="#905F9EA0">
        <Grid Background="Gainsboro" MinWidth="300" MinHeight="200" MaxWidth="400" MaxHeight="300">
            <Grid.RowDefinitions>
                <RowDefinition Height="28"></RowDefinition>
                <RowDefinition Height="28"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
    
            <DockPanel Height="28" Background="SteelBlue">
                <TextBox Text="{Binding DialogTitle, TargetNullValue=' 注意 ', FallbackValue=' 注意 '}" Height="26" Width="Auto" Background="SteelBlue" BorderThickness="0" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="White" FontSize="16" Focusable="False" IsHitTestVisible="False" IsTabStop="False" VerticalContentAlignment="Center" Padding="2,0,0,0">
                </TextBox>
                <Button x:Name="BtnClose" Command="{Binding CloseCommand}" Height="26" Width="26" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="16" Background="Transparent" Foreground="White" BorderThickness="0" >X</Button>
            </DockPanel>
    
            <StackPanel Grid.Row="1" Orientation="Horizontal" FlowDirection="RightToLeft">
                <TextBox VerticalContentAlignment="Center" Text="{Binding LeftTime, FallbackValue=20, TargetNullValue=20}" FontSize="16" Background="Transparent" Foreground="Coral" BorderThickness="0" Margin="5,0"></TextBox>
            </StackPanel>
    
            <TextBlock Grid.Row="2" FontSize="16" Text="{Binding DialogMessage, FallbackValue=' 是否确认操作?', TargetNullValue=' 是否确认操作?'}" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
    
            <StackPanel Grid.Row="3" Orientation="Horizontal" FlowDirection="RightToLeft" VerticalAlignment="Center" Margin="0, 10">
                <Button x:Name="BtnConfirm" Command="{Binding ConfirmCommand}" Content="{Binding DialogConfirmBtnText, TargetNullValue=' 确认 ', FallbackValue=' 确认 '}" FontSize="16" Background="SteelBlue" Foreground="White" Margin="10, 0" Width="120"></Button>
                <Button x:Name="BtnCancel" Command="{Binding CancelCommand}" Content="{Binding DialogCancelBtnText, TargetNullValue=' 取消 ', FallbackValue=' 取消 '}" FontSize="16" Background="SteelBlue" Foreground="White" Margin="10, 0" Width="120"></Button>
            </StackPanel>
        </Grid>
    </Grid>

    就是简单做了下布局和样式,然后做了数据绑定和命令绑定,我们移步到后台来看。

    三、用户控件后台

    由于使用了 MVVM 模式,所以页面的后台代码中没多少内容:

    /// <summary>
    /// [dlgcy] WPF MVVM 确认弹框;
    /// </summary>
    public partial class UC_ConfirmBox : UserControl
    {
        public UC_ConfirmBox ()
        {
            InitializeComponent ();
        }
    
        /// <summary>
        /// 绑定 VM 中的 IsShowDialog
        /// </summary>
        public bool IsShowDialog
        {
            get { return (bool) GetValue (IsShowDialogProperty); }
            set { SetValue (IsShowDialogProperty, value); }
        }
    
        public static readonly DependencyProperty IsShowDialogProperty = DependencyProperty.Register ("IsShowDialog", typeof (bool), typeof (UC_ConfirmBox), new PropertyMetadata (false, (obj, args) =>
        {
            if (args.NewValue is bool newValue)
            {
                try
                {
                    var control = obj as UC_ConfirmBox;
                    control.Visibility = newValue ? Visibility.Visible : Visibility.Collapsed;
                }
                catch (Exception ex)
                {
                    Console.WriteLine (ex.ToString ());
                    MessageBox.Show ($"{ex.Message}");
                }
            }
        }));
    }

    建了个依赖属性,用于使用用户控件时绑定。这个是绑定 ViewModel 中的同名属性 IsShowDialog 的(是否显示弹窗),实际上,不用这个依赖属性而直接用 Visibility 绑定 IsShowDialog(ViewModel 中的),然后加上相关转换器也可以,但那样对用户不太友好,所以这里直接在依赖属性中进行 Visibility 的判断。(关于依赖属性的使用可以看本人之前的文章《WPF 用户控件的自定义依赖属性在 MVVM 模式下的使用备忘》)。

    然后注意一点,这里并没有直接将 DataContext 关联 ViewModel,而是要在使用用户控件时再绑定(大家觉得我做得对吗),这个后面还会说到。

    四、用户控件对应的 ViewModel

    这里代码比较多,就不贴出来了,最后会给出代码托管地址。ViewModel 整体结构如下:

    ConfirmBoxViewModel 上有个特性 AddINotifyPropertyChangedInterface,这个是一个第三方的包 PropertyChanged.Fody 提供的,加上之后,类的公共自动属性就具有了属性变动通知功能。那么为什么还要继承 BindableBase (实现了 InotifyPropertyChanged 的基类,参考《WPF 原生绑定和命令功能使用指南》)呢? 原因是,如果在属性的 get/set 中做了一些操作的话,Fody 对该属性好像就不起作用了,所以补救一下。

    (1) 弹框时阻塞业务流程

    先来看看 “成员” 部分:

    有个线程同步对象 AutoResetEvent,缺省设置为阻塞线程,由上图可见,在弹框隐藏时会取消阻塞,那么阻塞的时机自然就是弹框显示后:

    上图显示的确认框帮助类的 “弹出确认框” 方法中,由于是使用异步调用,所以阻塞不会影响 UI 线程。阻塞方法可以指定超时时间,超时或者用户没有点击确认按钮则直接返回,否则,则执行传入的委托方法,即实际的业务方法。

    另一个 “弹出消息框” 方法则相较简单,只是简单阻塞了一段 “消息框超时时间”:

    (2) 倒计时

    上一小节开头处给出的” 成员图” 中,还有一个定时器类型对象 _timer,就是用于倒计时功能的。计时器在弹窗弹出时开始启动,代码位于 IsShowDialog 属性的 Set 方法中(见” 成员图”)。

    计时器的执行方法在构造函数中绑定,执行方法内部,每隔一秒(声明时设定)将剩余时间减 1,减为 0 时停止,并执行关闭命令。此处和弹窗阻塞超时那里可能有功能冗余,当然,从另一方面来说,也可以看作是双重保险。

    (3) 其它

    “Bindable” 区域中剩余的属性都没有做特殊处理。

    命令的使用可以参考前文提到的文章,命令的处理逻辑则比较简单,就是设置是否显示和是否确认:

    五、使用

    使用时,引入用户控件命名空间之后,将其与主界面平级摆放,实际就是覆盖在主界面上方,然后设置其 Visibility 属性为 “Collapsed”,不可见也不占用空间,避免影响主界面的开发:

    IsShowDialog=”{Binding IsShowDialog}” 也是固定这样设置即可,用于配合 DataContext 控制显示隐藏,而 DataContext 则是绑定了主页面 ViewModel 中相关的用户控件的 ViewModel 对象。

    调用的时候要注意异步的问题,使用辅助类 ConfirmBoxHelper 中的两个方法即可:

    给出文字版:

    // 前台;
    <uc:UC_ConfirmBox DataContext="{Binding DialogVm}" Visibility="Collapsed" IsShowDialog="{Binding IsShowDialog}"></uc:UC_ConfirmBox>
    
    // ViewModel;
    public ConfirmBoxViewModel DialogVm { get; set; } = new ConfirmBoxViewModel ();
    
    // 使用;
    await ConfirmBoxHelper.ShowMessage (DialogVm, "操作前通知", 6);
    
    await ConfirmBoxHelper.ShowConfirm (DialogVm, "您确定要进行此操作吗?", () =>
    {
        #region 业务方法
    
        //。。。   

      #endregion
    }, ShowInfo); await ConfirmBoxHelper.ShowMessage (DialogVm, "操作后通知");

    六、地址

    这个是在 XMPP 通信 Demo 项目中写的,项目地址为:

    https://gitee.com/dlgcy/XmppPractice

    同步首发:

    http://dlgcy.com/wpf-mvvm-confirm-dialog/

    微信订阅号

  • 相关阅读:
    Java-MyBatis-MyBatis3-XML映射文件:select
    Java-MyBatis-MyBatis3-XML映射文件:XML映射文件
    专业词汇-计算机-Java:JPA
    图书-计算机-软件编程-SpringBoot:《Spring Boot 企业级应用开发实战》
    图书-计算机-软件编程-SpringBoot:《Spring Boot2 + Thymeleaf 企业应用实战》
    svndumpfilter
    svnlook
    svnadmin
    svn
    sum
  • 原文地址:https://www.cnblogs.com/weiliuhong/p/wpf-mvvm-confirm-dialog.html
Copyright © 2011-2022 走看看