Application.DoEvents () 사용


272

Application.DoEvents()C #에서 사용할 수 있습니까 ?

이 함수는 VB6과 거의 같은 방식으로 GUI가 나머지 앱을 따라 잡을 수있는 방법 DoEvents입니까?


35
DoEventsC # 언어가 아닌 Windows Forms의 일부입니다. 따라서 모든 .NET 언어에서 사용할 있습니다. 그러나 .NET 언어에서는 사용 해서는 안됩니다 .
Stephen Cleary

3
2017 년입니다. Async / Await를 사용하십시오. 자세한 내용은 @hansPassant 답변의 끝 부분을 참조하십시오.
FastAl

답변:


468

Hmya, DoEvents ()의 지속적인 신비. 그것에 반발하는 엄청난 양의 반발이 있었지만, 왜 그것이 "나쁜"이유를 실제로 설명하지는 못했습니다. "구조를 변경하지 마십시오"와 같은 종류의 지혜. 음, 왜 런타임과 언어가 구조체가 너무 나쁘면 돌연변이를 지원합니까? 같은 이유로 : 당신이 제대로하지 않으면 발에 자신을 쏴. 용이하게. 그리고 올바르게 수행하려면 DoEvents ()의 경우 확실히 이해하기 쉽지 않은 일을 정확히 알아야합니다.

거의 모든 Windows Forms 프로그램에는 실제로 DoEvents ()에 대한 호출이 포함되어 있습니다. 그러나 ShowDialog ()라는 다른 이름으로 현명하게 위장되어 있습니다. 응용 프로그램의 나머지 창을 멈추지 않고 대화 상자를 모달 할 수있는 것은 DoEvents ()입니다.

대부분의 프로그래머는 자신의 모달 루프를 작성할 때 DoEvents를 사용하여 사용자 인터페이스가 정지되는 것을 막기를 원합니다. 확실히 그렇게합니다. Windows 메시지를 전달하고 페인트 요청을 전달합니다. 그러나 문제는 선택적이지 않다는 것입니다. 페인트 메시지를 발송할뿐만 아니라 다른 모든 것을 전달합니다.

그리고 문제를 일으키는 일련의 알림이 있습니다. 모니터 앞 약 3 피트에서 나옵니다. 예를 들어 DoEvents ()를 호출하는 루프가 실행되는 동안 사용자는 기본 창을 닫을 수 있습니다. 작동하지만 사용자 인터페이스는 사라졌습니다. 그러나 코드가 멈추지 않았으며 여전히 루프를 실행 중입니다. 그 나쁜. 아주 아주

더 많은 것 : 사용자는 동일한 루프를 시작시키는 동일한 메뉴 항목이나 버튼을 클릭 할 수 있습니다. 이제 DoEvents ()를 실행하는 두 개의 중첩 루프가 있으며 이전 루프가 일시 중단되고 새 루프가 처음부터 시작됩니다. 그것은 효과가 있지만 소년은 가능성이 적습니다. 특히 중첩 루프가 종료되고 일시 중단 된 루프가 재개되면 이미 완료된 작업을 완료하려고합니다. 그것이 예외로 폭발하지 않는다면, 데이터는 모두 지옥에 빠지게됩니다.

ShowDialog ()로 돌아갑니다. DoEvents ()를 실행하지만 다른 작업을 수행한다는 점에 유의하십시오. 이 응용 프로그램의 모든 창 비활성화 대화 이외. 3 피트 문제가 해결되었으므로 사용자는 논리를 어지럽히 기 위해 아무것도 할 수 없습니다. 창 닫기 및 다시 시작 실패 모드가 모두 해결되었습니다. 또는 달리 말하면 사용자가 프로그램을 다른 순서로 코드를 실행할 수있는 방법은 없습니다. 코드를 테스트 할 때와 마찬가지로 예상대로 실행됩니다. 대화가 매우 성가시다. 대화 상자가 활성화되어 있고 다른 창에서 무언가를 복사하여 붙여 넣을 수없는 것을 싫어하는 사람은 누구입니까? 그러나 가격입니다.

