[Winform]在Form里显示模态对话框ModalDialog

这篇具有很好参考价值的文章主要介绍了[Winform]在Form里显示模态对话框ModalDialog。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在Form里显示模态Dialog

问题

如何在WinForm的一个Form里面弹出一个模态Dialog?

背景

程序的框架是Winform,只有一个窗口MainForm。MainForm里面是一个TabControl,每个TabPage是一个Form,每个TabPage的Form相互独立,互不干扰,TabPage间可以随时切换。由于有某些需求,TabPage需要接受用户输入,并等待输入完成,才能执行后面的代码,此时,程序是需要阻塞等待输入的,所以需要弹出一个模态Dialog。

  1. 为什么不用MessageBox呢?因为MessageBox是直接弹出一个模态对话框且该对话框是一个新的窗口,这时候整个MainForm是伪阻塞状态,用户无法通过与MainForm的其他区域交互,包括点击标签页切换到其他TabPage。所以,我需要该对画框只在Form里显示。
  2. 为什么不用MDI呢? 最主要的原因是TabControl里的Form,其TopLevel属性是false的,如果想在Form里面添加MDI窗口,需要将Form的TopLevel属性设置为true,这时我将无法使用TabControl。

代码实现

创建一个CustomDialog类,继承Form类

public class CustomDialog : Form{

}

创建CustomDialog成员变量

  1. 这里使用到了两个类, PanelControlContainer。其中Panel充当CustomDialog的容器。ControlContainer则是Panel的容器。
public class CustomDialog : Form{
    private Panel? _panelContainer;
    private ControlContainer? _parentContainer;
    private Form? _parentForm;
    // 声明Panel,ControlContainer和Form
}

定义一个ShowDialog方法

要显示模态Dialog,当然要是实现ShowDialog方法啦!这里定义了一个ShowDialog方法,和其他ShowDialog方法有些许不同,该方法的参数是ControlContainer类型, 用于接收一个控件作为父控件

public class CustomDialog : Form{
    public void ShowDialog(ControlContainer parentControl){
        //TODO
    }

设置CustomDialog.PaneContainer的属性和内容

这部分代码最主要实现了CustomDialog在它的父控件Form中显示的功能,PS:有点简单粗暴,但是有效(_)

public class CustomDialog : Form{
    private void AddDialogToTheView(){
        if(ContainerControl is null){
            throw new NullReferenceException(nameof(_parentContainer));
        }
        //panel的高度
        int panelHeight = 350;
        int panelWidth = 500;
        
        //panel显示的位置
        int startUpLocationX = (_parentContainer.ClientSize.Width - panelWidth) / 2;
        int startUpLocationY = (_parentContainer.ClientSize.Height - panelHeight) / 2;
        
        // 设置_panelContainer的属性
        _panelContainer = new Panel(){
            Height = panelHeight,
            Width = panelWidth,
            Location = new Point(startUpLocationX, startUpLocationY),
        };

        // 设置Dialog的属性
        TopLevel = false;
        DockStyle = DockStyle.Fill;
        //添加进Panel里面
        _panelContainer.Controls.Add(this);
        Contianer.Controls.Add(_panelContainer);
        // 显示Dialog
        Show();
        PanelControl.BringToFront();
        
    }

}

实现伪阻塞

要说实现这个CustomDialog哪里最难,应该是这个伪阻塞功能最难。前面的View相关的方案,一般人稍微思考一下都可以想出来。但是想优雅的实现CustomDialog的伪阻塞功能,确实不易

  • 如何阻塞一段代码?
    我最初的做法是这样的:
public void WaitForExit(Cancellationtoken token){
    while(!toke.IsCancellationRequested){
        Application.DoEvents();
    }
}
CancellationTokenSource source = new CancellationTokenSource();
WaitForExit(source.Token);

//user cancel
source.Cancel();

这个写法有效,但还是不够优雅 😭 ps:切记请勿在UI线程中直接使用while(true){}

