【问题标题】:WinForm Disappears in Multithreaded C++/CLI ApplicationWinForm 在多线程 C++/CLI 应用程序中消失
【发布时间】:2013-04-11 03:20:08
【问题描述】:

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

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. 将 this->Show() 移动到任何我能想到的地方。
  2. 我添加了 t->Join() 来查看主线程是否试图使主窗口重新聚焦。线程 t 仍在执行,并且计时器仍然与 Join 一起正常运行,阻止用户输入 5 秒钟 - 但没有任何东西可以阻止屏幕。
  3. 我已经阅读了一些关于 SO 的问题,我认为最相关的问题是 WinForms multithreading issue - 尽管我觉得对于这种情况来说这可能是矫枉过正。

如果您对我需要做些什么来实现两种表单的平滑渲染有任何想法,请告诉我。谢谢!

【问题讨论】:

  • 你设置了timer的Interval属性吗?示例代码中未显示。
  • 抱歉 - 在表单设计器中设置为 1000。 this-&gt;timerPrep-&gt;Interval = 1000; this-&gt;timerPrep-&gt;Tick += gcnew System::EventHandler(this, &amp;GetReady::timerPrep_Tick);

标签: multithreading winforms c++-cli


【解决方案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 顺序问题。一个讨厌的习惯是你的窗口消失在主线程拥有的窗口后面。没有好的解决方案。

【讨论】:

  • Ups,我没有意识到消息循环和 STA 线程。你发布的每一个答案对我来说都是一个新的知识。谢谢汉斯。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-23
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多