007
25.02.2006, 21:55 Uhr
Spacelord
Hoffnungsloser Fall
|
@mmc20: Hi,ich hatte heute morgen schon begonnen ne Antwort zu schreiben,wurde dann unterbrochen und hatte den Text dann gespeichert.Sei mir also nicht böse wenn meine Antwort jetzt deinen Beitrag "ignoriert" .
@kleineSchildy: Hallo, mit diesem Thread hatte ich gerechnet . Das Problem dass du schilderst hatte ich dir ja in dem anderen Thread schon angekündigt. Was ich da gepostet hatte war nunmal die allereinfachste Variante die überhaupt machbar ist. Damit dein Bild beim Neuzeichnen nicht verschwindet ist,wie Guybrush Threepwood schon geschrieben hat, OnPaint genau der richtige Ort. OnPaint wird jedesmal aufgerufen wenn ein Bildschirmbereich deines Fensters als ungültig erklärt wurde und neu gezeichnet werden muss.Die Gültigkeit des Zeichenbereichs ist auch eines deiner Probleme. Ganz grob gesagt(und technisch eigentlich nicht richtig) ruft dein Dialog für all seine Kindfenster(Buttons Statics etc) auch die OnPaint auf.Interessant ist da immer der Teil CPaintDC dc(this). Dahinter verbergen sich WinApi Aufrufe von BeginPaint und EndPaint (EndPaint wird erst im Destruktor von CPaintDC aufgerufen,also nach dem Verlassen von OnPaint)wodurch sich deine Kindelemente neu zeichnen und wieder für gültig erklären.Das Problem ist jetzt halt dass das CStatic Feld dass du als Leinwand nimmst überhaupt nichts von dem Bild weiss das es zeichnen soll!Du benutzt das Staticfeld ja nur indirekt.Der Dialog wird erst nach dem Verlassen von OnPaint,mit den Daten die die Kindfenster und der Dialog geliefert haben,"aktuallisiert"...also werden deine Zeichenbemühungen brutal übermalt . Damit es überhaupt erstmal funktioniert müsstest du,nachdem du das Bild gezeichnet hast,den Bereich des Statics explizit als gültig erklären. Eine funktionierende (aber trotzdem schlechte) Variante würde dann so aussehen:
C++: |
void CCDSEM_Bild_ViewerDlg::OnPaint() { Gdiplus::Image image(L"test.TIFF");
if (IsIconic()) { CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); CDC* the_dc = m_cPicture.GetDC(); Gdiplus::Graphics g(the_dc->m_hDC); g.DrawImage(&image,Gdiplus::Point(0,0)); m_cPicture.ReleaseDC(the_dc); m_cPicture.ValidateRect(NULL); } }
|
Diese Lösung ist aber ziemlich übel! Da gehen jedem Programmierer die Nackenhaare hoch wenn so komplexe Objekte wie Bilder lokal angelegt werden und somit bei jedem Betreten von OnPaint angelegt,geladen und beim Verlassen wieder zerstört werden.Das mag bei nem 50KB tiff Bild noch garnichtmal sooo auffallen,aber probier das Gleiche mal mit nem 20MB Bitmap. Das Problem köntest du umgehen indem Du in deiner Dialogklasse einen Zeiger auf ein Image Objekt als Attribut anlegst und diesem in OnInitDialog mit der statischen Methode Image::FromFile ein Image zuordnest und dieses dann erst im Destruktor des Dialogs wieder löscht. Auf diese Lösung werde ich aber nicht weiter eingehen weil eine saubere objektorientierte Lösung nur mit unwesentlich mehr Aufwand verbunden ist und die Wiederverwendbarkeit diesen Minimalaufwand rechtfertigt. Also hier ne kleine Schritt für Schritt Anleitung: 1.Mach nen Rechtsklick und öffne den Klassenassi. 2.Leg ne neue Klasse CPictureStatic an und leite diese von CStatic ab. 3.Leg über die Nachrichtenzuordnungstabelle ne Bearbeitungsfunktion für WM_PAINT an. 4.Deklarier einen Zeiger auf ein Image Objekt als Attribut von CPictureStatic. 5.Initialisier den Zeiger im Konstruktor mit NULL. 6.Setz nen delete auf den Zeiger in den Destruktor . 7.Deklarier ne Methode void SetPicture(wchar_t* path,BOOL B = FALSE)
Der komplette Code der neuen Klasse sieht dann so aus: Der Header
C++: |
class CPictureStatic : public CStatic { // Konstruktion public: CPictureStatic();
// Attribute protected: Gdiplus::Image *image;
// Überschreibungen // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen //{{AFX_VIRTUAL(CPictureStatic) //}}AFX_VIRTUAL
// Implementierung public: void SetPicture(wchar_t* path,BOOL b = FALSE); virtual ~CPictureStatic();
// Generierte Nachrichtenzuordnungsfunktionen protected: //{{AFX_MSG(CPictureStatic) afx_msg void OnPaint(); //}}AFX_MSG
DECLARE_MESSAGE_MAP() };
|
Die Implementierung:
C++: |
#include "stdafx.h" #include "PictureStatic.h" //weitere projektbezogene includes
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
///////////////////////////////////////////////////////////////////////////// // CPictureStatic
CPictureStatic::CPictureStatic() { image = NULL; }
CPictureStatic::~CPictureStatic() { delete image; }
BEGIN_MESSAGE_MAP(CPictureStatic, CStatic) //{{AFX_MSG_MAP(CPictureStatic) ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CPictureStatic
void CPictureStatic::OnPaint() { CPaintDC dc(this); if(image != NULL) { Gdiplus::Graphics g(dc.m_hDC); g.DrawImage(image,Gdiplus::Point(0,0)); } }
void CPictureStatic::SetPicture(wchar_t* path,BOOL b) { delete image; image = Gdiplus::Image::FromFile(path,b); }
|
Jetzt musst du nur noch den Header in deine Dialogklasse inkludieren und dem Static,dem du ja ne Controlvariable vom Typ CStatic zugewiesen hattest, ne neue vom Typ CPictureStatic zuweisen.
Dann noch in OnInitDialog
C++: |
m_cPicture.SetPicture(L"PfadZuDeinemBild.tiff");
|
und feddich....
Ab jetzt sorgt das Staticfeld selber dafür dass bei Bedarf alles neu gezeichnet wird und du musst in der OnPaint von deinem Dialog garnicht mehr rumfummeln.
Und das Beste ist dass du in dem nächsten Projekt,wo du nen Bild anzeigen willst,nur noch die beiden Dateien einfügen musst,ne Variable anlegst und diese(eventuell über SubclassDlgItem) einem Staticfeld zuordnest. Dann noch mit SetPicture nen Bild festlegen und dann war es das. Damit bist du in Zukunft mit 2-3 Zeilen Code in der Lage nen "beliebiges" Bild anzuzeigen
MfG Spacelord -- .....Ich mach jetzt nämlich mein Jodeldiplom.Dann hab ich endlich was Eigenes. Dieser Post wurde am 25.02.2006 um 21:59 Uhr von Spacelord editiert. |