코드에서 DoEvents를 안전하게 사용하려면 다음이 필요합니다. 모든 양식의 Enabled 속성을 false로 설정하면 문제를 피하는 빠르고 효율적인 방법입니다. 물론 어떤 프로그래머도 실제로 이것을하는 것을 좋아하지 않습니다. 그리고 그렇지 않습니다. 그래서 DoEvents ()를 사용해서는 안됩니다. 스레드를 사용해야합니다. 그들은 당신에게 화려하고 엉성한 방법으로 발을 쏠 수있는 완벽한 무기고를 제공하지만. 그러나 자신의 발만 쏠 수 있다는 이점이 있습니다. (일반적으로) 사용자가 그녀를 쏘지 못하게합니다.

다음 버전의 C # 및 VB.NET은 새로운 await 및 async 키워드를 사용하여 다른 총을 제공합니다. DoEvents 및 스레드로 인한 문제에서 비롯된 부분이지만 비동기 작업이 진행되는 동안 UI를 최신 상태로 유지 해야하는 WinRT의 API 설계에서 많은 부분이 영감을 받았습니다 . 파일에서 읽는 것과 같습니다.


1
그러나 이것은 빙산의 일각에 불과합니다. Application.DoEvents코드에 UDP 기능을 추가 할 때까지 문제없이 사용하여 여기에 설명 된 문제가 발생했습니다 . 나는 주위에 방법이 있는지 알고 싶어요 와는 DoEvents.
darda

이것은 좋은 대답이며, 내가 놓친 느낌은 실행 및 스레드 안전 설계 또는 일반적으로 타임 슬라이싱에 대한 설명 / 토론입니다. 그러나 그것은 다시 텍스트보다 3 배나 더 쉽습니다. :)
jheriko 2016 년

5
일반적으로 "사용 스레드"보다 더 실용적인 솔루션의 이점이 있습니다. 예를 들어, BackgroundWorker구성 요소는 발자국의 화려한 결과를 피하면서 스레드를 관리하며 최첨단 C # 언어 버전을 필요로하지 않습니다.
Ben Voigt

29

가능하지만 해킹입니다.

DoEvents 이블입니까?를 참조하십시오 . .

에서 직접 은 MSDN 페이지 것을 thedev가 참조 :

이 메소드를 호출하면 모든 대기 창 메시지가 처리되는 동안 현재 스레드가 일시 중단됩니다. 메시지로 인해 이벤트가 트리거되면 응용 프로그램 코드의 다른 영역이 실행될 수 있습니다. 이로 인해 응용 프로그램에서 디버깅하기 어려운 예기치 않은 동작이 발생할 수 있습니다. 시간이 오래 걸리는 작업이나 계산을 수행하는 경우 새 스레드에서 해당 작업을 수행하는 것이 종종 바람직합니다. 비동기 프로그래밍에 대한 자세한 내용은 비동기 프로그래밍 개요를 참조하십시오.

따라서 Microsoft는 사용에 대해 경고합니다.

또한, 나는 그 행동이 예측할 수없고 부작용이 발생하기 때문에 해킹으로 간주합니다 (이것은 새로운 스레드를 돌리거나 백그라운드 작업자를 사용하는 대신 DoEvents를 사용하려는 경험에서 비롯됩니다).

여기에 마치 치모가 없습니다. 강력한 솔루션으로 작동하면 모든 것이 끝났습니다. 그러나 .NET에서 DoEvents를 사용하려고하면 고통이 없습니다.


1
해당 게시물은 2004 년 .NET 2.0 이전의 BackgroundWorker것이며 "올바른"방법을 단순화하는 데 도움이되었습니다.
Justin

동의했다. 또한 .NET 4.0의 작업 라이브러리도 좋습니다.
RQDQ

1
Microsoft가 종종 필요한 목적을 위해 기능을 제공했다면 왜 해킹일까요?
Craig Johnston

1
@Craig Johnston-DoEvents가 해커 범주에 속한다고 생각하는 이유를 더 자세히 설명하기 위해 답변을 업데이트했습니다.
RQDQ

그 코딩 공포 기사는 "DoEvents spackle"이라고 불렀습니다. 훌륭한!
gonzobrains

24

