hin-und-her-schalten zwischen Dialogen -> Flackern
-
Hallo,
in meiner Applikation verwende ich einen Wizard (wie er bei jeder normalen Installation eines Programms verwendet wird). D.h. ich habe stets einen Next und einen Back Button, mit denen ich zwischen den einzelnen Dialogen hin- und herschalten kann. All diese Dialoge sind in einem weiteren Dialog eingebunden als Staticframe, dieser besitzt auch die Buttons zur Navigation.
Wenn ich jetzt zwischen mehreren Subdialogen mittels Next / Back Button schalte, flackert der Dialog ab und zu. Ich vermute, dass es an dem ShowWindow(HIDE / SHOW) liegt.
Nachdem Drücken des Next oder Back Buttons wird die Funktion ActivatePage() aufgerufen, um den aktuellen Subdialog zu hiden und den neuen zu zeigen.
BOOL CMainDialog::ActivatePage(CSubPage* pPage) { // if the page has not been created, then create it if (pPage->m_bCreated == FALSE) { if (pPage->OnCreatePage() == FALSE) return FALSE; } // deactivate the current page if (!DeactivatePage()) return FALSE; CRect rect; CWnd *pWnd = GetDlgItem(m_nPlaceholderID); pWnd->GetWindowRect(&rect); ScreenToClient(&rect); pPage->SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE ); pPage->EnableWindow(TRUE); pPage->ShowWindow(SW_SHOW); pPage->InvalidateRect(NULL); pPage->UpdateWindow(); pPage->m_bActive = TRUE; return TRUE; } BOOL CMainDialog::DeactivatePage() { CSubPage* pPage = GetActivePage(); if (pPage == NULL) return TRUE; pPage->ShowWindow(SW_HIDE); pPage->m_bActive = FALSE; return TRUE; }
Wie kann ich das Flackern minimieren bzw. besser gesagt abstellen?
Gruß
Patrick
-
InvalidateRect und UpdateWindow sind überflüssig. ShowWindow muss alleine dafür sorgen, dass das Fenster angezeigt wird.
BTW: Du solltest das ShowWindow vor dem Enable machen, denn ein unsichtbares Fenster kann man schlecht enablen... Das könnte sonst zu Focues Problemen führen.
Evtl. ist es sogar beser, den neuen Dialog erst anzuezeigen und den alten dann zu verstecken.Was das Flackern betrifft kann auch mit WS_CLIPCHILDRREN und WS_CLIPSIBLINGS oft Abhilfe geschaffen werden.
PS: Mir ist nicht klar warum Du nicht CPrpertySheet im Wizard Mode benutzt...
-
Vielen Dank für deine Tipps. Hat sich gleich um einiges verbessert. Lediglich bei meinen selbstgezeichneten Editboxen sieht man das Flackern noch.
Bei diesen Editboxen füge ich im OnNcPaint() Handler ein Bitmap ein. Hab zwar schon CMemDC versucht zu verwenden, aber irgendwie klappt das double Buffering scheinbar nicht
void CMyEdit::OnNcPaint() { Default(); CWindowDC dc(this); CBitmap m_edtbgbmp; m_edtbgbmp.DeleteObject(); m_edtbgbmp.LoadBitmap(IDB_BMP_EDT); //m_Brush ist ein protected member von dieser class m_Brush.DeleteObject(); m_Brush.CreatePatternBrush(&m_edtbgbmp); //1. Ansatz mit der CMemDC Class CMemDC memdc(&dc); memdc.FillRect(m_rectNCBottom, &m_Brush); memdc.FillRect(m_rectNCTop, &m_Brush); //2. Ansatz mit einer CDC CDC mydc; mydc.CreateCompatibleDC(&dc); CBitmap *poldbmp = mydc.SelectObject(&edtbgbmp); dc.BitBlt(0,0,m_rectNCBottom.right,m_rectNCBottom.bottom,&mydc,0,0,SRCCOPY); dc.BitBlt(0,0,m_rectNCTop.right,m_rectNCTop.bottom,&mydc,0,0,SRCCOPY); mydc.SelectObject(poldbmp); }
Wenn ich zwischen zwei Subdialogen mit dieser Paint-Methode hin-und-her-schalte, dann flackern diese Editboxen.
Gruß
Patrick
-
Das kann nicht funktionieren. WM_NCPAINT ist dafpr gar nicht gedacht. Eher für Popup-Fenster und deren Non-Client Area.
Es wird in jedem Fall zweimal gezeichent und durch WM_PAINT/WM_ERASEBKGND wird Dein Edit Control erneut gezeichnet...Der korrekte Weg wäre das im WM_CTLCOLOR Handler (wenn es ein Brush ist) zu machen oder eben im WM_ERASEBKGND, wenn es den gesamten Hintergrund betrifft.
BTW: Alle Windows Controls mit ComCTl 6.0 benutzen Double Bufgfering. Man kann nur technisch WM_PAINT nicht subclassen ohne dessen Funktion zu verfälschen.
-
Ok, ich hab mal den OnNcPaint() disabled, so dass diese Methode nicht mehr aufgerufen wird.
Am Besten ich poste erstmal den kompletten relevanten Code für die Zeichnenroutine für die Editboxen. Ich hab diese nämlich vergrößert und möchte über die OnNcCalcSize erreichen, dass der Text in der Editbox trotzdem vertikal und horizontal zentriert geschrieben wird. Das funktioniert auch mit dieser Methode.
Eine OnPaint() oder OnEraseBackground() Methode hatte ich bis jetzt nicht implementiert. Lediglich die von dir angesprochene CtlColor Methode. Aber leider flackert das Bild vom Editfeld trotzdem.
void CMyEdit::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) { CRect rectWnd, rectClient; //calculate client area height needed for a font CFont *pFont = GetFont(); CRect rectText; rectText.SetRectEmpty(); CDC *pDC = GetDC(); CFont *pOld = pDC->SelectObject(pFont); pDC->DrawText(_T("Ky"), rectText, DT_CALCRECT | DT_LEFT); UINT uiVClientHeight = rectText.Height(); pDC->SelectObject(pOld); ReleaseDC(pDC); //calculate NC area to center text. GetClientRect(rectClient); GetWindowRect(rectWnd); ClientToScreen(rectClient); UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2; UINT uiCY = (rectWnd.Height() - rectClient.Height()) / 2; UINT uiCX = (rectWnd.Width() - rectClient.Width()) / 2; rectWnd.OffsetRect(-rectWnd.left, -rectWnd.top); m_rectNCTop = rectWnd; m_rectNCTop.DeflateRect(uiCX, uiCY, uiCX, uiCenterOffset + uiVClientHeight + uiCY); m_rectNCBottom = rectWnd; m_rectNCBottom.DeflateRect(uiCX, uiCenterOffset + uiVClientHeight + uiCY, uiCX, uiCY); lpncsp->rgrc[0].top +=uiCenterOffset; lpncsp->rgrc[0].bottom -= uiCenterOffset; lpncsp->rgrc[0].left +=uiCX; lpncsp->rgrc[0].right -= uiCY; }
HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor) { if(m_rectNCTop.IsRectEmpty()) { SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); } CRect rectClient; GetClientRect(rectClient); int height = rectClient.Height(); int width = rectClient.Width(); HBRUSH hbr; CBitmap m_edtbgbmp; m_edtbgbmp.DeleteObject(); m_edtbgbmp.LoadBitmap(IDB_BMP_EDT); BITMAP bm; m_edtbgbmp.GetObject(sizeof(bm), &bm); CDC speicherDC; speicherDC.CreateCompatibleDC(GetDC()); speicherDC.SelectObject(&m_edtbgbmp); CDC BmpDC; CBitmap KleinesBmp; //erstelle bitmap mit folgenden werten (weite und höhte) KleinesBmp.CreateCompatibleBitmap(GetDC(),width,height); BmpDC.CreateCompatibleDC(GetDC()); BmpDC.SelectObject(&KleinesBmp); BmpDC.BitBlt(0, 0, width, height, &speicherDC, 0, height, SRCCOPY); m_Brush.DeleteObject(); m_Brush.CreatePatternBrush(&KleinesBmp); hbr=(HBRUSH)m_Brush; pDC->SetBkMode(TRANSPARENT); pDC->SetBkColor(TRANSPARENT); return hbr; }
Was mache ich in dieser Methode noch falsch bzw. muss ich anders machen, damit das Flackern ein Ende nimmt?
Gruß
Patrick
-
Male die Bitmap in OnEraseBkgnd.
BTW: Warum benutzt WM_NCCALCSIZE es gibt dich EM_SETRECT.
-
Hi Martin,
verwende ich diese Sendmessage richtig im OnPaint() Handler? Bis jetzt funktioniert es leider noch nicht, dass der Text vertikal zentriert dargestellt wird im Editfeld.
void CMyEdit::OnPaint() { CRect rectClient; CRect rectWnd; GetClientRect(rectClient); GetWindowRect(rectWnd); ClientToScreen(rectClient); SendMessage(EM_SETRECT,0,(LPARAM)&rectClient); }
-
Nein! In OnPaint hat das nichts zu suchen. EM_SETRECT (BTW: Es gibt auch CEdit::SetRect) wird irgendwo nach dem erzeugen gesetzt.
In OnPaint hat solch ein Code, der das Format ändert nichts zu suchen. Das würde rekursiv ja gerade wieder ein OnPaint auslösen.BTW: Hebelst Du mit dem Code, den ganzen normalen Zeichen-Algorithmus aus.
-
Das Editfeld ist eine Membervariable von einer Klasse. Wenn ich in der OnInitDialog(-Methode dieser Klasse SetRect() verwende und mir dann die Größe vom ClientRect() in der OnPaint() Methode vom Editfeld ansehe, dann wird dieses ClientRect nicht verändert in der Größe...
//OnInitDialog() m_EdtField.ModifyStyle(ES_RIGHT | ES_LEFT , ES_CENTER); m_EdtField.SetFont(&m_StatFont); CRect rectClient; m_EdtField.GetClientRect(rectClient); //(0, 46, 0, 65) rectClient.bottom = 10; //(0, 10, 0, 65) m_EdtField.SendMessage(EM_SETRECT,0,(LPARAM)&rectClient); m_EdtField.SetRect(rectClient);
void CMyEdit::OnPaint() { CPaintDC dc(this); // device context for painting CRect rectClient; GetClientRect(rectClient); //(0, 46, 0, 65) }
Hab auch schon versucht Multiline auszuwählen für das Editfeld - aber auch ohne Erfolg.
Gruß
Patrick