在WPF(.net 4.0)中,如何使子窗口阻止代码而不阻塞主窗口?

时间:2015-08-27 13:14:29

标签: wpf modal-dialog window async-await

我有一个WPF应用程序来运行测试用例并收集结果。在主窗口上,使用可以选择一些测试用例并循环运行它们。运行案例时,会弹出一个自定义子窗口并向用户显示一些数据,然后用户点击“传递”按钮或“失败”来设置此测试用例的结果并关闭这个子窗口。然后下一个测试用例开始运行。在主窗口中,有一个“停止”按钮。用户可以点击它以在当前轮次结束后停止循环测试。 代码如下:

while (!stopByUser)
{
   foreach(var caseItem in caseList)
   {
      // on TestWindow UI, caseItem.isPassed will be set by user with clicking buttons;
      caseItem.isPassed = false;
      TestWindow tw = new TestWindow(caseItem);
      tw.ShowDialog();
      if (caseItem.isPassed)
      {
         totalPassed++;
         // update UI ...
      }
   }
}

问题是用户无法点击主窗口上的“停止”按钮,因为我使用tw.ShowDialog()来弹出模态窗口。但是,我也不能简单地将它改为tw.Show()来弹出一个非模态窗口,因为foreach循环中的代码必须同步执行。

我发现本机MessageBox具有以下功能:阻止代码而不阻止主窗口。例如

var result= MessageBox.Show(message, "Is this test case passed?", MessageBoxButton.YesNo);
// the following line will be executed after the MessageBox is closed
// and meanwhile I can operate my main window when the MessageBox is still visible
var passed = (judgement == MessageBoxResult.Yes);

所以我的问题是如何在.net 4.0中使用WPF窗口来实现此功能? 我的应用程序将在Windows XP上运行,因此.net 4.0是必需的。

有什么想法吗?提前谢谢!

2 个答案:

答案 0 :(得分:0)

MessageBox.Show会阻止所有窗口,因为它使用ShowDialog方法。你能做的就是写逻辑:

  1. 提供下一个项目
  2. 为其创建新窗口
  3. 订阅此窗口的已结束活动
  4. OnClosed事件更新您的ui并转到第一步
  5. 示例:

        private bool stopByUser {get;set;}
        private int currentItemIndex {get;set;}
        private List<object> caseList {get;set;}]
        private TestWindow tw {get;set;
        public void OnLoaded(object sender, RoutedEventArgs e)
        {
            ShowNext();
        }
    
        private void ShowNext()
        {
            if (stopByUser) return;
    
            var caseItem = caseList[currentItemIndex++]
            caseItem.isPassed = false;
    
            tw = new TestWindow(caseItem);
            tw.Closed += (s, e) => 
            { 
                if (caseItem.isPassed)
                {
                    totalPassed++;
                    // update UI ...
    
                    ShowNext();
                }
            };
            tw.Show();
        }
    

答案 1 :(得分:0)

感谢@Noseratio和@ VMaleev的帮助,我最终得到了一个使用Microsoft.Bcl.Async和async / await的解决方案。它不漂亮,但它有效。

while (!stopByUser)
{
   foreach(var caseItem in caseList)
   {
        caseItem.isPassed = false;
        GlobalVar.isFinished = false; // static variable: isFinished
        TestWindow tw = new TestWindow(caseItem);
        tw.Closed += (s, e) =>
        {
            GlobalVar.isFinished  = true;
        };
        Task task = new Task(() =>
        {
            while (!GlobalVar.isFinished)
            {
                Thread.Sleep(100);
            }
        });
        task.Start();
        tw.Show();
        await task;
        // get the test result here from GlobalVar.isPassed
        var passed = caseItem.isPassed;
        // do other things
   }
}

请记住,要使用Microsoft.Bcl.Async,必须在xp / win7上安装.NET Framework 4.0( KB2468871 )。查看更多here