예. System.Windows.Forms 네임 스페이스의 Application 클래스에는 정적 DoEvents 메서드가 있습니다. UI 스레드에서 장기 실행 작업을 수행 할 때 UI 스레드의 큐에서 대기중인 메시지를 처리하는 데 System.Windows.Forms.Application.DoEvents ()를 사용할 수 있습니다. 이는 긴 작업이 실행되는 동안 UI가보다 반응 적으로 보이고 "고정"되지 않는 이점이 있습니다. 그러나 이것은 거의 항상 일을하는 가장 좋은 방법은 아닙니다. DoEvents를 호출하는 Microsoft에 따르면 "... 모든 대기 창 메시지가 처리되는 동안 현재 스레드가 일시 중지됩니다." 이벤트가 트리거되면 추적하기 어려운 예기치 않은 버그와 간헐적 인 버그가 발생할 수 있습니다. 광범위한 작업이있는 경우 별도의 스레드에서 수행하는 것이 훨씬 좋습니다. 별도의 스레드에서 긴 작업을 실행하면 UI가 계속 원활하게 실행되는 것을 방해하지 않고 처리 할 수 ​​있습니다. 보기자세한 내용은 여기 를 참조하십시오.

다음 은 DoEvents 사용 방법의 예입니다. Microsoft는 또한 사용에 대해주의를 기울입니다.


13

내 경험상 .NET에서 DoEvents를 사용하는 데 큰주의를 기울일 것입니다. DataGridViews를 포함하는 TabControl에서 DoEvents를 사용할 때 매우 이상한 결과가 발생했습니다. 반면에, 당신이 다루는 모든 것이 진행률 표시 줄이있는 작은 양식이라면 괜찮을 것입니다.

결론은 DoEvents를 사용하려는 경우 응용 프로그램을 배포하기 전에 철저히 테스트해야합니다.


좋은 대답이지만, 진행률 표시 줄에 대한 해결책을 제안 할 수 있다면 별도의 스레드에서 작업을 수행하고 진행률 표시기를 휘발성, 연동 변수에서 사용할 수 있도록하고 타이머에서 진행률 표시 줄을 새로 고 칩니다 . 이런 식으로 유지 보수 코더가 루프에 무거운 코드를 추가하려는 유혹을받지 않습니다.
mg30rg

1
UI 프로세스가 많고 UI를 잠그고 싶지 않은 경우 DoEvents 또는 이와 동등한 항목을 피할 수 없습니다. 첫 번째 옵션은 많은 UI 처리를 수행하지 않는 것이지만 코드를 유지 관리하기가 쉽지 않습니다. 그러나 Dispatcher.Yield ()는 DoEvents와 매우 유사한 작업을 수행하며 본질적으로 모든 의도와 목적을 위해 화면을 잠그는 UI 프로세스가 비동기 적으로 이루어 지도록 할 수 있습니다.
Melbourne Developer 5

11

예.

그러나를 사용해야하는 Application.DoEvents경우 이는 대부분 응용 프로그램 디자인이 잘못되었음을 나타냅니다. 아마도 별도의 스레드에서 일부 작업을 수행하고 싶습니까?


3
다른 스레드에서 작업이 완료 될 때까지 회전하고 기다릴 수 있도록하려면 어떻게해야합니까?
jheriko

@jheriko 그러면 async-await를 시도해야합니다.
mg30rg

5

위의 jheriko의 의견을 보았고 처음에 다른 스레드에서 오래 실행되는 비동기 코드 조각이 완료되기를 기다리는 기본 UI 스레드를 돌리면 DoEvents를 사용하지 않는 방법을 찾을 수 없다는 데 동의했습니다. 그러나 Matthias의 답변에서 UI의 작은 패널을 간단하게 새로 고침하면 DoEvents를 대체 할 수 있습니다 (불쾌한 부작용을 피할 수 있음).

내 사건에 대한 자세한 내용 ...

오랫동안 실행되는 SQL 명령 중에 진행률 표시 줄 유형 시작 화면 ( "로드 중"오버레이 표시 방법 ... )이 업데이트 되도록 다음을 수행했습니다 ( 여기 제안 ) .

IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10); 
      Application.DoEvents();  //to make the UI responsive
}

나쁜 점 : DoEvents를 호출하면 TopMost를 만든 경우에도 스플래시 화면 뒤의 폼에서 마우스 클릭이 발생하는 경우가있었습니다.

