Program Tip

UI 스레드에서 강제로 GUI 업데이트

programtip 2020. 11. 4. 08:18
반응형

UI 스레드에서 강제로 GUI 업데이트


WinForms에서 UI 스레드에서 즉시 UI 업데이트를 강제하는 방법은 무엇입니까?

내가하는 일은 대략 다음과 같습니다.

label.Text = "Please Wait..."
try 
{
    SomewhatLongRunningOperation(); 
}
catch(Exception e)
{
    label.Text = "Error: " + e.Message;
    return;
}
label.Text = "Success!";

작업 전에 레이블 텍스트가 "잠시 기다려주십시오 ..."로 설정되지 않습니다.

작업을 위해 다른 스레드를 사용하여이 문제를 해결했지만 털이 많아서 코드를 단순화하고 싶습니다.


처음에는 왜 OP가 응답 중 하나를 답으로 표시하지 않았는지 궁금했지만 직접 시도했지만 여전히 작동하지 않는 경우 조금 더 깊이 파고이 문제에 훨씬 더 많은 것이 있음을 발견했습니다. 추정.

유사한 질문을 읽으면 더 나은 이해를 얻을 수 있습니다. 프로세스 중간에 업데이트 / 새로 고침을 제어하지 않는 이유

마지막으로 기록을 위해 다음을 수행하여 레이블을 업데이트 할 수있었습니다.

private void SetStatus(string status) 
{
    lblStatus.Text = status;
    lblStatus.Invalidate();
    lblStatus.Update();
    lblStatus.Refresh();
    Application.DoEvents();
}

내가 이해하는 바에 따르면 이것은 우아하고 올바른 접근 방식과는 거리가 멀다. 스레드가 얼마나 바쁜지에 따라 작동하거나 작동하지 않을 수있는 해킹입니다.


호출 label.Invalidatelabel.Update()-일반적으로 업데이트는 현재 함수를 종료 한 후에 만 ​​발생하지만 Update를 호출하면 코드의 특정 위치에서 강제로 업데이트됩니다. 에서 MSDN :

Invalidate 메서드는 칠하거나 다시 칠할 항목을 제어합니다. Update 메서드는 페인팅 또는 다시 페인팅이 발생하는시기를 제어합니다. Refresh를 호출하는 대신 Invalidate 및 Update 메서드를 함께 사용하는 경우 다시 그리는 것은 사용하는 Invalidate의 오버로드에 따라 다릅니다. Update 메서드는 컨트롤이 즉시 그려 지도록 강제하지만 Invalidate 메서드는 Update 메서드를 호출 할 때 그려지는 내용을 제어합니다.


Application.DoEvents()레이블을 설정 한 후 호출 하지만 대신 별도의 스레드에서 모든 작업을 수행해야 사용자가 창을 닫을 수 있습니다.


나는 방금 같은 문제를 우연히 발견하고 흥미로운 정보를 찾았고 2 센트를 넣고 여기에 추가하고 싶었습니다.

우선, 다른 사람들이 이미 언급했듯이 장기 실행 작업은 백그라운드 작업자, 명시 적 스레드, 스레드 풀의 스레드 또는 작업 (.Net 4.0 이후) 일 수있는 스레드에 의해 수행되어야합니다. Stackoverflow 570537 : update-label-while-processing-in-windows-forms 를 사용하여 UI가 계속 응답하도록합니다.

그러나 짧은 작업의 경우 당연히 아프지는 않지만 스레딩이 실제로 필요하지 않습니다.

이 문제를 분석하기 위해 하나의 단추와 하나의 레이블로 winform을 만들었습니다.

System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
{
  label1->Text = "Start 1";
  label1->Update();
  System::Threading::Thread::Sleep(5000); // do other work
}

내 분석은 코드를 건너 뛰고 (F10 사용) 무슨 일이 일어 났는지 확인하는 것이었다. 이 기사를 읽은 후 WinForms의 멀티 스레딩에서 흥미로운 것을 발견했습니다. 이 기사는 첫 페이지의 맨 아래에 현재 실행중인 함수가 완료 될 때까지 UI 스레드가 UI를 다시 그릴 수없고 잠시 후 Windows에서 "응답하지 않음"으로 창을 표시 할 수 있다고 설명합니다. 나는 또한 그것을 단계별로 진행하는 동안 위에서 내 테스트 응용 프로그램에서 그것을 발견했지만 특정 경우에만 해당됩니다.

