WinForm在多线程C ++ / CLI应用程序中消失

时间:2013-04-11 03:20:09

标签: multithreading winforms c++-cli

我目前正在开发一个应用程序,需要利用表单来阻止用户使用主表单几秒钟。尽管在主线程上运行代码似乎没问题,但弹出窗体上的标签在表单首次出现时不会呈现大约一秒钟。我认为如果我在一个单独的线程上运行该表单,渲染会更顺畅。渲染现在非常流畅,渲染后表格立即消失。计时器设置为五秒钟,屏幕上的标签倒计时。这是调用新线程和表单的相关代码:

System::Void MainScreen::runGame(int playerTurn) {
    Thread ^ t = gcnew Thread(gcnew ThreadStart(gcnew MainScreen(),
        &MainScreen::showModalDialog));
        t->Start();
        t->Join();

        InitializeDice();
        startTimer();
}

System::Void MainScreen::showModalDialog() {
    GetReady ^ gr = gcnew GetReady();
    gr->showModalPopup();
}

以下是表单中的代码:

public:
    GetReady(void)
    {
        InitializeComponent();
    }

    System::Void showModalPopup() {
            this->Show();
            startTimer();

        }
private: System::Void timerPrep_Tick(System::Object^  sender, System::EventArgs^  e) {
         ts = ts->Subtract(TimeSpan(0, 0, 1));
         if (ts->TotalSeconds <= 0) {
             finishTimer();
         } else {
             lblTimer->Text = ts->ToString("mm\\:ss");
         }
    }

    System::Void startTimer() {
         array<String ^>^ minSec = gcnew array<String ^>(2);
         minSec = lblTimer->Text->Split(':');
         ts = gcnew TimeSpan(0, Convert::ToInt32(minSec[0]), Convert::ToInt32(minSec[1]));
         Thread::Sleep(900);
         timerPrep->Start();
     }

     System::Void finishTimer() {
         timerPrep->Stop();
         lblTimer->Text = "GO!!!";
         Thread::Sleep(900);
         this->Close();
     }

我理想的解决方案是使用线程生成新表单,这样主表单和弹出表单中的呈现都很流畅。

我尝试过的事情:

  1. 移动这个 - &gt; Show()每个我能想到的地方。
  2. 我添加了t-&gt; Join()以查看主线程是否正在尝试将主窗口重新聚焦。线程t仍然执行,计时器仍然使用Join正常运行,阻止用户输入五秒钟 - 但没有任何东西可以阻止屏幕。
  3. 我已经阅读了几个关于SO的问题,我认为最相关的问题是WinForms multithreading issue - 尽管我觉得这种情况可能有点过分。
  4. 如果您对我需要做什么有任何想法,以便为两种表格进行平滑渲染,请告诉我。谢谢!

2 个答案:

答案 0 :(得分:1)

问题是showModalPopup使用 Show 方法而不是 ShowDialog 。线程启动,显示表单(它不阻止执行),启动计时器,结束并加入主线程。创建表单的线程已经完成,而winforms多线程问题就在这里。

首先启动计时器,然后使用 ShowDialog 显示模态窗口。像这样:

System::Void showModalPopup() 
{            
   startTimer();
   this->ShowDialog();
}

答案 1 :(得分:1)

在工作线程上显示UI充满了陷阱和惊喜。你在这里遇到的主要问题是线程不会引发消息循环。首先要求确保窗口可以接收Windows消息并确保线程不会立即终止。除了Jairo建议使用ShowDialog()之外,样板解决方案是:

System::Void MainScreen::showModalDialog() {
    Application::Run(gcnew GetReady);
}

还有更多,显示窗口的线程应始终是STA线程。 COM的实现细节,需要获取剪贴板,拖放和shell对话框正常运行等内容:

Thread ^ t = gcnew Thread(gcnew ThreadStart(this, &MainScreen::showModalDialog));
t->SetApartmentState(ApartmentState::STA);    
t->Start();

并且您通常在主线程上显示的窗口存在Z顺序问题。你的窗口令人讨厌的习惯消失在主线所拥有的窗口后面。没有好的解决方案。