2013-04-11 105 views
0

我目前正在研究需要利用表单阻止用户使用主窗体几秒钟的应用程序。尽管在主线程上运行代码似乎没问题,但当窗体首次出现时,弹出窗体上的标签不会呈现大约一秒。我认为如果我在单独的线程上运行该表单,渲染将会更顺畅。渲染现在非常流畅,表单在渲染后立即消失。计时器设置为五秒钟,屏幕上的标签倒计时。下面是相关的代码调用新的线程和形式:WinForm在多线程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. 移动这个 - >显示()的每一个地方我能想到把它。
  2. 我已经添加了t-> Join()来查看主线程是否试图让主窗口回到焦点。线程t仍然会执行,定时器仍然可以通过Join来正常运行,阻止用户输入5秒 - 但没有任何东西可以阻止屏幕。
  3. 我已经阅读了几个关于这个问题的问题,我认为最相关的是WinForms multithreading issue - 尽管我觉得这可能是矫枉过正的情况。

如果您对我需要做的事情有任何想法才能让这两种表单顺利呈现,请让我知道。谢谢!

+0

您是否设置了定时器的_Interval_属性?它没有在示例代码中显示。 – Jairo 2013-04-11 09:53:09

+0

对不起 - 它已在表单设计器中设置为1000。 'this-> timerPrep-> Interval = 1000; \t \t \t this-> timerPrep-> Tick + = gcnew System :: EventHandler(this,&GetReady :: timerPrep_Tick);' – floppsb 2013-04-12 00:44:32

回答

1

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

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

还有更多,显示窗口线程应该永远是一个STA线程。实现细节COM和需要得到的东西,如剪贴板,拖动+下降和外壳对话框正确操作:

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

而且你通常有一个Z顺序问题与被显示在主线程上的窗口。随着你的窗户的讨厌习惯消失在主线程拥有的窗口后面。没有好的解决方案。

+0

Ups,我没有意识到消息循环和STA线程。你发布的每一个答案都是对我而言的新知识。感谢Hans。 – Jairo 2013-04-12 16:18:59

1

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

先启动计时器,然后用ShowDialog显示模式窗口。类似这样的:

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