(다음 테스트의 경우 Visual Studio를 전체 화면으로 설정하지 않는 것이 중요합니다. 그 옆에 작은 응용 프로그램 창을 동시에 볼 수 있어야합니다. 디버깅을 위해 Visual Studio 창과 응용 프로그램 창에서 어떤 일이 발생하는지 확인하십시오. 응용 프로그램을 시작하고에 중단 점을 설정 한 label1->Text ...다음 VS 창 옆에 응용 프로그램 창을 놓고 VS 창 위에 마우스 커서를 놓습니다.)

  1. 앱 시작 후 VS를 한 번 클릭하고 (포큐를 거기에 놓고 스테핑을 활성화하기 위해) 마우스를 움직이지 않고 단계별로 진행하면 새 텍스트가 설정되고 update () 함수에서 레이블이 업데이트됩니다. 즉, UI가 분명히 다시 그려집니다.

  2. When I step over the first line, then move the mouse around a lot and click somewhere, then step further, the new text is likely set and the update() function is called, but the UI is not updated/repainted and the old text remains there until the button1_click() function finishes. Instead of repainting, the window is marked as "not responsive"! It also doesn't help to add this->Update(); to update the whole form.

  3. 추가 Application::DoEvents();하면 UI가 업데이트 / 다시 칠할 수 있습니다. 어쨌든 사용자가 버튼을 누르거나 UI에서 허용되지 않는 다른 작업을 수행 할 수 없도록주의해야합니다 !! 따라서 : DoEvents ()를 피하십시오! , 더 나은 스레딩을 사용하십시오 (.Net에서 매우 간단하다고 생각합니다).
    그러나 ( @Jagd, Apr 2 '10 at 19:25 ) .refresh().invalidate().

My explanations is as following: AFAIK winform still uses the WINAPI function. Also MSDN article about System.Windows.Forms Control.Update method refers to WINAPI function WM_PAINT. The MSDN article about WM_PAINT states in its first sentence that the WM_PAINT command is only sent by the system when the message queue is empty. But as the message queue is already filled in the 2nd case, it is not send and thus the label and the application form are not repainted.

<>joke> Conclusion: so you just have to keep the user from using the mouse ;-) <>/joke>


you can try this

using System.Windows.Forms; // u need this to include.

MethodInvoker updateIt = delegate
                {
                    this.label1.Text = "Started...";
                };
this.label1.BeginInvoke(updateIt);

See if it works.


After updating the UI, start a task to perform with the long running operation:

label.Text = "Please Wait...";

Task<string> task = Task<string>.Factory.StartNew(() =>
{
    try
    {
        SomewhatLongRunningOperation();
        return "Success!";
    }
    catch (Exception e)
    {
        return "Error: " + e.Message;
    }
});
Task UITask = task.ContinueWith((ret) =>
{
    label.Text = ret.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());

This works in .NET 3.5 and later.


It's very tempting to want to "fix" this and force a UI update, but the best fix is to do this on a background thread and not tie up the UI thread, so that it can still respond to events.


Try calling label.Invalidate()

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate(VS.80).aspx


Think I have the answer, distilled from the above and a little experimentation.

progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;

I tried decrementing the value and the screen updated even in debug mode, but that would not work for setting progressBar.Value to progressBar.Maximum, because you cannot set the progress bar value above the maximum, so I first set the progressBar.Value to progressBar.Maximum -1, then set progressBar.Maxiumum to equal progressBar.Value. They say there is more than one way of killing a cat. Sometimes I'd like to kill Bill Gates or whoever it is now :o).

With this result, I did not even appear to need to Invalidate(), Refresh(), Update(), or do anything to the progress bar or its Panel container or the parent Form.


If you only need to update a couple controls, .update() is sufficient.

btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly

I had the same problem with property Enabled and I discovered a first chance exception raised because of it is not thread-safe. I found solution about "How to update the GUI from another thread in C#?" here https://stackoverflow.com/a/661706/1529139 And it works !

참고URL : https://stackoverflow.com/questions/1360944/force-gui-update-from-ui-thread

반응형