프로그래스바 중에 언제 끝날지 모르는 작업에 대하여 그냥 흐르는 프로그래스바를 간혹가다 보게 됩니다.
쉬운 예로 윈도우 XP, Vista가 부팅될 때 지렁이가 지나가는 그런 프로그래스바죠.
Win32 공용컨트롤 6.0 에는 그런 마퀴 형태의 프로그래바를 지원하는데 MFC 의 CProgressCtrl에서는 그런 마퀴 관련 API가 노출되어 있지 않습니다.

실제로 프로그래스바 공용 컨트롤이 어떻게 구현되어 있는지 wine 을 통해 살펴봅시다.
아래의 소스는 wine 에 구현되어 있는 progress common control 의 일부입니다.

  338 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)

  339 {

  340     int barSize;

  341     DWORD dwStyle;

  342     BOOL barSmooth;

  343     const ProgressDrawProc* drawProcs;

  344     ProgressDrawInfo pdi;

  345 

  346     TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);

  347 

  348     pdi.hdc = hdc;

  349     //pdi.theme = GetWindowTheme (infoPtr->Self);

  350     pdi.theme = infoPtr->hTheme;

  351 

  352     /* get the required bar brush */

  353     if (infoPtr->ColorBar == CLR_DEFAULT)

  354         pdi.hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);

  355     else

  356         pdi.hbrBar = CreateSolidBrush (infoPtr->ColorBar);

  357 

  358     if (infoPtr->ColorBk == CLR_DEFAULT)

  359         pdi.hbrBk = GetSysColorBrush(COLOR_3DFACE);

  360     else

  361         pdi.hbrBk = CreateSolidBrush(infoPtr->ColorBk);

  362 

  363     /* get the window style */

  364     dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);

  365 

  366     /* get client rectangle */

  367     GetClientRect (infoPtr->Self, &pdi.rect);

  368     if (!pdi.theme) {

  369         FrameRect( hdc, &pdi.rect, pdi.hbrBk );

  370         InflateRect(&pdi.rect, -1, -1);

  371     }

  372     else

  373     {

  374         RECT cntRect;

  375         int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;

  376 

  377         GetThemeBackgroundContentRect (pdi.theme, hdc, part, 0, &pdi.rect,

  378             &cntRect);

  379 

  380         /* Exclude content rect - content background will be drawn later */

  381         ExcludeClipRect (hdc, cntRect.left, cntRect.top,

  382             cntRect.right, cntRect.bottom);

  383         if (IsThemeBackgroundPartiallyTransparent (pdi.theme, part, 0))

  384             DrawThemeParentBackground (infoPtr->Self, hdc, NULL);

  385         DrawThemeBackground (pdi.theme, hdc, part, 0, &pdi.rect, NULL);

  386         SelectClipRgn (hdc, NULL);

  387         CopyRect (&pdi.rect, &cntRect);

  388     }

  389 

  390     /* compute some drawing parameters */

  391     barSmooth = (dwStyle & PBS_SMOOTH) && !pdi.theme;

  392     drawProcs = &((pdi.theme ? drawProcThemed : drawProcClassic)[(barSmooth ? 0 : 4)

  393         + ((dwStyle & PBS_VERTICAL) ? 2 : 0)]);

  394     barSize = get_bar_size( dwStyle, &pdi.rect );

  395     if (pdi.theme)

  396     {

  397         GetWindowRect( infoPtr->Self, &pdi.bgRect );

  398         ScreenToClient( infoPtr->Self, (POINT*)&pdi.bgRect );

  399         ScreenToClient( infoPtr->Self, (POINT*)&pdi.bgRect.right );

  400     }

  401 

  402     if (!barSmooth)

  403         pdi.ledW = get_led_size( infoPtr, dwStyle, &pdi.rect);

  404     pdi.ledGap = get_led_gap( infoPtr );

  405 

  406 

  407 

  408     if (dwStyle & PBS_MARQUEE)

  409     {

  410         const int ledW = !barSmooth ? (pdi.ledW + pdi.ledGap) : 1;

  411         const int leds = (barSize + ledW - 1) / ledW;

  412         const int ledMEnd = infoPtr->MarqueePos + MARQUEE_LEDS;

  413 

  414         if (ledMEnd > leds)

  415         {

  416             /* case 1: the marquee bar extends over the end and wraps around to

  417             * the start */

  418             const int gapStart = max((ledMEnd - leds) * ledW, 0);

  419             const int gapEnd = min(infoPtr->MarqueePos * ledW, barSize);

  420 

  421             drawProcs[0]( &pdi, 0, gapStart);

  422             drawProcs[1]( &pdi, gapStart, gapEnd);

  423             drawProcs[0]( &pdi, gapEnd, barSize);

  424         }

  425         else

  426         {

  427             /* case 2: the marquee bar is between start and end */

  428             const int barStart = infoPtr->MarqueePos * ledW;

  429             const int barEnd = min (ledMEnd * ledW, barSize);

  430 

  431             drawProcs[1]( &pdi, 0, barStart);

  432             drawProcs[0]( &pdi, barStart, barEnd);

  433             drawProcs[1]( &pdi, barEnd, barSize);

  434         }

  435     }

  436     else

  437     {

  438         int barEnd = get_bar_position( infoPtr, dwStyle, &pdi.rect,

  439             infoPtr->CurVal);

  440         if (!barSmooth)

  441         {

  442             const int ledW = pdi.ledW + pdi.ledGap;

  443             barEnd = min (((barEnd + ledW - 1) / ledW) * ledW, barSize);

  444         }

  445         drawProcs[0]( &pdi, 0, barEnd);

  446         drawProcs[1]( &pdi, barEnd, barSize);

  447     }

  448 

  449     /* delete bar brush */

  450     if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (pdi.hbrBar);

  451     if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (pdi.hbrBk);

  452 

  453     return 0;


