【WPF】如何让弹出的窗口"阻塞"

WPF点击按钮弹出一个窗口 / 实例化新窗口// 非模态显示(用户可继续操作原窗口)// 或 newWindow.ShowDialog();// 模态显示阻塞窗口,直到新窗口关闭)Show()// 新窗口的构造函数// 设置窗口标题// 按钮点击事件// 新窗口的公共属性set;// 按钮点击事件// 设置数据// 新窗口(模态)// 获取用户输入// 设置对话框结果// 原窗口调用。 阅读详情
还存在一些问题,再研究一下
1、ComponentDispatcher其实可以不用
2、new一个DispatcherFrame其实是把一个消息循环(姑且称作嵌套消息循环)当做一个DispatchFrame来处理,在这个消息循环结束之前,原来的代码就是阻塞的
3、正是因为第二个原因,如果再次弹出一个窗口,将是在前一个嵌套消息循环中,再次执行2,也就会导致第一个窗口关闭,并不会立即执行后面的代码。

【场景描述】

某些时候可能会有这种需求,一个用户界面里面分为好多个功能区域。这些功能区域有时候会有一些“模态”的弹出窗口的交互,这些弹出窗口需要:
1、只影响当前区域。即鼠标或者键盘无法操作当前区域,而其他区域不受影响。比如说,有好多个选项卡页面,每个选项卡页面弹出的窗口只影响当前选项卡,而不影响其他的选项卡。
2、窗口未关闭之前,后面的代码不执行,即“阻塞”。

【问题分析】

WPF中的窗口的弹出提供了两个方法:Show和ShowDialog。其中,Show方式是一个非“阻塞”的方法,即:调用完Show之后立即返回,后面的代码将立即被执行,但是这个方法不会挡住整个窗口。而ShowDialog方法是一个“阻塞”的方法,即调用完ShowDialog之后需要等窗口关闭代码才会继续执行。然而,ShowDialog会挡住整个窗口,而不是指定的区域。
综合这个两个方法考虑,其实,对于场景中的第一条,我们可以考虑通过调用Show让窗口弹出之后,即将该区域置为IsEnabled=false来模拟“模态”的窗口,这个倒不是很难。问题在于第二条,如何让窗口“阻塞”住当前代码的继续执行呢?即:
var win = new MyDialog();
win.Show();
// 关闭后才要执行的代码
1、用一个while死循环,直到关闭才跳出。如果采用这种方式,那我们不得不好好考虑一下,在UI线程做死循环UI还有没有办法响应。当然,方法是有的,可以实现一个WPF版的DoEvents方法,然后在循环中调用。DoEvents相关的代码可以参见:http://www.cnblogs.com/sheva/archive/2006/08/24/485790.html
2、用异步模式来写。代码将类似于:
var win = new MyDialog();
// MyCloseCallback是一个窗口关闭时调用的回调
win.Show(MyCloseCallback);
这样确实可以满足需求,不过我们不得不忍受把一份代码拆开为两份来写的痛苦。当然,匿名方法和Lamba可以带来一些缓解,但是写起来始终不是那么舒服。
难道,我们除了使用可怕的while死循环+DoEvents,或者忍受别扭的代码之外,就没有别的办法了么?

【揭开ShowDialog的面纱】

其实,回过头再去想,为什么ShowDialog方法就可以阻塞代码的执行呢?如果我们能够知道如何阻塞代码,然后把“挡住”整个窗口的部分给去掉,不就OK了么?
好,我们请出神器Reflector打开Window的ShowDialog方法一窥究竟。
public bool? ShowDialog()
{

try
{
this._showingAsDialog= true;
this.Show();
}
catch
{

}
finally
{
this._showingAsDialog= false;
}

}


核心的部分如上所示,其实ShowDialog只是置了一个状态,最主要的代码还是在Show里面。好,我们接着转战到Show
public void Show()
{
this.VerifyContextAndObjectState();
this.VerifyCanShow();
this.VerifyNotClosing();
this.VerifyConsistencyWithAllowsTransparency();
this.UpdateVisibilityProperty(Visibility.Visible);
this.ShowHelper(BooleanBoxes.TrueBox);
}

前面都是在做一些检查,最终的调用原来跑到了ShowHelper

private object ShowHelper(object booleanBox)
{
if (!this._disposed)
{

if (this._showingAsDialog&& this._isVisible)
{
try
{
ComponentDispatcher.PushModal();
this._dispatcherFrame= new DispatcherFrame();
Dispatcher.PushFrame(this._dispatcherFrame);
}
finally
{
ComponentDispatcher.PopModal();
}
}
}
return null;
}





关键一步看来是:
ComponentDispatcher.PushModal();
ComponentDispatcher是个什么东东?MSDN之:

Enables shared control of the message pump between Win32 and WPF in interoperation scenarios.
原来是用于操作消息循环的。
而这两句:
this._dispatcherFrame = new DispatcherFrame();
Dispatcher.PushFrame(this._dispatcherFrame);
如果有看过DoEvents的实现,就好理解了,简单的说,这里其实是让UI可以继续处理消息队列。

【实现】

