Tutorial WinAPI C++ 3.1 Creación del ObjetoBarraProgreso
Categorías : Windows, Programación, C y C++.

En este tutorial nos vamos a hacer nuestra barra de progreso desde cero, que se usara en el instalador final para mostrar el progreso de instalación.
Al ser un control 'visual' que no requiere interaccion por parte del usuario, sera bastante facil de programar. Lo unico que tenemos que tener claro es que la barra puede ser vertical o horizontal, y esto se decidira por su forma inicial al crearla. Por ejemplo si creamos una barra de 200 pixeles de ancho por 10 de alto, esta barra sera horizontal.
En primer lugar creamos la clase ObjetoBarraProgreso que heredara de ObjetoControl, y definimos sus miembros. Vamos a necesitar varios miembros para asignar/obtener el mínimo, el valor y el máximo de la barra, además de los colores para el control :
// Clase que hereda ObjetoControl y se centra en las funciones// de la clase ObjetoBarraProgresoclass ObjetoBarraProgreso : public ObjetoControl {public : //////// Miembros publicos// -ConstructorObjetoBarraProgreso(void);// -Destructor~ObjetoBarraProgreso(void);// -Función para crear la progressbarHWND CrearBarraProgreso( HWND hWndParent, const UINT nID,// Posición y tamañoconst int cX, const int cY,const int cAncho, const int cAlto,// Valores y colores por defectoconst float nMin = 0.0f, const float nMax = 100.0f,const float nValor = 0.0f,COLORREF ColorDegradadoSuperior= RGB(200, 200, 200),COLORREF ColorDegradadoInferior= RGB(100, 100, 100),COLORREF ColorBordeExterno = RGB(128, 128, 128),COLORREF ColorBordeInterno = RGB(192, 192, 192),COLORREF ColorFondo = RGB(220, 220, 220)/* FIN declaración CrearBarraProgreso */ );// -Función para asignar el minimo de la progressbarvoid Minimo(const float nMinimo, const bool nRepintar = false);// -Función para obtener el minimo de la progressbarinline float Minimo(void) const { return _Minimo; };// -Función para asignar el maximo de la progressbarvoid Maximo(const float nMaximo, const bool nRepintar = false);// -Función para obtener el maximo de la progressbarinline float Maximo(void) const { return _Maximo; };// -Función para asignar el valor de la progressbarvoid Valor(const float nValor, const bool nRepintar = true);// -Función para obtener el valor de la progressbar// (ojo que devuelve UINT)inline float Valor(void) const { return _Valor; };// -Función que repinta el controlvoid Repintar(void);// -Función que pinta el controlLRESULT Evento_Pintar(HDC hDC, PAINTSTRUCT &PS);//////////////// Colores// -Macro que crea funciones para asignar y obtener// el COLORREF _Color_DegradadoSuperiorAGREGAR_COLOR(Color_DegradadoSuperior);// -Macro que crea funciones para asignar y obtener// el COLORREF _Color_DegradadoInferiorAGREGAR_COLOR(Color_DegradadoInferior);// -Macro que crea funciones para asignar y obtener// el COLORREF _Color_BordeExternoAGREGAR_COLOR(Color_BordeExterno);// -Macro que crea funciones para asignar y obtener// el COLORREF _Color_BordeInternoAGREGAR_COLOR(Color_BordeInterno);// -Macro que crea funciones para asignar y obtener// el COLORREF _Color_FondoAGREGAR_COLOR(Color_Fondo);protected : ///// Miembros protegidosfloat _Minimo;float _Maximo;float _Valor;};
Podríamos haber hecho que mínimo siempre fuera 0 y máximo siempre fuera 100, pero me parece interesante poder asignar esos valores y que la barra calcule el porcentaje, ya que nos ahorrara código para calcular el porcentaje en la aplicación final. Pensad por ejemplo que tenemos 433 archivos y queremos mostrar el porcentaje completado, si la barra tuviera mínimo 0 y máximo 100, la aplicación sería la encargada de calcular el porcentaje, en cambio al hacer que el mínimo y el máximo sean variables de forma que el ObjetoBarraProgreso tenga que calcularlos internamente, nos ahorramos tener que escribir un código para calcular el porcentaje en la aplicación.
El ObjetoBarraProgreso es un control que no requiere acciones del usuario por lo que será bastante fácil de programar, ya que no necesitamos responder a la mayoría de eventos. El único evento que necesitaremos re-escribir es el Evento_Pintar :
LRESULT ObjetoBarraProgreso::Evento_Pintar(HDC hDC, PAINTSTRUCT &PS) {RECT RC;GetClientRect(_hWnd, &RC);// 1 - Creación de un buffer para el pintadoHDC Buffer = CreateCompatibleDC(hDC);HBITMAP Bmp = CreateCompatibleBitmap(hDC, RC.right, RC.bottom);HBITMAP Viejo = static_cast<HBITMAP>(SelectObject(Buffer, Bmp));HRGN Region = CreateRoundRectRgn(0, 0, RC.right, RC.bottom, REDONDEAMIENTO, REDONDEAMIENTO);HFONT VFuente = NULL;RECT Degradado = { RC.left + 2, RC.top +2, RC.right -4, RC.bottom -4 };float Parte = 0.0f;bool EsVertical = (RC.right < RC.bottom);// 2 -Calculamos los pixeles necesarios por cada partefloat TotalValores = 0.0f;if (EsVertical == true) {// (RC.bottom - 4) = Alto utilizable en pixeles del control// (_Maximo - _Minimo) = Total de valores a recorrer// (Alto / Total varlores) = Parte por valorfloat AltoU = static_cast<float>(RC.bottom - 4);TotalValores = (_Maximo - _Minimo);if (TotalValores > AltoU) Parte = TotalValores / AltoU;else Parte = AltoU / TotalValores;Degradado.top = Degradado.bottom - static_cast<int>(Parte * (_Valor - _Minimo));}else {// (RC.right - 4) = Ancho utilizable en pixeles del control// (_Maximo - _Minimo) = Total de valores a recorrer// (Ancho / Total varlores) = Parte por valorfloat AnchoU = static_cast<float>(RC.right - 4);TotalValores = (_Maximo - _Minimo);if (TotalValores > AnchoU) Parte = TotalValores / AnchoU;else Parte = AnchoU / TotalValores;Degradado.right = static_cast<int>(Parte * (_Valor - _Minimo));}// 3 - Pintamos el fondoHBRUSH Brocha = CreateSolidBrush(_Color_Fondo);FillRect(Buffer, &RC, Brocha);DeleteObject(Brocha);// 4 - Pintamos la barra de progreso con un degradadoTRIVERTEX GCVertex[2];GRADIENT_RECT tGRect;GCVertex[0].Red = RGB_OBTENER_R(_Color_DegradadoSuperior);GCVertex[0].Green = RGB_OBTENER_G(_Color_DegradadoSuperior);GCVertex[0].Blue = RGB_OBTENER_B(_Color_DegradadoSuperior);GCVertex[0].x = Degradado.left;GCVertex[0].y = Degradado.top;GCVertex[1].Red = RGB_OBTENER_R(_Color_DegradadoInferior);GCVertex[1].Green = RGB_OBTENER_G(_Color_DegradadoInferior);GCVertex[1].Blue = RGB_OBTENER_B(_Color_DegradadoInferior);GCVertex[1].x = Degradado.right + 2;GCVertex[1].y = Degradado.bottom + 2;tGRect.UpperLeft = 0;tGRect.LowerRight = 1;GradientFill(Buffer, GCVertex, 2, &tGRect, 1, (EsVertical) ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V);// 5 - Pintamos el bordeBrocha = CreateSolidBrush(_Color_BordeInterno);FrameRgn(Buffer, Region, Brocha, 2, 2);DeleteObject(Brocha);Brocha = CreateSolidBrush(_Color_BordeExterno);FrameRgn(Buffer, Region, Brocha, 1, 1);DeleteObject(Brocha);// 6 - Pintamos el Buffer en el DC de la barra de progresoBitBlt(hDC, 0, 0, RC.right, RC.bottom, Buffer, 0, 0, SRCCOPY);// 7 - Selecciono los objetos originales del Buffer y elimino objetos gdi de la memoriaSelectObject(Buffer, VFuente);SelectObject(Buffer, Viejo);DeleteObject(Region);DeleteObject(Bmp);DeleteDC(Buffer);return 0;}
Veamos la función en 7 pasos :
- Creamos el buffer de pintado con las API’s CreateCompatibleDC, CreateCompatibleBitmap, SelectObject y creamos la región que usaremos para pintar el borde del control con la API CreateRoundRectRgn.
- Hacemos el cálculo de pixeles necesarios por parte. Para hacer este cálculo necesitamos saber si la barra es horizontal o vertical, y luego aplicaremos la siguiente fórmula para determinar el tamaño en pixeles que necesita : ((Maximo – Minimo) / TamañoControl) * (Valor – Minimo).
- Pintamos el fondo utilizando las API’s CreateSolidBrush, FillRect, y eliminamos la brocha con al API DeleteObject. Este fondo es el fondo “vacio” del control, o la parte de barra que no se ha completado.
- Pintamos un degradado en la parte de la barra de progreso con la API GradientFill.
- Pintamos un borde de 2 pixeles con 2 colores distintos utilizando las API’s CreateSolidBrush, FrameRgn y borramos la brocha utilizada con la API DeleteObject.
- Pintamos el buffer al HDC del control utilizando la API BitBlt.
- Liberamos la memoria utilizada por el buffer y la región utilizando las API's SelectObject, DeleteObject y DeleteDC.
Con esto ya tenemos un control al que le podemos especificar mínimo, máximo y valor para que nos pinte una barra.
Llegados a este punto ya podemos pasar al siguiente tutorial 3.2 Common Dialogs.