안녕하세요~ 민행복 홍환민입니다.

TWinControl에 보면 더블버퍼드 (DoubleBuffered) 프로퍼티가 추가되어 있습니다.
컴포넌트가 깜빡이는 문제를 해결하기 위한 프로퍼티인데요.
(뭐 더블버퍼드만으로 해결이 안되는 문제들도 있지만..)

이 속성이 어떻게 구현되어 있는지 궁금해서 VCL 소스를 열어봤습니다.
(VCL에 대해 공부하는 가장 좋은 방법은 역시 VCL 소스를 분석해 보는 방법인 것 같습니다.)

다음은 더블버퍼드 속성의 소스입니다.

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
  PaintBuffer: HPAINTBUFFER;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited
    else
      PaintHandler(Message);
  end
  else
  begin
    if DwmCompositionEnabled then
    begin
      DC := BeginPaint(Handle, PS);
      try
        PaintBuffer := BeginBufferedPaint(DC, PS.rcPaint, BPBF_COMPOSITED, nil, MemDC);
        if PaintBuffer <> 0 then
          try
            Perform(WM_ERASEBKGND, MemDC, MemDC);
            Perform(WM_PRINTCLIENT, MemDC, PRF_CLIENT);
            if not (Self is TCustomForm) then
              BufferedPaintMakeOpaque(PaintBuffer, @PS.rcPaint);
          finally
            EndBufferedPaint(PaintBuffer, True);
          end;
      finally
        EndPaint(Handle, PS);
      end;
    end
    else
    begin
      DC := BeginPaint(Handle, PS);
      MemBitmap := CreateCompatibleBitmap(DC, PS.rcPaint.Right - PS.rcPaint.Left,
        PS.rcPaint.Bottom - PS.rcPaint.Top);
      try
        MemDC := CreateCompatibleDC(DC);
        OldBitmap := SelectObject(MemDC, MemBitmap);
        try
          SetWindowOrgEx(MemDC, PS.rcPaint.Left, PS.rcPaint.Top, nil);
          Perform(WM_ERASEBKGND, MemDC, MemDC);
          Message.DC := MemDC;
          WMPaint(Message);
          Message.DC := 0;
          BitBlt(DC, PS.rcPaint.Left, PS.rcPaint.Top,
            PS.rcPaint.Right - PS.rcPaint.Left,
            PS.rcPaint.Bottom - PS.rcPaint.Top,
            MemDC,
            PS.rcPaint.Left, PS.rcPaint.Top,
            SRCCOPY);
        finally
          SelectObject(MemDC, OldBitmap);
        end;
      finally
        EndPaint(Handle, PS);
        DeleteDC(MemDC);
        DeleteObject(MemBitmap);
      end;
    end;
  end;
end;

보시다시피 더블퍼버드 속성이 지정되어 있으면, WM_PAINT 메시지가 발생했을때, 그리기 연산을 바로 컴포넌트의 DC에 하는 것이 아니라, 메모리 비트맵을 만들어서 그 DC에 다 그린 다음에 마지막으로 컴포넌트의 DC에 그리게 됩니다.

예를 들어 그림을 그리는 과정을 동영상으로 촬영해서 보여주는게 아니라, 다 그린 다음에 완성작을 사진으로 찍어 보여주는 것과 같은 원리입니다. 중간과정이 보이기 때문에 깜빡 거린다고 느껴지는 것입니다.

그리고 또한, 깜빡 거리는 문제의 핵심 원인 중 하나는 WM_ERASEBKGND 메시지인데요. (Erase Background, 배경을 지운다.)
WM_PAINT 메시지는 그 다음에 발생합니다.

예를 들면, 화면을 지운 다음에 (청소한 다음에), 그 위에 그림을 그리는 거지요.
기본 구현은 지정된 색상으로 화면을 칠하는 것이지요.
화면을 칠하다 보니까 깜빡이는 것처럼 보이는 것입니다.

뭐 결론적으로 더블버퍼드 속성을 이용하면, 배경을 지우는 것 뿐만 아니라, 그 위에 그림을 그리는 과정까지 눈에 보이지 않는 메모리 비트맵에서 처리가 되고 최종 결과만 한번에 딱 그려주기 때문에 깜빡이는 문제가 대부분 해결되는 것입니다.

그런데, 단점이 있죠. 화면이 다시 그려지는 것이 좀 느리게 느껴질 수도 있습니다. 구현 원리상 어쩔 수 없는 부분이지요.
그럼 이만.

P.S.> 지금 제가 만드는 프로그램의 깜빡임 문제를 해결하다가 이 글을 작성하는 건데요.
대부분 잡았는데 한두개가 참 안잡히네요...흑흑..
깜빡이는 문제. 이거 별거 아닌데도 막상 잡으려는 참 귀찮고... ㅠㅠ

P.S.2> 또한 ParentBackground 라는 속성도 깜빡임과 관련이 깊습니다.
왜냐하면, 부모의 백그라운드를 사용한다는 것이기 때문에 부모가 다시 그려지면, 자신도 다시 그려져야 하기 때문입니다. 그러므로 필요없다면 False로 설정해 주는 것이 좋습니다. (기본값이 True인 듯 싶습니다.)

P.S.3> P.S. 에서 말한 버그는 잡았습니다. 우하하... 근데 또다른 버그가... 쩝 ㅡㅡ;
TRichEdit는 DoubleBuffered 속성을 True로 설정하면 아에 내용이 안나오는군요... 뭐지..

P.S.4>
Note: Some controls, such as TRichEdit, can't paint themselves into a bitmap. For such controls, DoubleBuffered must be set to false.
P.S.3 에서 언급한 문제가 퀄리티센트럴 (코드기어의 버그보고시스템)에도 보고가 되었는데, 답변이 '원래 디자인된 대로 동작하는거다. 도움말을 봐라' 라고 되어있더군요. 그래서 DoubleBuffered 속성의 도움말을 보니 위와 같이 나오는군요. 결국 TRichEidt도 윈도우에서 제공하는 컨트롤을 랩핑해 놓은 것인데, 어떻게 할 수 있는 방법이 없나 보네요. 흠냐 흑흑

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_24452953.html
위에 링크를 보면.. TRichEdit의 깜빡임을 해결하는 방법이라는데.. 그냥 페이크를 쓰는거군요. 리치에디트 내용을 캡춰해서 뿌려주는 식.. ㅡㅡ; 흠... 저런 방법까지 쓰고 싶진 않군요. 그냥 TRichEdit의 깜빡임은 어쩔수 없나봅니다 ㅡㅡ;

행복한 하루 되세염~