有了以上准备,我们就很好处理了。
1、首先,我们通过模仿ShowHelper中的代码,来实现阻塞代码的执行
2、其次,我们通过设置IsEnabled属性来模拟“模态”的效果。

 publicclass MyDialog : Window
{
private DispatcherFrame _dispatcherFrame;
private FrameworkElement _container= null;
private double _lastLeft = 0.0;
private double _lastTop = 0.0;

public void Open(FrameworkElement container)
{
if (container!= null)
{
_container = container;
// 通过禁用来模拟模态的对话框
_container.IsEnabled =false;
// 保持总在最上
this.Owner= GetOwnerWindow(container);
if (this.Owner!= null)
{
this.Owner.Closing+= new System.ComponentModel.CancelEventHandler(Owner_Closing);
}
// 通过监听容器的Loaded和Unloaded来显示/隐藏窗口
_container.Loaded +=new RoutedEventHandler(Container_Loaded);
_container.Unloaded +=new RoutedEventHandler(Container_Unloaded);
}
this.Show();
try
{
ComponentDispatcher.PushModal();
_dispatcherFrame =new DispatcherFrame(true);
Dispatcher.PushFrame(_dispatcherFrame);
}
finally
{
ComponentDispatcher.PopModal();
}
}

// 在Owner关闭的时候关闭
privatevoid Owner_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
this.Close();
}

private void Container_Unloaded(object sender, RoutedEventArgs e)
{
// 只能通过这种方式隐藏,而不能通过Visiblity = Visibility.Collapsed,否则会失效
_lastLeft =this.Left;
_lastTop = this.Top;
this.Left = -10000;
this.Top = -10000;
}

private void Container_Loaded(object sender, RoutedEventArgs e)
{
this.Left = _lastLeft;
this.Top = _lastTop;
}

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
if (_container!= null)
{
_container.Loaded -= Container_Loaded;
_container.Unloaded -= Container_Unloaded;
}
if (this.Owner!= null)
{
this.Owner.Closing-= Owner_Closing;
}
}

protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 当关闭终止消息循环
if (_dispatcherFrame!= null)
{
_dispatcherFrame.Continue =false;
}
// 这里必须强制调用一下
// 否则出现同时点开多个窗口时,只有一个窗口让代码继续
ComponentDispatcher.PopModal();
if (_container!= null)
{
_container.IsEnabled =true;
}
}

private Window GetOwnerWindow(FrameworkElement source)
{
var parent = VisualTreeHelper.GetParent(source)as FrameworkElement;
if (parent == null)
return null;
var win = parentas Window;
return
win != null ?
parent as Window :
GetOwnerWindow(parent);
}
}


使用的时候

