代码简介或代码解析: 不用位图文件的电子钟表 实现步骤: 1 把CGCColorStatic.h/cpp和GCClock.h/cpp加入到工程中 2 在StaticDigitalClock2Dlg.h中添加 #include "GCClock.h" 3 添加如图所示的控件,并为第一个单选按钮IDC_RADIO_12HOURS添加一个变量 int m_iFormat; 4 在StaticDigitalClock2Dlg.h中添加 private: CGCClock m_Clock; CGCClock m_StyleExampleA; CGCClock m_StyleExampleB; CGCClock m_StyleExampleC; CGCClock m_StyleExampleD; 5 在构造函数中加入 m_iFormat =0; 在DoDataExchange(CDataExchange* pDX)的 //{{AFX_DATA_MAP(CStaticDigitalClock2Dlg)上面增加: DDX_Control(pDX, IDC_STATIC_CLOCK, m_Clock); DDX_Control(pDX, IDC_STATIC_STYLE_EXA, m_StyleExampleA); DDX_Control(pDX, IDC_STATIC_STYLE_EXB, m_StyleExampleB); DDX_Control(pDX, IDC_STATIC_STYLE_EXC, m_StyleExampleC); DDX_Control(pDX, IDC_STATIC_STYLE_EXD, m_StyleExampleD); 6 在OnInitDialog()中加入: // TODO: Add extra initialization here m_StyleExampleA.SetOn(TRUE); m_StyleExampleA.SetTextOffColor(RGB(0,180,255)); m_StyleExampleA.SetBackgroundOffColor(RGB(0,0,0)); m_StyleExampleA.SetBold(); m_StyleExampleA.SetSunken(); m_StyleExampleA.SetPointFont(12,"Comic Sans MS"); m_StyleExampleA.Start(); m_StyleExampleB.SetOn(FALSE); m_StyleExampleB.SetTextOffColor(RGB(255,255,255)); m_StyleExampleB.SetPointFont(11,"Times New Roman"); // m_StyleExampleB.SetBold(); m_StyleExampleB.SetColorFrame(TRUE,2,RGB(190,0,0)); m_StyleExampleB.Start(); m_StyleExampleC.SetOn(FALSE); m_StyleExampleC.SetTextOffColor(RGB(0,0,255)); m_StyleExampleC.SetBackgroundOffColor(RGB(0,0,0)); m_StyleExampleC.SetBold(); m_StyleExampleC.SetSunken(); m_StyleExampleC.SetPointFont(12,"幼圆"); m_StyleExampleC.Start(); m_StyleExampleD.SetOn(FALSE); m_StyleExampleD.SetTextOffColor(RGB(0,255,0)); m_StyleExampleD.SetBackgroundOffColor(RGB(0,0,0)); m_StyleExampleD.SetBold(); m_StyleExampleD.SetSunken(); m_StyleExampleD.SetPointFont(12,"楷体GB2312"); m_StyleExampleD.Start(); m_Clock.SetOn(FALSE); m_Clock.SetTextOffColor(RGB(0,180,255)); m_Clock.SetBackgroundOffColor(RGB(0,0,50)); m_Clock.SetBold(); m_Clock.SetPointFont(9,"Arial"); m_Clock.SetModalFrame(); m_Clock.Start(); 7 写单选按钮响应函数 void CStaticDigitalClock2Dlg::OnRadio12hours() ...{ m_Clock.Format(); } void CStaticDigitalClock2Dlg::OnRadio24hours() ...{ m_Clock.Format(TRUE); } 8 附相关代码: CGCClock::CGCClock() : CGCColorStatic() ...{ m_b24hr = FALSE; m_uiHour =0; m_uiMinutes =0; m_bAlarmSet = FALSE; m_bAlarmRunning = FALSE; m_bAlarmActivated = FALSE; } CGCClock::~CGCClock() ...{ } BEGIN_MESSAGE_MAP(CGCClock, CGCColorStatic) ON_WM_TIMER() END_MESSAGE_MAP() void CGCClock::OnTimer(UINT nIDEvent) ...{ // The CGCClock class is derived from the CGCColorStatic class. // Since the CGCColorStatic class provides the capability to flash the background // or text using timer events, we must distinguish between CGCColorStatic timer events // and the CGCClock's clock update timer event. Verify that the current timer // event is the CGCClock class' update event. If it isn't route it on to the // parent class' OnTimer() event handler because that may be one of the // CGCColorStatic class' flashing timer events. if (nIDEvent != CLOCK_UPDATE_TIMER_ID) CGCColorStatic::OnTimer(nIDEvent); // If we are here then the current timer event is the clock's update event. // Get the current time. GetLocalTime(&m_CurrentDateTime); // Format it for display. FormatTime(); // Display the time. SetWindowText(m_FormattedTime); // If it is time for the alarm, initiate flashing of the background // by calling the inherited FlashBackground() method. if ( (!m_bAlarmRunning) && m_bAlarmSet && m_bAlarmActivated && ( (m_CurrentDateTime.wHour >= m_uiHour) && (m_CurrentDateTime.wMinute >= m_uiMinutes) ) ) ...{ FlashBackground(TRUE,FALSE,1); m_bAlarmRunning = TRUE; // Here is where you would add sound for the alarm. } } void CGCClock::FormatTime() ...{ if (m_b24hr) m_FormattedTime.Format("%02d:%02d:%02d", m_CurrentDateTime.wHour, m_CurrentDateTime.wMinute, m_CurrentDateTime.wSecond); else ...{ if (m_CurrentDateTime.wHour >12) ...{ UINT th; th = m_CurrentDateTime.wHour -12; m_FormattedTime.Format("%2d:%02d:%02d p.m.", th, m_CurrentDateTime.wMinute, m_CurrentDateTime.wSecond); } else m_FormattedTime.Format("%d:%02d:%02d p.m.", m_CurrentDateTime.wHour, m_CurrentDateTime.wMinute, m_CurrentDateTime.wSecond); } } BOOL CGCClock::Start() ...{ GetLocalTime(&m_CurrentDateTime); FormatTime(); SetWindowText(m_FormattedTime); UINT id =this->SetTimer(CLOCK_UPDATE_TIMER_ID,1000,NULL); if (id ==0) return FALSE; return TRUE; } void CGCClock::Format(const BOOL bTwentyFourHour) ...{ m_b24hr = bTwentyFourHour; FormatTime(); SetWindowText(m_FormattedTime); } void CGCClock::GetAlarmTime(UINT &Hour, UINT &Minutes, BOOL& Active) const ...{ Hour = m_uiHour; Minutes = m_uiMinutes; Active = m_bAlarmRunning; } BOOL CGCClock::SetAlarm(const UINT Hour, const UINT Minutes) ...{ if ( Hour <=0 ) return FALSE; m_bAlarmSet = TRUE; if (m_bAlarmRunning) ...{ // Since we are setting a new alarm time, kill the current alarm // if it is active (going off). FlashBackground(FALSE,FALSE); m_bAlarmRunning = FALSE; } m_uiHour = Hour; m_uiMinutes = Minutes; return TRUE; } BOOL CGCClock::ActivateAlarm(const BOOL bOn) ...{ BOOL prev = m_bAlarmActivated; m_bAlarmActivated = bOn; return prev; } CGCColorStatic::CGCColorStatic(const BOOL bOn) ...{ m_bBackgroundOn = bOn; // Set initial bacground state. m_bTextOn = bOn; // Set initial text state. m_bColorFrameFlag = FALSE; // Is a color frame displayed? m_ColorFrameWidth =2; // At what width is the color frame to be displayed, if displayed? // Initialize default on/off color variables. m_CurrentBackgroundOnColor = DEFAULT_BACKGROUND_ON_COLOR; m_CurrentBackgroundOffColor = DEFAULT_BACKGROUND_OFF_COLOR; m_CurrentTextOnColor = DEFAULT_TEXT_ON_COLOR; m_CurrentTextOffColor = DEFAULT_TEXT_OFF_COLOR; m_ColorFrameColor = DEFAULT_BACKGROUND_OFF_COLOR; // Create default background ON brush. m_brOnBrush.CreateSolidBrush(m_CurrentBackgroundOnColor); // Create default background OFF brush. m_brOffBrush.CreateSolidBrush(m_CurrentBackgroundOffColor); // m_brOffBrush.CreateSolidBrush(GetSysColor(COLOR_3DFACE)); m_FlashTypeFlag = NONE; // Default to not flashing. m_bTimerFlag = FALSE; // Indicates if a flashing Timer exists or not. m_uiTimerID =0; m_bHatchMode = FALSE; // Is the background in hatch pattern mode? m_iHatchType =-1; // Initialize hatch pattern. // Initialize variables used for Asymmetrical Background Flashing m_fOnDuration =0.0; m_fOffDuration =0.0; // Initialize variables used for Pulsed Background Flashing m_pulseDuration =0.0; m_pulseRep =0; m_pulseOnDuration =0.0; m_pulseCounter =0; } CGCColorStatic::~CGCColorStatic() ...{ m_brOnBrush.DeleteObject(); m_brOffBrush.DeleteObject(); } BEGIN_MESSAGE_MAP(CGCColorStatic, CStatic) ON_MESSAGE( WM_GECCOLORSTATIC_DRAWFRAME, OnDrawFrame ) //{{AFX_MSG_MAP(CGCColorStatic) ON_WM_CTLCOLOR_REFLECT() ON_WM_TIMER() ON_WM_NCPAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() /**////////////////////////////////////////////////////////////////////////////// // CGCColorStatic message handlers // **************************************************************************** // **************************************************************************** // CGCColorStatic::CtlColor() // Handles the appropriate colors when the control is repainted on screen // // REMEMBER // // If you handle WM_CTLCOLOR messages in your parent window (the window that // contains this control) you must be sure to call the parent window's base // class OnCtlColor() handler if the parent's OnCtlColor() handler is being // called for this control. Otherwise, the OnCtlColor() handler in the // parent window will OVERRIDE the OnCtlColor() handler for this control. // // Windows is designed that way to give the parent a shot at handling the // message before reflecting it back to this control. // **************************************************************************** HBRUSH CGCColorStatic::CtlColor(CDC* pDC, UINT nCtlColor) ...{ #ifdef _DEBUG CtlColorCount +=1; _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s %d ",">>>>>>>>> CtlColor() called.",CtlColorCount); #endif // Set the Text Color according to the control's state. // It is not necessary to utilize the nCtlColor parameter in this implementation. if (m_bTextOn) pDC->SetTextColor(m_CurrentTextOnColor); // Set text to its ON color. else pDC->SetTextColor(m_CurrentTextOffColor); // Set text to its OFF color. // Set the fill color according to the background state. if (m_bBackgroundOn) pDC->SetBkColor(m_CurrentBackgroundOnColor); else pDC->SetBkColor(m_CurrentBackgroundOffColor); // If the control is in Hatch Mode, the control will display the // hatch in the background's ON color and the background behind the // hatch pattern in the background's OFF color by calling the SetBkColor() method // of the CDC class above. // The SetHatch() method changes the ON brush to a hatch brush. // We return the hatch brush when in Hatch Mode. if (m_bHatchMode) ...{ // I do not set the background mode of the device context to transparent // when in Hatch Mode because having the lines passing through underneath // the text usually makes the text difficult to read when the control is in // its "off" state. Therefore I keep the background mode to OPAQUE (default) // to use the background off color as a block on which the text is drawn. // This makes it much easier to read. // PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return static_cast(m_brOnBrush.GetSafeHandle()); } // At this point, we must not be in Hatch Mode. // Set background mode of the DC to TRANSPARENT so that if a bitmap pattern // is being used for the background, it will show beneath the text. // pDC->SetBkMode(TRANSPARENT); if (!m_bBackgroundOn) ...{ PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return static_cast(m_brOffBrush.GetSafeHandle()); // Return background's OFF color. } else ...{ PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return static_cast(m_brOnBrush.GetSafeHandle()); // Return background's ON color. } } // **************************************************************************** // **************************************************************************** COLORREF CGCColorStatic::SetBackgroundOnColor(const COLORREF backColor) ...{ // Prepare to return previous background ON color. COLORREF prevColor = m_CurrentBackgroundOnColor; // Store new background ON color. m_CurrentBackgroundOnColor = backColor; // Calling SetBackgroundOnColor() does not change whether or not // a hatch pattern is displayed. It changes the ON color of the // background. If the control is in Hatch Mode, the hatch pattern // will be updated with the new ON color. If the control is NOT in // Hatch Mode, then the solid ON color will be updated. if (!m_bHatchMode) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetBackgroundOnColor() - creating background On solid brush."); #endif // We are not in Hatch Mode. Delete the old brush and create a new // SOLID brush with the new "on" color. m_brOnBrush.DeleteObject(); m_brOnBrush.CreateSolidBrush(m_CurrentBackgroundOnColor); } else ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetBackgroundOnColor() - creating background On hatch brush."); #endif // We ARE in Hatch Mode. Delete the old hatch brush and create a new // HATCH brush with the new "on" color. m_brOnBrush.DeleteObject(); m_brOnBrush.CreateHatchBrush(m_iHatchType,m_CurrentBackgroundOnColor); } Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prevColor; // Return previous background ON color. } COLORREF CGCColorStatic::SetTextOnColor(const COLORREF textColor) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetTextOnColor() - Setting text On color."); #endif COLORREF prevColor = m_CurrentTextOnColor; // Prepare to return text's previous ON color. m_CurrentTextOnColor = textColor; // Store text's new ON color. Invalidate(TRUE); // Force edit window to repaint. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prevColor; } COLORREF CGCColorStatic::SetTextOffColor(const COLORREF offColor) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetTextOffColor() - Setting text Off color."); #endif COLORREF prevColor = m_CurrentTextOffColor; // Prepare to return text's previous OFF color. m_CurrentTextOffColor = offColor; // Store text's new OFF color. Invalidate(TRUE); // Force edit window to repaint. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prevColor; // Return text's previous OFF color. } COLORREF CGCColorStatic::GetTextOffColor() const ...{ return m_CurrentTextOffColor; } void CGCColorStatic::GetTextOffColor(BYTE &Red, BYTE &Green, BYTE &Blue) const ...{ Red = GetRValue(m_CurrentTextOffColor); Green = GetGValue(m_CurrentTextOffColor); Blue = GetBValue(m_CurrentTextOffColor); } COLORREF CGCColorStatic::GetBackgroundOnColor() const ...{ return m_CurrentBackgroundOnColor; } COLORREF CGCColorStatic::GetTextOnColor() const ...{ return m_CurrentTextOnColor; } void CGCColorStatic::GetBackgroundOnColor(BYTE & Red, BYTE & Green, BYTE & Blue) const ...{ Red = GetRValue(m_CurrentBackgroundOnColor); Green = GetGValue(m_CurrentBackgroundOnColor); Blue = GetBValue(m_CurrentBackgroundOnColor); } void CGCColorStatic::GetTextOnColor(BYTE & Red, BYTE & Green, BYTE & Blue) const ...{ Red = GetRValue(m_CurrentTextOnColor); Green = GetGValue(m_CurrentTextOnColor); Blue = GetBValue(m_CurrentTextOnColor); } BOOL CGCColorStatic::SetPointFont(constint PointSize, const LPCTSTR fontname) ...{ BOOL bResult = FALSE; // Get device context for this edit box. CDC* pDC =this->GetDC(); // Delete previous CGdiObject and free its memory. m_Font.DeleteObject(); // Create a new Font object. bResult = m_Font.CreatePointFont((PointSize*10),fontname,pDC); if (! bResult) return bResult; // Set the new font for this edit box. CStatic::SetFont(&m_Font,TRUE); // Release the device context. ReleaseDC(pDC); return bResult; } BOOL CGCColorStatic::SetFontIndirect(const LOGFONT * lpLogFont) ...{ BOOL bResult; // Delete previous CGdiObject and free its memory. m_Font.DeleteObject(); // Create a new Font object. bResult = m_Font.CreateFontIndirect(lpLogFont); if (! bResult) return bResult; // Set the new font for this edit box. CStatic::SetFont(&m_Font,TRUE); return bResult; } BOOL CGCColorStatic::SetPointFontIndirect(const LOGFONT * lpLogFont) ...{ BOOL bResult; // Delete previous CGdiObject and free its memory. m_Font.DeleteObject(); // Get device context for this edit box. CDC* pDC =this->GetDC(); // Create a new Font object. bResult = m_Font.CreatePointFontIndirect(lpLogFont, pDC); if (! bResult) ...{ // Release the device context. ReleaseDC(pDC); return bResult; } // Set the new font for this edit box. CStatic::SetFont(&m_Font,TRUE); // Release the device context. ReleaseDC(pDC); return bResult; } LOGFONT CGCColorStatic::GetLogFont() const ...{ LOGFONT fontstruct; CFont* pFont = NULL; pFont =this->GetFont(); pFont->GetLogFont(&fontstruct); return fontstruct; } void CGCColorStatic::SetUnderline(const BOOL On) ...{ LOGFONT fontstruct; fontstruct =this->GetLogFont(); fontstruct.lfUnderline = (BYTE) On; SetFontIndirect(&fontstruct); } void CGCColorStatic::SetItalic(const BOOL On) ...{ LOGFONT fontstruct; fontstruct =this->GetLogFont(); fontstruct.lfItalic = (BYTE) On; SetFontIndirect(&fontstruct); } void CGCColorStatic::SetBold(const BOOL On) ...{ LOGFONT fontstruct; fontstruct =this->GetLogFont(); switch (On) ...{ case TRUE: fontstruct.lfWeight =700; break; case FALSE: fontstruct.lfWeight =400; } SetFontIndirect(&fontstruct); } BOOL CGCColorStatic::SetFontWeight(constint Weight) ...{ if ( (Weight <1) || (Weight >1000) ) return FALSE; LOGFONT fontstruct; fontstruct =this->GetLogFont(); fontstruct.lfWeight = Weight; return SetFontIndirect(&fontstruct); } BOOL CGCColorStatic::FlashBackground(const BOOL bFlash, const BOOL bFinalState, constfloat duration) ...{ if (m_bTimerFlag) KillTimer(m_uiTimerID); // If a timer is active, kill it. if (bFlash) ...{ // We are turning on flashing of the background. m_bTextOn = m_bBackgroundOn; // Resync on/off states. Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); m_FlashTypeFlag = BACKGROUND; // Set flash type to BACKGROUND. m_bTimerFlag = TRUE; // Yes, a timer process exists. m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(duration *1000),NULL); // Create the timer. if (!m_uiTimerID) return FALSE; } else ...{ // If we are turning off the flashing, make sure the background is left // in the state specified by bFinalState. m_bBackgroundOn = bFinalState; // Set final state of background. m_bTextOn = bFinalState; // Resync text state with background state. m_FlashTypeFlag = NONE; // No flash state exists. Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } return TRUE; } BOOL CGCColorStatic::FlashBackgroundAsym(const BOOL bFlash, const BOOL bFinalState, constfloat OnDuration, constfloat OffDuration) ...{ if (m_bTimerFlag) KillTimer(m_uiTimerID); // If a timer is active, kill it. if (bFlash) ...{ m_fOnDuration = OnDuration *1000; m_fOffDuration = OffDuration *1000; // We are turning on flashing of the background. SetOn(TRUE); // Resync text with background and // set to on state. Start asymmetrical // flashing out in ON state. m_FlashTypeFlag = ASYM_BACKGROUND; // Set flash type to ASYM_BACKGROUND. // This is the Asymmetrical Flash mode. m_bTimerFlag = TRUE; // Yes, a timer process exists. m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_fOnDuration),NULL); // Create the timer. if (!m_uiTimerID) return FALSE; } else ...{ // If we are turning off the flashing, make sure the background is left // in the state specified by bFinalState. m_bBackgroundOn = bFinalState; // Set final state of background. m_bTextOn = bFinalState; // Resync text state with background state. m_FlashTypeFlag = NONE; // No flash state exists. Invalidate(TRUE); // Force repaint. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } return TRUE; } BOOL CGCColorStatic::FlashBackgroundPulsed(const BOOL bFlash, const BOOL bFinalState, constfloat pulseDuration, constint pulseRep, constfloat onDuration) ...{ if (m_bTimerFlag) KillTimer(m_uiTimerID); // If a timer is active, kill it. if (bFlash) ...{ m_pulseDuration = pulseDuration *1000; m_pulseOnDuration = onDuration *1000; m_pulseRep = pulseRep *2+1; // We are turning on flashing of the background. SetOn(TRUE); // Resync text with background and // set to on state. Start pulsed // flashing out in ON state. m_FlashTypeFlag = PULSED_BACKGROUND; // Set flash type to PULSED_BACKGROUND. // This is the Pulsed Flash mode. m_bTimerFlag = TRUE; // Yes, a timer process exists. m_pulseCounter +=1; m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_pulseDuration),NULL); // Create the timer. if (!m_uiTimerID) return FALSE; } else ...{ // If we are turning off the flashing, make sure the background is left // in the state specified by bFinalState. m_bBackgroundOn = bFinalState; // Set final state of background. m_bTextOn = bFinalState; // Resync text state with background state. m_FlashTypeFlag = NONE; // No flash state exists. Invalidate(TRUE); // Force repaint. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } return TRUE; } BOOL CGCColorStatic::FlashText(const BOOL bFlash, constfloat duration, const BOOL bFinalState) ...{ if (m_bTimerFlag) KillTimer(m_uiTimerID); // If a timer is active, kill it. if (bFlash) ...{ // We are turning on flashing of the text. m_bTextOn = m_bBackgroundOn; // Resync on/off states. Invalidate(TRUE); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); m_FlashTypeFlag = TEXT; // Set flash type to TEXT. m_bTimerFlag = TRUE; // Yes, a timer process exists. m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(duration *1000),NULL); // Spawn timer. if (!m_uiTimerID) return FALSE; } else ...{ m_FlashTypeFlag = NONE; // No flash state exists. m_bTextOn = bFinalState; // Resync text with background final state. Invalidate(TRUE); // Force redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } return TRUE; } void CGCColorStatic::OnTimer(UINT nIDEvent) ...{ // Toggle the state of the appropriate item. switch (m_FlashTypeFlag) ...{ case TEXT: // If the flash type is TEXT... m_bTextOn =!m_bTextOn; // Invert state. Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); break; case BACKGROUND: // If the flash type is BACKGROUND... m_bBackgroundOn =!m_bBackgroundOn; // Invert state. m_bTextOn =!m_bTextOn; // Invert state. Yes, I do this here // but not for TEXT above. Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); break; case ASYM_BACKGROUND: // If the flash type is Asymmetrical Background... KillTimer(m_uiTimerID); // Kill previous timer. // When FlashBackgroundAsym() is called, the control is immediately updated // to the "ON" state by that method. So, we want to start out with // setting the timer for the OnDuration as specified by m_fOnDuration // member variable. m_bBackgroundOn =!m_bBackgroundOn; // Invert state. m_bTextOn =!m_bTextOn; // Invert state. Yes, I do this here Invalidate(TRUE); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); switch (m_bBackgroundOn) ...{ case TRUE: m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_fOnDuration),NULL); // Create the "ON" timer. break; case FALSE: m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_fOffDuration),NULL); // Create the "OFF" timer. } break; case PULSED_BACKGROUND: // If the flash type is Pulsed Background... // When FlashBackgroundPulsed() is called, the control is immediately updated // to the "ON" state by that method. So, we want to start out with // setting the timer for the pulse's on duration as specified by m_pulseDuration // member variable. m_bBackgroundOn =!m_bBackgroundOn; // Invert state. m_bTextOn =!m_bTextOn; // Invert state. Yes, I do this here Invalidate(TRUE); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); m_pulseCounter +=1; if (m_pulseCounter <= m_pulseRep) ...{ m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_pulseDuration),NULL); // Create a pulse timer. } else ...{ m_pulseCounter =0; SetOn(); m_uiTimerID = SetTimer(FLASH_TIMER_ID,int(m_pulseOnDuration),NULL); // Create the "ON" timer. } } CStatic::OnTimer(nIDEvent); } COLORREF CGCColorStatic::SetBackgroundOffColor(const COLORREF offColor) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetBackgroundOffColor() - Creating background Off brush."); #endif COLORREF prevColor = m_CurrentBackgroundOffColor; // Prepare to return background's previous OFF color. m_CurrentBackgroundOffColor = offColor; // Store background's new OFF color. m_brOffBrush.DeleteObject(); // Delete background's old OFF brush. m_brOffBrush.CreateSolidBrush(m_CurrentBackgroundOffColor); // Create background's new OFF brush. if (!m_bBackgroundOn) ...{ Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } return prevColor; // Return background's previous OFF color. } COLORREF CGCColorStatic::GetBackgroundOffColor() const ...{ return m_CurrentBackgroundOffColor; } void CGCColorStatic::GetBackgroundOffColor(BYTE &Red, BYTE &Green, BYTE &Blue) const ...{ Red = GetRValue(m_CurrentBackgroundOffColor); Green = GetGValue(m_CurrentBackgroundOffColor); Blue = GetBValue(m_CurrentBackgroundOffColor); } BOOL CGCColorStatic::SetOn(const BOOL bOn) ...{ #ifdef _DEBUG if (bOn) _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetOn() - Setting state to ON"); else _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">> SetOn() - Setting state to OFF"); #endif _DEBUG BOOL prevState = m_bBackgroundOn; // Key off background for overall state of control. m_bBackgroundOn = bOn; // Set background to specified on/off state. m_bTextOn = m_bBackgroundOn; // Sync text state to background state. Invalidate(); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prevState; // Return previous state. } BOOL CGCColorStatic::SetSunken(const BOOL bSunken) ...{ BOOL Result = FALSE; if (bSunken) ...{ if (m_bColorFrameFlag) SetColorFrame(FALSE); // Color frame and sunken frame mutually exclusive. Result = ModifyStyleEx(0,WS_EX_STATICEDGE,SWP_DRAWFRAME); } else Result = ModifyStyleEx(WS_EX_STATICEDGE,0,SWP_DRAWFRAME); return Result; } BOOL CGCColorStatic::SetBorder(const BOOL bBorder) ...{ BOOL Result = FALSE; if (bBorder) Result = ModifyStyle(0,WS_BORDER,SWP_DRAWFRAME); else Result = ModifyStyle(WS_BORDER,0,SWP_DRAWFRAME); return Result; } BOOL CGCColorStatic::SetModalFrame(const BOOL bModal) ...{ BOOL Result = FALSE; if (bModal) ...{ if (m_bColorFrameFlag) SetColorFrame(FALSE); // Color frame and modal frame mutually exclusive. Result = ModifyStyleEx(0,WS_EX_DLGMODALFRAME,SWP_DRAWFRAME); } else Result = ModifyStyleEx(WS_EX_DLGMODALFRAME,0,SWP_DRAWFRAME); return Result; } BOOL CGCColorStatic::TextOn(const BOOL bOn) ...{ BOOL prevState = m_bTextOn; if (m_bTextOn == bOn) return m_bTextOn; m_bTextOn = bOn; Invalidate(TRUE); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prevState; } void CGCColorStatic::GetStates(BOOL &bBackgroundState, BOOL &bTextState) const ...{ bBackgroundState = m_bBackgroundOn; bTextState = m_bTextOn; } COLORREF CGCColorStatic::SetBackgroundOnPattern(CBitmap * pattern) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetBackgroundOnPattern() - Creating background on pattern brush"); #endif m_bHatchMode = FALSE; COLORREF prev = m_CurrentBackgroundOnColor; // Since the bitmap to be used to create the pattern brush may // not be a single color, just set the background "on" color to // the default background ON color. m_CurrentBackgroundOnColor = DEFAULT_BACKGROUND_ON_COLOR; m_brOnBrush.DeleteObject(); // Delete the old background "on" brush. m_brOnBrush.CreatePatternBrush(pattern); // Create the new "on" pattern brush. Invalidate(); // Repaint the control. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prev; // Return previous background ON color. } COLORREF CGCColorStatic::SetBackgroundOffPattern(CBitmap * pattern) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetBackgroundOffPattern() - Creating background off pattern brush"); #endif m_bHatchMode = FALSE; COLORREF prev = m_CurrentBackgroundOffColor; // Since the bitmap to be used to create the pattern brush may // not be a single color, just set the background "off" color to // the default background OFF color. m_CurrentBackgroundOffColor = DEFAULT_BACKGROUND_OFF_COLOR; m_brOffBrush.DeleteObject(); // Delete the old background "on" brush. m_brOffBrush.CreatePatternBrush(pattern); // Create the new "on" pattern brush. Invalidate(); // Repaint the control. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return prev; // Return previous background OFF color. } BOOL CGCColorStatic::SetHatch(const BOOL bOn, const UINT HatchType) ...{ if (m_bHatchMode == bOn) return (m_bHatchMode == bOn); m_bHatchMode = bOn; if (m_bHatchMode) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetHatch() - Creating hatch brush"); #endif // If the control is being set to Hatch Mode, delete the old brush // used for the background's ON state and create a hatch brush // using the same color. m_brOnBrush.DeleteObject(); m_brOnBrush.CreateHatchBrush(HatchType,m_CurrentBackgroundOnColor); // If the previous background OFF brush was a bitmap pattern, // we want to delete it and set the background OFF color to the default // off color. It is expected that the client will set a color for the // background off color as well as for the background on color that will // be used for the hatch pattern. LOGBRUSH logicalBrush; m_brOffBrush.GetLogBrush(&logicalBrush); if ( (logicalBrush.lbStyle == BS_PATTERN) || (logicalBrush.lbStyle == BS_PATTERN8X8) ) ...{ m_CurrentBackgroundOffColor = DEFAULT_BACKGROUND_OFF_COLOR; m_brOffBrush.DeleteObject(); m_brOffBrush.CreateSolidBrush(DEFAULT_BACKGROUND_OFF_COLOR); } m_iHatchType = HatchType; } else ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetHatch() - Creating solid brush"); #endif // if Hatch Mode is being turned off, then delete the hatch brush // and re-create the solid brush for the background's ON state. m_brOnBrush.DeleteObject(); m_brOnBrush.CreateSolidBrush(m_CurrentBackgroundOnColor); } Invalidate(); // Force the control to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); return FALSE; } void CGCColorStatic::SetColorFrame(const BOOL bOn, constint Width, const COLORREF FrameColor) ...{ m_bColorFrameFlag = bOn; if (!bOn) ...{ #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetColorFrame() - Color Frame turned on"); #endif if (m_bBackgroundOn) m_ColorFrameColor = m_CurrentBackgroundOnColor; else m_ColorFrameColor = m_CurrentBackgroundOffColor; } else ...{ // I made the color frame mutually exclusive of the modal and sunken frames. They just // do not look nice together. // When using the modal or sunken frames, the client area that is actually visible // is decreased in size. If you were to add the color frame with the modal or sunken // frames, and draw the color frame so that it did not overlay the modal or sunken frame, // the usable area would be even smaller. Therefore, as a design decision, // I made the color frame border type mutually exclusive with the modal and sunken types. // Overlaying the color frame on the modal or sunken frame looks bad. BOOL Result = ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE,0,SWP_DRAWFRAME); m_ColorFrameWidth = Width; #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>SetColorFrame() - Color Frame turned off"); #endif m_ColorFrameColor = FrameColor; } Invalidate(TRUE); // Force to redraw. PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); } /**//* void CGCColorStatic::OnNcPaint() { if (m_bColorFrameFlag) { // Note: I do not inflate the rectangle at which the color frame is drawn because // this would make the control take up more area on the screen. If there are multiple // indicators next to each other, you would have the color frame of one // control being drawn on top of the adjoinging control. Therefore, I paint the // frame inside the edges of the control. Remember this when selecting fonts because // the color frame makes the area availble for displaying text a little smaller. _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>OnNcPaint() - Painting frame"); CRect rect; this->GetWindowRect(&rect); CPen pen(PS_INSIDEFRAME , m_ColorFrameWidth,m_ColorFrameColor); ScreenToClient(&rect); CDC* pDDC = this->GetDC(); pDDC->SelectObject(pen); pDDC->SelectObject(GetStockObject(NULL_BRUSH)); pDDC->Rectangle(rect); ReleaseDC(pDDC); } else { _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>OnNcPaint() - Calling base class OnNcPaint()"); CStatic::OnNcPaint(); } CStatic::OnNcPaint(); } */ void CGCColorStatic::OnDrawFrame(WPARAM wp, LPARAM lp) ...{ if (m_bColorFrameFlag) ...{ // Note: I do not inflate the rectangle at which the color frame is drawn because // this would make the control take up more area on the screen. If there are multiple // indicators next to each other, you would have the color frame of one // control being drawn on top of the adjoinging control. Therefore, I paint the // frame inside the edges of the control. Remember this when selecting fonts because // the color frame makes the area availble for displaying text a little smaller. #ifdef _DEBUG _CrtDbgReport(_CRT_WARN,"GCColorStatic.cpp",__LINE__,"Demo"," %s ",">>OnDrawFrame() - Painting frame"); #endif CRect rect; this->GetWindowRect(&rect); // Do not allow an exception constructing the pen or from creating // the device context to kill the client. try ...{ CPen pen(PS_INSIDEFRAME , m_ColorFrameWidth,m_ColorFrameColor); ScreenToClient(&rect); CClientDC dc(this); dc.SelectObject(pen); dc.SelectObject(GetStockObject(NULL_BRUSH)); dc.Rectangle(rect); } catch (CResourceException) ...{ } } } void CGCColorStatic::SetWindowText(LPCTSTR lpszString) ...{ // Had to override this method. Calling the // inherited SetWindowText() resulted in a message being generated causing the // background to be repainted. Since my color frame is drawn inside the edge of the // control, over the client area, this would erase the colored border when a colored // border was being used. Therefore, I had to override this method and force // the redraw of the frame so that it would stay visible. // // CStatic::SetWindowText(lpszString); PostMessage(WM_GECCOLORSTATIC_DRAWFRAME); }