C# Winform-在不隐藏窗口的情况下从ShowDialog继续
在多线程环境中 我正在进度表单中使用ShowDialog阻止其他表单上的所有用户活动。C# Winform-在不隐藏窗口的情况下从ShowDialog继续,c#,multithreading,winforms,C#,Multithreading,Winforms,在多线程环境中 我正在进度表单中使用ShowDialog阻止其他表单上的所有用户活动。 完成部分流程后,我隐藏进度表,执行一些任务并再次显示(ShowDialog)。 一切正常,但进度表闪烁。 有没有一种方法可以在不隐藏窗口的情况下从ShowDialog继续播放,或者更好的方法是,有没有一种方法可以将一个节目转换为ShowDialog,然后再转换回来 编辑 我的代码与此类似 class frmProgress : Form { // Standard stuff public
完成部分流程后,我隐藏进度表,执行一些任务并再次显示(ShowDialog)。
一切正常,但进度表闪烁。
有没有一种方法可以在不隐藏窗口的情况下从ShowDialog继续播放,或者更好的方法是,有没有一种方法可以将一个节目转换为ShowDialog,然后再转换回来 编辑
我的代码与此类似
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
ShowDialog();
}
}
void RunWork1()
{
// Do a lot of things including update UI
Hide();
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
编辑2感谢大家的回答和建议。
最后,我采用了这个解决方案,将一些小的工作分组到一个更大的工作中,我在UI中使用这个工作,而在单元测试中,我仍然测试小的工作。
实际上,这并不是我想要的解决方案,我希望找到一个基于处理消息泵或做类似事情的解决方案(请参阅System.Windows.Forms.Application源代码,从RunDialog(.)开始,以及我在发布此问题之前丢失的所有调用方法)。当窗体离开由ShowDialog()启动的模式消息循环时,它将始终处于关闭状态。您所能做的最好是模拟模式,使用Show()显示窗体,并禁用所有其他窗口
因此,您可以枚举所有应用程序的窗口,并在需要时启用/禁用它们,还可以在模拟模态时将对话框窗口设置为始终位于顶部。我认为始终位于顶部意味着它位于操作系统中的所有窗口之上,因此您可能需要一个计时器来将窗口置于最前面(在应用程序窗口中)如果将其设置为模拟模态我想您有如下方法:
void Task1()
{
Debug.WriteLine("Task1 Started");
System.Threading.Thread.Sleep(5000);
Debug.WriteLine("Task1 Finished");
}
void Task2()
{
Debug.WriteLine("Task2 Started");
System.Threading.Thread.Sleep(3000);
Debug.WriteLine("Task2 Finished");
}
如果要并行运行它们,请执行以下操作:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.WhenAll(new Task[] {
Task.Run(()=>Task1()),
Task.Run(()=>Task2()),
});
//Hide the loading image
this.Enabled = true;
}
如果要逐个运行它们:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.Run(()=>Task1());
await Task.Run(()=>Task2());
//Hide the loading image
this.Enabled = true;
}
你不能那样做。至少我知道没有一个简单的方法。解决这个问题的一个方法是按如下方式更改代码:
//C#-ish pseudocode based on OPs example; doesn't compile
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
}
void RunWork1()
{
// Do a lot of things including update UI
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
}
else
{
Hide();
}
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
EDIT对于那些抱怨代码无法编译的人来说,他们是对的。但这是OP在代码示例中获得的最好结果,这也是我猜他被无情地否决的原因
但是为了让答案与其他人更相关,我的建议是:不要在两个任务之间隐藏进度表,当你确定任务已经结束时隐藏进度表。所有这些都是通过尊重你正在处理的上下文的线程模型来实现的,这是OP的代码没有做到的来自最终将在其他线程中运行的方法的表单将不起作用。引用
我建议您利用异步事件处理程序和任务,通过show dialog在窗体处于模式时允许非阻塞调用
class frmProgress : Windows.Form {
// Standard stuff
public void DoSomeWorks() {
Work -= OnWork;
Work += OnWork;
Work(this, EventArgs.Empty);//raise the event and do work on other thread
ShowDialog();
}
private CancellationTokenSource cancelSource;
private event EventHandler Work = delegate { };
private async void OnWork(object sender, EventArgs e) {
Work -= OnWork; //unsubscribe
cancelSource = new CancellationTokenSource(); //to allow cancellation.
var _iLikeTheResultOfWork1 = await RunWork1Async(cancelSource.Token);
if (_iLikeTheResultOfWork1) {
await RunWork2Async(cancelSource.Token);
}
DialogResult = DialogResult.OK; //just an example
}
Task<bool> RunWork1Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
Task<bool> RunWork2Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
}
class frmProgress:Windows.Form{
//标准材料
公共工程{
工作-=正在工作;
工作+=在职;
Work(this,EventArgs.Empty);//引发事件并在其他线程上执行操作
ShowDialog();
}
私有取消令牌源取消源;
私有事件EventHandler Work=delegate{};
专用异步void OnWork(对象发送方、事件参数){
工作-=OnWork;//取消订阅
cancelSource=new CancellationTokenSource();//以允许取消。
var_iLikeTheResultOfWork1=等待运行工作1同步(cancelSource.Token);
如果(工作结果1){
等待RunWork2Async(cancelSource.Token);
}
DialogResult=DialogResult.OK;//只是一个例子
}
任务RunWork1Async(CancellationToken cancelToken){
//做很多事情,包括更新用户界面
//如果在工作时呼叫取消
if(cancelToken.IsCancellationRequested){
返回Task.FromResult(false);
}
//多做事
//如果成功,则返回true
返回Task.FromResult(true);
}
任务RunWork2Async(CancellationToken cancelToken){
//做很多事情,包括更新用户界面
//如果在工作时呼叫取消
if(cancelToken.IsCancellationRequested){
返回Task.FromResult(false);
}
//多做事
//如果成功,则返回true
返回Task.FromResult(true);
}
}
请注意,使用取消令牌可以根据需要取消任务
用户界面现在已解锁,异步函数可以继续工作。表单显示时不会闪烁,不会出现任何中断。我可能会被误解,在这里,我用
wndProc
处理对话。我创建了一个额外的表单并使用Show()
方法。显示表单后,异步任务开始。显示表单后,我通过wndProc
处理该表单
protected override void WndProc(ref Message m) {
if((f2.IsDisposed || !f2.Visible)) {
foreach(var control in controlList) {
control.Enabled = true; // enable all controls or other logic
}
}
if(m.Msg == 0x18 && !f2.IsDisposed) { // notify dialog opens and double checks dialog's situation
foreach(var control in controlList.Where(ctrl => ctrl.Name != "button1")) {
control.Enabled = false; // disable except cancel button
}
}
base.WndProc(ref m);
}
希望对您有所帮助,根据您发布的代码,您将在RunWork()方法的末尾调用Hide(),然后立即调用ShowDialog()。如果我理解正确,您希望首先在RunWork()中调用Hide()方法,使主UI窗口在UI更新发生时可访问。完成所有操作后,ShowDialog()方法将发生并再次阻止主UI线程
class frmProgress : Form
{
public bool _iLikeTheResultOfWork1 = true;
// Note: changed to async method and now awaiting the task
public async void DoSomeWorks()
{
await Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
await Task.Run(() => RunWork2());
ShowDialog();
}
}
// Hide window and wait 5 seconds. Note that the main window is active during this 5 seconds.
// ShowDialog() is called again right after this, so the dialog becomes modal again.
void RunWork1()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
void RunWork2()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
}
我上面的代码将调用RunWork1(),它将隐藏对话框5秒钟。在此期间,主UI窗口将处于活动状态。之后,该方法返回并调用ShowDialog()。关闭该窗口以调用RunWork2(),然后重新启动该过程
这就是您试图实现的目标吗?我刚刚测试了这段代码,它在没有可见“闪烁”的情况下工作。如果这是您想要的,使用此方法是否会出现“闪烁”?为什么在完成部分过程后隐藏“进度表”“?在没有任何复制代码的情况下难以帮助。这在很大程度上取决于你如何真正做事。
ShowDialog
正在阻塞。忘记显示对话框。这没有道理。事实上,我无法想象ShowDialog的用途是什么