var btn = senderas Button;
var dialog =new MyDialog
{
Width = 300,
Height = 200,
Content = btn.Tag,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
var border =this.FindName((string)btn.Tag)as Border;
dialog.Open(border);
// 在窗口关闭之前不会执行
MessageBox.Show(string.Format("{0}上弹出对话框结束!", btn.Tag));


代码下载

/Files/RMay/TryMessageBox.zip

WPF按钮点击事件全解析:从Show到ShowDialog窗口交互实战 本文深入解析WPF中按钮点击事件打开新窗口,Show()与ShowDialog()的核心区别与应用场景。通过实战代码对比,阐明非模态窗口的并行交互与模态窗口的焦点独占特性,帮助开发者根据工具窗口或配置对话框等不同需求做出正确选择,从而构建流程清晰、用户体验良好的桌面应用程序。 阅读详情

相关推荐

WPF窗口交互实战:如何优雅地通过按钮打开并管理新窗口

本文深入探讨了WPF应用中通过按钮交互实现多窗口管理的优雅方案。文章对比了模态与非模态窗口的适用场景,分析了直接实例化窗口的资源管理陷阱,并详细介绍了通过构造函数、属性、事件以及MVVM模式与消息机制进行窗口间数据传递的方法。最后,提出了引入窗口服务/工厂模式以及遵循生命周期最佳实践来构建可维护、健壮的多窗口应用架构,为开发者提供了从基础到进阶的实战指南。

weixin_29266749的博客 242

WPF ShowDialog模式下 只阻塞窗口(owner)的解决方法

WPF程序中,正常情况下,调用窗体的ShowDialog方法之后,w1.ShowDialog() 该应用程序的除去w1窗口之外所有窗口都将处于无效状态(disable),直到w1窗口关闭。 在某些情况下我们希望,w1.ShowDialog()方法调用之后,只有w1窗口的父窗口处于无效状态 而程序其他窗口可以继续响应用户操作。百度、google了一番,方法都太复杂,自己想了一个方法也挺简单的 这里我们需要用到W32函数 EnableWindow 我做了简单的封装 直接使用下面的类就可以了 调...

u012046379的专栏 2584

C# WPF中利用Windows API实现第三方EXE无缝嵌入与窗口控制

本文详细介绍了在C# WPF应用程序中,如何利用Windows API(特别是SetParent函数)将第三方EXE程序无缝嵌入到自定义界面中。通过FindWindow定位窗口、SetParent建立父子关系、MoveWindow调整布局以及SendMessage实现远程控制,手把手演示了嵌入Windows计算器的完整流程。文章还深入探讨了嵌入后可能遇到的焦点丢失、DPI缩放、消息循环同步等常见问题及其解决方案,为开发者集成遗留或第三方工具提供了实用的技术指南。

weixin_29207533的博客 354

Qt——对话框详解

Qt——对话框详解

m0_73443478的博客 3655

MUI 弹出消息框

dialog(对话框) 创建并显示对话框,弹出的对话框为非阻塞模式,用户点击对话框上的按钮后关闭( h5模式的对话框也可通过 closepopup关闭 ),并通过callback函数返回用户点击按钮的索引值或输入框中的值。 Dialog 组件包含: alert 警告框 confirm 确认框 prompt 输入对话框 toast 消息提示框 mui会根据ua判断,弹出原生对话框还是h5绘制...

weixin_44264878的博客 2690

WPF-键盘事件

通过一个demo来了解键盘事件 设计代码 <Window x:Class="keyboard.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&amp

Maybe_ch的博客 1243

wpf 点击按钮弹出选择框_关于WPF弹出窗口

几个重要的概念需要清楚:Show和ShowDialog区别1、调用Show方法后弹出窗口后,线程会继续往下执行。调用ShowDialog方法弹出窗口后,线程会阻塞,直到子窗口关闭才继续往下执行。2、ShowDialog弹出的子窗口会使父窗口不能获得焦点,必须先关闭子窗口才能关闭父窗口。Show则不会。3、在ShowDialog弹出的子窗口中设置DialogResult会关闭子窗口,父窗口可根据...

weixin_39845613的博客 2597

关于WPF弹出窗口

几个重要的概念需要清楚: Show和ShowDialog区别 1、调用Show方法后弹出窗口后,线程会继续往下执行。调用ShowDialog方法弹出窗口后,线程会阻塞,直到子窗口关闭才继续往下执行。 2、ShowDialog弹出的子窗口会使父窗口不能获得焦点,必须先关闭子窗口才能关闭父窗口。Show则不会。 3、在ShowDialog弹出的子窗口中设置DialogResult会关闭子窗口,父窗...

weixin_44591019的博客 1567

wpf】11 在WPF中实现父窗口蒙版效果:原理详解与进阶优化

实现效果:子窗口弹出,父窗口呈现半透明遮罩;子窗口关闭后,蒙版自动消失,父窗口恢复交互。优势代码简洁,依赖纯WPF原生控件实现。支持模态与非模态窗口,扩展性强。动画和样式优化后,用户体验更佳。开发者可根据实际需求选择基础实现或进阶优化方案。本文代码已通过Visual Studio 2022测试,建议读者动手实践以加深理解。

kewaqi618的博客 1910

WPF 利用子线程弹出子窗体的研究

原文:WPF 利用子线程弹出子窗体的研究  一般来说子线程都是用来处理数据的,主窗体用来实现展现,但是有些候我们希望子窗体实现等待效果,遮挡主窗体并使主窗体逻辑正常进行,这个业务需求虽然不多,但是正好我们用到了,于是我打算把研究成果写在这了。稍后我会上传我的代码,包含测试程序以及之前两边博文谈到的控件,这里我还实现了一个类似雷达扫描的等待窗体,大家可以稍后查看。   实际上这里只有两个难点需要...

weixin_34184561的博客 336

wpf中重写OnClosing窗口关闭事件报错

贴代码   protected override void OnClosing(System.ComponentModel.CancelEventArgs e)             {                          //base.OnClosing(e);             if (System.Windows.Forms.MessageBox.Show("

3635

WPF/WinForm在线程中调用提示窗MessageBox阻塞窗口方法

把MessageBox.Show交给主线程去处理就可以了public void ShowMessage(string msg) { this.Dispatcher.Invoke(new MessageBoxShow(MessageBoxShow_F), new object[] { msg }); } delegate vo

oArvin1234的专栏 5766

WPF 按钮触发新窗口的交互设计与实现

本文详细讲解了WPF中通过按钮点击触发新窗口的交互设计与实现方法。核心在于理解并正确使用Show()与ShowDialog()方法,分别对应非模态与模态窗口,以满足不同场景下的用户交互需求。文章通过实战案例,阐述了从事件处理、数据传递到窗口生命周期的完整流程,并提供了MVVM模式下的高级实现方案与常见问题排查技巧,帮助开发者构建健壮、用户体验良好的WPF桌面应用。

keras9composer的博客 498

WPF 窗口显示数秒后自动关闭

窗口加载,我们调用“ShowWindowForTwoSeconds”方法。在方法中,我们“Show”窗口,等待2秒钟,然后关闭窗口。通过使用“async / await”机制,我们可以防止UI线程被挂起。

2302_76350189的博客 1067

wpf 开发实用笔记

本笔记主要描述我在学习wpf开发中遇到的一些具体的问题,以及处理方法,以便于日后使用找到相应的依据。

TRAMP~ 1222
上一篇: linux下gdb单步调试(下)(转摘)
neufan
博客等级 码龄17年 1粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值