좋은 답변 : 시작 화면 중앙에있는 작은 패널에 대한 간단한 Refresh 호출로 DoEvents 행을 바꾸십시오 FormSplash.Panel1.Refresh(). UI가 멋지게 업데이트되고 다른 사람들이 경고 한 DoEvents의 이상 함이 사라졌습니다.


3
그러나 Refresh는 창을 업데이트하지 않습니다. 사용자가 데스크탑에서 다른 창을 선택하면 창을 다시 클릭해도 아무런 영향을 미치지 않으며 OS는 응용 프로그램을 응답하지 않는 것으로 표시합니다. DoEvents ()는 메시징 시스템을 통해 OS와 상호 작용하므로 새로 고치기 이상의 기능을 수행합니다.
ThunderGr

4

"DoEvents-Hack"을 사용하여 많은 상용 응용 프로그램을 보았습니다. 특히 렌더링이 시작될 때 나는 종종 이것을 본다 :

while(running)
{
    Render();
    Application.DoEvents();
}

그들은 모두 그 방법의 악에 대해 알고 있습니다. 그러나 그들은 다른 해결책을 모르기 때문에 핵을 사용합니다. 여기에서 가져온 몇 가지 방법입니다 블로그 게시물 에 의해 톰 밀러 :

  • WmPaint에서 모든 드로잉이 발생하도록 양식을 설정하고 렌더링을 수행하십시오. OnPaint 메서드가 끝나기 전에이 작업을 수행해야합니다 .Invalidate (); 이로 인해 OnPaint 메서드가 즉시 다시 시작됩니다.
  • P / Win32 API를 호출하고 PeekMessage / TranslateMessage / DispatchMessage를 호출하십시오. (사건은 실제로 비슷한 것을 수행하지만 추가 할당 없이이 작업을 수행 할 수 있습니다).
  • CreateWindowEx를 둘러싼 작은 래퍼 인 자체 폼 클래스를 작성하고 메시지 루프를 완벽하게 제어하십시오. DoEvents 메소드가 제대로 작동하는지 확인하고이를 준수하십시오.


3

DoEvents를 사용하면 사용자가 클릭하거나 다른 이벤트를 입력하고 다른 이벤트를 트리거 할 수 있으며 백그라운드 스레드가 더 나은 방법입니다.

그러나 플러시 이벤트 메시지가 필요한 문제가 여전히 발생할 수 있습니다. 처리 할 대기열에 메시지가있을 때 RichTextBox 컨트롤이 ScrollToCaret () 메서드를 무시하는 문제가 발생했습니다.

다음 코드는 DoEvents를 실행하는 동안 모든 사용자 입력을 차단합니다.

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Integrative.Desktop.Common
{
    static class NativeMethods
    {
        #region Block input

        [DllImport("user32.dll", EntryPoint = "BlockInput")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);

        public static void HoldUser()
        {
            BlockInput(true);
        }

        public static void ReleaseUser()
        {
            BlockInput(false);
        }

        public static void DoEventsBlockingInput()
        {
            HoldUser();
            Application.DoEvents();
            ReleaseUser();
        }

        #endregion
    }
}

1
doevents를 호출 할 때 항상 이벤트를 차단해야합니다. 그렇지 않으면 앱의 다른 이벤트가 응답으로 시작되고 앱이 한 번에 두 가지 작업을 시작할 수 있습니다.
FastAl

2

그래픽 처리 이외의 다른 메시지가 메시지 큐에있는 경우 Application.DoEvents가 문제를 일으킬 수 있습니다.

시간이 걸리는 경우 진행률 표시 줄을 업데이트하고 MainForm 생성 및로드와 같은 진행 상황을 사용자에게 알리는 데 유용 할 수 있습니다.

최근에 만든 응용 프로그램에서 DoEvents를 사용하여 MainForm의 생성자에서 코드 블록이 실행될 때마다 로딩 화면의 일부 레이블을 업데이트했습니다. 이 경우 UI 스레드는 SendAsync () 호출을 지원하지 않는 SMTP 서버에서 전자 메일을 보내는 데 사용되었습니다. 아마도 Begin () 및 End () 메서드를 사용하여 다른 스레드를 만들고 Send ()를 호출 할 수는 있지만 해당 메서드는 오류가 발생하기 쉽고 건설 중에 예외가 발생하지 않는 응용 프로그램의 기본 형식을 선호합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.