dhtmlxgantt的弹窗时间范围_WPF MVVM 模式下的弹窗

WPF MVVM 模式下的弹窗

独立观察员 2020 年 7 月 15 日

一、总体展示

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

99a400bc6268303a31e183e2171c0193.png

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

2c1b62dea351a867617d74981f87b097.png

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

893b3e8d71d2505f65daad9bd13d998e.png

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

7654beda3ca1c83d847095a3356eff63.png

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

4edc93515bba61ec8a0eec99d92c3f33.png

二、用户控件前端

新建 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" >XButton>        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 确认弹框;/// public partial class UC_ConfirmBox : UserControl{    public UC_ConfirmBox ()    {        InitializeComponent ();    }    ///     /// 绑定 VM 中的 IsShowDialog    ///     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 整体结构如下:

96b02f20f472764f51b4a5695d9c28e5.png

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

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

先来看看 “成员” 部分:

dcd5f13dff64cb5c09e2957424865f68.png

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

afda31570aa0cfb8d8b5218c6c0898bc.png

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

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

2d0e9572cfc437da7e5ea25b96eca82c.png

(2) 倒计时

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

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

8e4f1d227cff4384ce52f2ab1432ad12.png

(3) 其它

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

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

4a0fcf8c9d9e231183bd5a02b240c60c.png

五、使用

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

03839cb2cd7cc22409ca5ee2614005af.png

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

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

86a5adc42cdc7ef40eb2d2ed13e34890.png

给出文字版:

// 前台;<uc:UC_ConfirmBox DataContext="{Binding DialogVm}" Visibility="Collapsed" IsShowDialog="{Binding IsShowDialog}">// 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
dhtmlXTree进行一个小的扩展 需求1: 动态生成树形菜单,每个节点都有各自的URL地址,单击不同的节点框架页的右侧跳转到该节点所对应的URL。(框架页说明:左边是树形菜单;右边是显示页面相应信息的页面) 分析: dhtmlXTree提供了很好的添加,删除节点的方法,故选择dhtmlXTree。 但是dhtmlXTree不能满足"每个节点都有各自的URL地址,单击不同的节点框架页的右侧跳转到该节点所对应的URL"这点需求,因次想到了对dhtmlXTree进行一个小的扩展,即在其节点对象原有属性的基础上,再添加两个扩展属性。具体操作如下: 1、找到定义节点对象的那个函数(或方法) function dhtmlXTreeItemObject(itemId,itemText,parentObject,treeObject,actionHandler,mode) 修改为 function dhtmlXTreeItemObject(itemId,itemText,parentObject,treeObject,actionHandler,mode,url,target) 并在方法体中添加赋值语句:this.itemURL=url;this.itemTarget=target; 2、然后修改所有与dhtmlXTreeItemObject有关(直接或者间接相关)的方法: _attachChildNode,insertNewItem,insertNewChild,insertNewNext,_recreateBranch,_parseXMLTree 注:_parseXMLTree方法是与loadXML,loadXMLString相关的。 在这些方法中生成节点的语句中添加相应的参数和语句,以支持新添加的属性itemURL,itemTarget。 需求2: 为dhtmlXTree树上的每一个节点添加右键菜单。附:在树上的节点上点右键时才会生成菜单,空白区域单击时不会生成菜单。 分析: 1、用 dhtmlXTree + dhtmlxmenu 实现。 2、 用dhtmlxmenu生成菜单的部分代码: var menu = new dhtmlXMenuObject(); menu.setImagePath("imgs/"); menu.setIconsPath("images/"); menu.renderAsContextMenu(); menu.loadXML("dhtmlxmenu.xml?e="+new Date().getTime()); menu.addContextZone("treeboxbox_tree"); menu.addContextZone方法是为了把菜单添加到指定区域。 3、dhtmlXTreeObject.prototype._createItem方法是构造树形菜单上元素的具体实现方法。看这个方法的具体操作,可以发现它为每一个节点构建了一个table,节点的内容(即名字)放置在一个span中。 4、考虑到dhtmlxmenu实在指定的区域构建菜单,所以可以为dhtmlXTree树上的每一个节点添加右键事件,在这个右键事件里获得该节点对象所对应的区域,然后在这个区域内构建Menu菜单。 难点和解决方案: 1、怎样获得dhtmlXTree树上的每一个节点对象所对应的区域?(dhtmlXTreeObject.prototype._createItem方法没有为这个节点的span设置id) 解决方法: 在dhtmlXTreeObject.prototype._createItem方法中添加一个为span设置id的语句: 即: 在itemObject.span=document.createElement('span'); itemObject.span.className="standartTreeRow"; 后,新添加一句 itemObject.span.id="treeNode_"+itemObject.id;//为这个span新增一个Id属性 2、为dhtmlXTree树上的每一个节点添加右键事件,在这个右键事件里获得该节点对象所对应的区域,然后在这个区域内构建Menu菜单。 解决方法: a、为dhtmlXTree树上的每一个节点添加右键事件: tree.setOnRightClickHandler(treeOnRegihtClick);//右键事件 b、构建Menu菜单: var menu = new dhtmlXMenuObject(); function treeOnRegihtClick(id){ alert("右键 "+" span.id:"+tree.getItem(id).span.id); menu.setImagePath("imgs/"); menu.setIconsPath("images/"); menu.renderAsContextMenu(); menu.loadXML("dhtmlxmenu.xml?e="+new Date().getTime()); menu.addContextZone(tree.getItem(id).span.id);alert("width:"+tree.getItem(id).span.clientWidth); //var X=tree.getItem(id).span.getBoundingClientRect().left; //var Y=tree.getItem(id).span.getBoundingClientRect().top; var X=document.getElementById('mouseXPosition').value;//获得鼠标的横坐标位置 var Y=document.getElementById('mouseYPosition').value;//获得鼠标的纵坐标位置 menu.showContextMenu(X,Y);//调用showContextMenu方法显示菜单 说明:如果这儿不加上这条语句的话,第一次点击右键时只能生成菜单,但是显示不出菜单,下次点击右键Menu菜单才能弹出。 //menu._showContextMenu(X,Y,tree.getItem(id).span.id); } c、用 javascript 获取当页面上鼠标(光标)位置 <script type="text/javascript"> // 说明:获取鼠标位置 function mousePosition(ev){ if(ev.pageX || ev.pageY){ return {x:ev.pageX, y:ev.pageY}; } return { x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, y:ev.clientY + document.body.scrollTop - document.body.clientTop }; } document.onmousemove = mouseMove; function mouseMove(ev){ ev = ev || window.event; var mousePos = mousePosition(ev); document.getElementById('mouseXPosition').value = mousePos.x; document.getElementById('mouseYPosition').value = mousePos.y; } </script> 页面上放置两个隐藏域存放鼠标的位置:<input type="hidden" id=mouseXPosition><input type="hidden" id=mouseYPosition>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值