  • 最后我的写法是这样的:
    在这里我使用了一个Winform中默认没有的命名空间:System.Windows.Threading
    在csproj里开启wpf的命名空间
<enableWpf>true</enableWpf>

这里其实是借鉴了wpf的模态Dialog的实现方式,具体可以参考wpf的源码;有现成的轮子?直接偷!😊

public class CustomDialog : Form{
    private DispatcherFrame? CurrentDispatcherFrame;
    private void WaitForExit(){
        try{
            
        ComponentDispatcher.PushModal();
        CurrentDispatcherFrame = new DispatcherFrame(true);
        Dispatcher.PushFrame(CurrentDispatcherFrame);
        }
        finally{
            ComponentDispatcher.PopModal();
        }
    }
}

当调用WaitForExit()方法后,程序就进入伪阻塞状态了,此时UI线程仍然能绘制UI;直到调用CurrentDispatcherFrame?.Continue = false;,WaitForExit才会退出伪阻塞状态。

细节优化

  • 这个时候,整个CustomDialog的大体实现基本完成了,下一步就是优化细节

重写Form的Closed事件

当调用CustomDialog的Close()方法时,会触发Form.OnClosed事件,此时阻塞状态将会退出

protected override void OnClosed(EventArgs e){
    base.OnClosed(e);
    if(CurrentDispatcherFrame is not null){
        CurrentDispatcherFrame.Continue = false;
        CurrentDispatcherFrame = null;
    }
}

在ParentForm中注册关闭事件

在CustomDialog弹出的状态,如果用户想退出程序,点击MainForm的关闭按钮,此时是关闭不了的。MainForm是需要等CustomDialog关闭后才能关闭的,而CustomDialog需要等待用户关闭才能关闭。此时需要将MainForm的关闭事件注册到CustomDialog的关闭事件上。

  1. ParentForm_Closing事件
private void ParentForm_Closing(object? sender, CancelEventArgs e){
    this.Close();//ParentForm关闭时,关闭CustomDialog
}
  1. ParentForm订阅关闭事件
public void ShowDialog(ContainerControl _parentContainer){

    if(_parentContainer is Form containerForm && containerForm.TopLevel){
        this.Owner = containerForm;
    }
    _parentForm = _parentContainer.ParentForm;
    _parentForm.Closing += ParentForm_Closing;//订阅关闭事件
    
    //TO DO
}

重写Form的Closing事件

订阅了closing事件记得也要取消订阅

protected override void OnClosing(CancelEventArgs e){
    base.OnClosing(e);
    if(_parentForm is not null){
        _parentForm.Closing -= ParentForm_Closing;
    }
}

完整的ShowDialog方法

public IAsyncResult ShowDialogAsync(ContainerControl _parentContainer){
    var asyncResult = _parentContainer.BeginInvoke(new Action(() => 
    {
        if(_parentContainer is Form containerForm && containerForm.TopLevel){
            this.Owner = containerForm;
        }
        _parentForm = _parentContainer.ParentForm;
        _parentForm.Closing += ParentForm_Closing;//订阅关闭事件

        AddDialogToTheView(); //已完成
        WaitForExit(); //已完成
        RemoveTheDialogFromTheView();//TODO 这里懒得写了
    }));
    return asyncResult;
    }

同步方法

public void ShowDialog(ContainerControl _parentContainer){
    var asyncResult = ShowDialogAsync(_parentContainer);
    asyncResult.AsyncWaitHandle.WaitOne();
}

结语

  • 至此,CustomDialog已经可以使用了。定制的DialogForm,只需要继承CustomDialog即可。其他交互逻辑在子类中实现即可

其他细节

当Form改变的时候,自动调整CustomDialog到Form中间:向_parentContainer订阅SizeChanged事件文章来源地址https://www.toymoban.com/news/detail-760715.html

protected void _parentContainer_SizeChanged(object sender, EventArgs e){
    if (sender is not ContainerControl control || _containerPanel is null)
    {
        return;
    }

    _panelContainer.Location = new Point((control.ClientSize.Width - _containerPanel.Width) / 2, (control.ClientSize.Height - _containerPanel.Height) / 2);
}

到了这里,关于[Winform]在Form里显示模态对话框ModalDialog的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ApeForms | C# - WinForm临时对话框(消息框、提示框、输入框、密码框、单选框、多选框等)