위와 같이, 생각에는 6.0미만의 공용 컨트롤에는 저 마퀴관련한 스타일을 처리하는 부분이 없나봅니다.
이쯤에서 살펴볼 것이, MFC에서 마퀴 프로그래스바를 사용하는 2가지 방법입니다.

먼저 첫번째는 MFC로 프로그래스바를 만들되 마퀴가 나타나도록 하는 것입니다.
이것의 단점은 XP 이상의 OS에서만 구현이된다는 것입니다. 2000 같은 경우는 아무것도 하지않는 프로그래스바로 나오게 되죠.

  110     const UINT PBS_MARQUEE = 0x08;

  111     const UINT PBM_SETMARQUEE = (WM_USER+10);

  112     HWND hWnd = GetDlgItem(IDC_PROG)->GetSafeHwnd();

  113     LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);

  114     style |= PBS_MARQUEE;

  115     SetWindowLongPtr(hWnd, GWL_STYLE, style);

  116     ::SendMessage(hWnd,(UINT) PBM_SETMARQUEE,(WPARAM) TRUE,(LPARAM)50 );  


실제 코드는 위와 같습니다. IDC_PROG 가 프로그래스 컨트롤이라면 저렇게만 해주면 마퀴의 형태를 가지게 됩니다. 다만 부드럽게 나타나지는 않습니다. ^^;
참, 말씀드렸다시피 저렇게 한 후, manifest 넣어야지 됩니다.

두번째로 살펴볼 것이, AVI 를 리소스로 사용하여 에니메이션 컨트롤로 구현하는 것입니다. 이것의 단점은 프로그래스바 에니메이션이 제작된 AVI의 크기가 fixed 된 것이기 때문에 크기를 바꿀 수 없다는 것이지요.
하지만 OS를 가리지 않고 부드럽게 잘 표현된다는 것이 장점입니다. 밑에 테스트 프로그램에서 사용된 프로그래스 바는 VS8의 리소스에서 추출한 AVI 입니다.




  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기