如何通过在 MFC 对话框中按下按钮来停止 while 循环
How to stop while loop by pressing a button in MFC dialog
我正在 Visual Studio 2017 年使用 C++ 开发基于对话框的 MFC 应用程序。
在我的应用程序中,我有一个显示文本的 window。当我按下“开始”按钮时,我正在使用无限循环向其中写入数字,当我按下“停止”按钮时,我想停止这个过程。 window 看起来像这样:
为此,在 Start
按钮处理程序中,我使用 while 循环进行计数并在应用程序的 window 中打印计数器。每次进入 while 循环时,我都会检查 ICD_BUTTON2
(停止按钮 ID)是否像这样按下:
void CEditableListControlDlg::OnBnClickedButton1()
{
int counter = 0;
while ((WM_COMMAND != IDC_BUTTON2)) {
counter++;
m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
}
}
我没有在 Stop
按钮处理程序中输入任何代码。
但是我的应用程序停止响应,我需要使用任务管理器将其关闭。可能是什么问题呢?也许这不是检查是否收到按钮单击消息的正确方法?
谢谢。
这里的根本是你阻塞了主线程。即使您更正了停止按钮上的错误检查,您也不会得到预期的结果。原因是当一个事件被触发时,一个按钮被按下,主线程需要处理那个事件。您的循环妨碍了 why the suggestion of using a timer。这就是为什么你必须终止进程才能结束它。
如果你真的需要做一些非常激烈的事情,那么可能需要一个话题,但我不认为那是你的 objective。
//在.h
UINT_PTR timer;
//在.cpp中
//initialize timer to null in constructor or CEditableListControlDlg::Create
//you may want to disable the this button while the timer is active
//but don't start another timer if active!
void CEditableListControlDlg::OnBnClickedButton1()
{
if(timer)
return;
//200 millisecond events.
timer= SetTimer( ID_TIMER, 200, NULL );
}
void CEditableListControlDlg::OnTimer( UINT nIDEvent )
{
//if you have more than one timer, if(nIDEvent == ID_TIMER)...;
counter++;
m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
CWnd::OnTimer( nIDEvent );
}
//you may want to disable the this button while the timer is inactive
void CEditableListControlDlg::OnBnClickedButton2()
{
if(timer)
KillTimer(timer);
timer= nullptr;
}
我看不出计时器如何解决在后台处理某些内容时允许 UI 操作的问题。计时器方式将最多处理帽子计时器事件的一个工作单元。
一个单独的线程是一个合适的解决方案,但对于这种情况来说可能太多了。
中间方法是在繁忙的 while
循环中“推送”消息,详情请参阅:
https://docs.microsoft.com/en-us/cpp/mfc/idle-loop-processing?view=vs-2019
每个 GUI 程序在某个地方都有一个消息循环,它检查来自 OS 的传入消息并对其做出反应。通过自己创建无限循环,您可以阻止这种情况的发生——这就是为什么您需要任务管理器来关闭程序。它还会阻止诸如 window 重绘之类的事情。
MFC 提供了一个 OnIdle
函数,当消息循环无事时调用该函数。您可以使用此功能进行后台工作,例如递增计数器。有关详细信息,请参阅 Microsoft 的文档 Idle Loop Processing。
要允许主消息循环处理 GUI 事件,例如单击“停止”按钮,请在您的 while
循环中添加并调用此函数 PumpMessages()
:
void CEditableListControlDlg::PumpMessages()
{
// Must call Create() before using the dialog
ASSERT(m_hWnd!=NULL);
MSG msg;
// Handle dialog messages
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(!IsDialogMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
我正在 Visual Studio 2017 年使用 C++ 开发基于对话框的 MFC 应用程序。
在我的应用程序中,我有一个显示文本的 window。当我按下“开始”按钮时,我正在使用无限循环向其中写入数字,当我按下“停止”按钮时,我想停止这个过程。 window 看起来像这样:
为此,在 Start
按钮处理程序中,我使用 while 循环进行计数并在应用程序的 window 中打印计数器。每次进入 while 循环时,我都会检查 ICD_BUTTON2
(停止按钮 ID)是否像这样按下:
void CEditableListControlDlg::OnBnClickedButton1()
{
int counter = 0;
while ((WM_COMMAND != IDC_BUTTON2)) {
counter++;
m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
}
}
我没有在 Stop
按钮处理程序中输入任何代码。
但是我的应用程序停止响应,我需要使用任务管理器将其关闭。可能是什么问题呢?也许这不是检查是否收到按钮单击消息的正确方法?
谢谢。
这里的根本是你阻塞了主线程。即使您更正了停止按钮上的错误检查,您也不会得到预期的结果。原因是当一个事件被触发时,一个按钮被按下,主线程需要处理那个事件。您的循环妨碍了 why the suggestion of using a timer。这就是为什么你必须终止进程才能结束它。
如果你真的需要做一些非常激烈的事情,那么可能需要一个话题,但我不认为那是你的 objective。
//在.h
UINT_PTR timer;
//在.cpp中
//initialize timer to null in constructor or CEditableListControlDlg::Create
//you may want to disable the this button while the timer is active
//but don't start another timer if active!
void CEditableListControlDlg::OnBnClickedButton1()
{
if(timer)
return;
//200 millisecond events.
timer= SetTimer( ID_TIMER, 200, NULL );
}
void CEditableListControlDlg::OnTimer( UINT nIDEvent )
{
//if you have more than one timer, if(nIDEvent == ID_TIMER)...;
counter++;
m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
CWnd::OnTimer( nIDEvent );
}
//you may want to disable the this button while the timer is inactive
void CEditableListControlDlg::OnBnClickedButton2()
{
if(timer)
KillTimer(timer);
timer= nullptr;
}
我看不出计时器如何解决在后台处理某些内容时允许 UI 操作的问题。计时器方式将最多处理帽子计时器事件的一个工作单元。
一个单独的线程是一个合适的解决方案,但对于这种情况来说可能太多了。
中间方法是在繁忙的 while
循环中“推送”消息,详情请参阅:
https://docs.microsoft.com/en-us/cpp/mfc/idle-loop-processing?view=vs-2019
每个 GUI 程序在某个地方都有一个消息循环,它检查来自 OS 的传入消息并对其做出反应。通过自己创建无限循环,您可以阻止这种情况的发生——这就是为什么您需要任务管理器来关闭程序。它还会阻止诸如 window 重绘之类的事情。
MFC 提供了一个 OnIdle
函数,当消息循环无事时调用该函数。您可以使用此功能进行后台工作,例如递增计数器。有关详细信息,请参阅 Microsoft 的文档 Idle Loop Processing。
要允许主消息循环处理 GUI 事件,例如单击“停止”按钮,请在您的 while
循环中添加并调用此函数 PumpMessages()
:
void CEditableListControlDlg::PumpMessages()
{
// Must call Create() before using the dialog
ASSERT(m_hWnd!=NULL);
MSG msg;
// Handle dialog messages
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(!IsDialogMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}