    ApeForms一款基于WinForm实现的开源免费商用的UI库,其中提供了一套便于用户交互的临时对话框组件(Dialog),可用于快速创建开发中常见的交互对话框,例如:消息框、提示框、输入框、单选框、多选框等。 ApeFree.ApeDialogs并不是一个包含Dialog实现的UI库,而是一套Dialog标准(

    2023年04月08日
    浏览(35)
  • QT 消息对话框按钮显示

    搞QT嘛,大多数都是军工。都要国产化,而且消息对话框的按钮的英文也不是很得劲,所以需要汉化。使用静态函数的按钮就是显示英文,汉化的代码如下。 QmessageBox功能挺强大的,可以设置默认的按钮,也可以设置点击窗口的关闭按钮实际上点击的是那个按钮,默认情况下

    2024年02月10日
    浏览(35)
  • Android 显示 指纹/人脸 身份验证对话框

            为了增加用户的隐私和安全,需保护您的应用中的敏感信息或付费内容,一种方法是请求生物识别身份验证,例如使用人脸识别或指纹识别。本篇文章介绍了如何在您的应用中支持生物识别登录流程。 如需定义您的应用支持的身份验证类型,请使用 BiometricManager.Au

    2024年02月01日
    浏览(28)
  • Opencv显示图片(MFC基于对话框窗口)

    #1.编辑对话框按钮,修改各个按钮窗口ID和绑定变量 picture 控件 “打开图片”按钮控件 #2.编写显示图片函数 void DrawMat(cv::Mat img, UINT nID); .h文件进行声明 .cpp进行定义 #3.定义CString fun_LoadImg();函数 .h文件进行声明 .cpp文件进行定义 #4.双击“打开图片”按钮 系统自动弹出响应函数

    2024年04月15日
    浏览(37)
  • 微信小程序开发系列(二十九)·界面交互API·loading 提示框、showModal模态对话框、showToast消息提示框

    目录 1.  loading 提示框 1. 1  wx.showLoading()显示loading提示框 1.2  wx.hideLoading()关闭 loading 提示框 2.  showModal 模态对话框 3.  showToast 消息提示框         小程序提供了一些用于界面交互的 API,例如:loading 提示框、消息提示框、模态对话框等 API。 loading 提示框常配合网络请

    2024年03月25日
    浏览(32)
  • UG\NX二次开发 非模态消息对话框函数 UF_UI_display_nonmodal_msg

    文章作者:里海 来源网站: https://blog.csdn.net/WangPaiFeiXingYuan         uc1601函数提供了一个模态消息对话框,ufun函数中还有一个非模态消息对话框,运行一次弹出一个窗口,不点确定不消失,对话框显示后不影响使用其他命令运行。         UF_UI_display_nonmodal_msg的第二个参

    2024年02月15日
    浏览(33)
  • Bootstrap之JavaScript的模态框(modal)使用-不离开父窗体的情况下的互动-点击按钮弹出对话框

    Bootstrap的JavaScript的模态框(modal)是覆盖在父窗体上的子窗体。通常,目的是显示一个单独的内容,可以在不离开父窗体的情况下有一些互动。 子窗体可以自定义内容,可提供信息展示、交互等功能。 运行效果如下: 一个完整的模态框包括三个部分:头部、正文和页脚,分别

    2024年02月04日
    浏览(36)
  • Rust UI开发(三):iced如何打开图片(对话框)并在窗口显示图片?

    注:此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库,用于为rust语言程序构建UI界面。 这是一个系列博文,本文是第三篇,前两篇的链接: 1、Rust UI开发(一):使用iced构建UI时,如何在界面显示中文字符 2、Rust UI开发(二):iced中如何为窗口添加icon图标 本篇是

    2024年02月04日
    浏览(57)
  • 解决问题:Element ui组件中Dialog对话框只显示遮罩层,不显示弹框内容

    以下是Dialog 对话框的基本用法: 这个时候页面只显示了遮罩层,但是不显示弹框的内容,查阅文档后,发现有这样一条: append-to-body的默认值是false,所以需要自己在el-dialog标签中手动设置为true,  加上之后弹框内容就可以显示了。

    2024年02月06日
    浏览(35)
  • vue中使用el-dialog设置弹窗对话框在前端显示为居中

    废话不多直接上图

    2024年02月01日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包