2 dringende Fragen zu Trackbar-Property und OnPaint beim Panel



  • Das Flackern bekommst Du weg, indem Du AllPaintingInWmPaint setzt. Damit wird nämlich verhindert, dass *vor* dem Zeichnen zuerst mal der Hintergrund mit "weiss" gefüllt wird 😉



  • Irgendwie will das nicht klappen. 😞
    Ich habe jetzt im Konstruktor:

    public: TransparentPanel()
    	{
    		tTimer->Tick += gcnew EventHandler( this, &TransparentPanel::TickHandler );
    		this->tTimer->Interval = 500;
    		this->tTimer->Enabled = true;
    		// setze Verhalten des Panels
    		this->SetStyle( ControlStyles::UserPaint, true );
    		this->SetStyle( ControlStyles::ResizeRedraw, true );
    		this->SetStyle( ControlStyles::AllPaintingInWmPaint, true );
    		//this->SetStyle( ControlStyles::OptimizedDoubleBuffer, true );
    	}
    

    Aber es flackert genau wie vor dem Setzen der Styles.

    Edit:

    this->tTimer->Interval = 500;
    		this->tTimer->Enabled = true;
    

    scheinen das Flackern zu verursachen, wenn ich sie auskommentiere flackert es nicht, mir ist nur nicht ganz klar was die beiden Zeilen genau machen.



  • scheinen das Flackern zu verursachen, wenn ich sie auskommentiere flackert es nicht, mir ist nur nicht ganz klar was die beiden Zeilen genau machen.

    this->tTimer->Interval = 500;    // setzt das Timer Interval auf 500ms 
    this->tTimer->Enabled = true;    // started den Timer
    

    Da der Tick- Handler mit der TickHandler Methode verbunden ist, wird dieser nun alle 500ms ausgeführt. Im Handler wird dann den Control Inhalt invalidiert, was ein neuzeichnen zur Folge hat.

    Hier die MSDN Doku: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx



  • Alles klar vielen Dank das hat geholfen. Wenn ich das Ganze nun verwenden möchte in meinem Programm instanziere ich ja den TransparentPanel dabei kann ich nen String übergeben der den Bildpfad enthält, aber was mache ich wenn sich das Bild ändert? Die Instanz durch eine neue ersetzen erscheint mir als der falsche weg. Wie geht es besser?



  • Welches Bild - das hast Du soweit ich das sehen kann - noch nicht erwähnt.



  • theta schrieb:

    Welches Bild - das hast Du soweit ich das sehen kann - noch nicht erwähnt.

    Doch dachte das sei klar geworden.

    Image^ imImage = Image::FromFile( "../Grafik/test.png" );
            e->Graphics->DrawImage( imImage, 0, 0 );
    

    zeichnet ein Bild, ist in meiner OnPaint() auf der Seite davor. Soll ich nochmal alles posten?



  • Ah, jetzt hab ichs gesehen:

    Normale Abfolge:
    1. Image nur einmal laden z.B. im Konstruktor und dann als Member halten
    2. Das Bild dann im Paint Handler zeichnen

    Bild ändern:
    1. Methode BildWechsel(Pfad) oder ähnlich machen, darin das Bild eben neu laden und im Member der Klasse halten. Dann Invalidate(..) aufrufen, was das Paint Ereignis auslöst.



  • Das SetSTyle darfst Du nur *einmal* aufrufen und die Styles entsprechend mit oder verknüpfen!!! Auch darfst Du die schon gesetzten Styles nicht weglöschen (also zuerst GetStyles aufrufen)!



  • Jochen Kalmbach schrieb:

    Das SetSTyle darfst Du nur *einmal* aufrufen und die Styles entsprechend mit oder verknüpfen!!! Auch darfst Du die schon gesetzten Styles nicht weglöschen (also zuerst GetStyles aufrufen)!

    Entschuldigung das konnte ich nicht wissen ich habe es genau wie in der Doku von Visual Studio beschrieben gemacht. Kannst du ein CLI-Beispiel geben, wie du das meinst? Das andere Beispiel aus der Doku Kompiliert leider nicht.
    Fehler: Duplizierte Typen wurden gefunden, aber die Methodendeklarationen sind nicht konsistent (Typen: TransparentPanel; Methoden: get_CreateParams): (0x06000009).

    this->SetStyle( static_cast<ControlStyles>(ControlStyles::DoubleBuffer | ControlStyles::UserPaint | ControlStyles::ResizeRedraw | ControlStyles::AllPaintingInWmPaint), true );
    		this->UpdateStyles();
    

    Wo muss ich da das getStyle() ranhängen?

    Ich habe noch ein Problem eine Instanz von TransparentPanel kann auf keine seiner Public Memberfunktionen zugreifen, warum? Warum greift er auf Panel zu und nicht auf meine Klasse?

    Fehler lautet:
    error C2039: 'get_XCoordinate' : is not a member of 'System::Windows::Forms::Panel'
    error C2039: 'get_YCoordinate' : is not a member of 'System::Windows::Forms::Panel'
    error C2039: 'set_XCoordinate' : is not a member of 'System::Windows::Forms::Panel'
    error C2039: 'set_YCoordinate' : is not a member of 'System::Windows::Forms::Panel'
    error C2040: 'changePicture' : is not a member of 'System::Windows::Forms::Panel'

    Hier der aktuelle Code:

    // Diese Headerdatei ist inspiriert durch den C# Artikel http://www.bobpowell.net/transcontrols.htm
    #pragma once
    
    #include "stdafx.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::ComponentModel;
    
    // Klasse erbt von Panel
    ref class TransparentPanel : public System::Windows::Forms::Panel
    {	
    	public: String^ sImageWithPath;
    	public: Int32^ iXCoords;
    	public: Int32^ iYCoords;
    
    	// Declaration weiterer Memberfunktionen
    	public: Int32^ get_XCoordinate();
    			Int32^ get_YCoordinate();
    			System::Void set_XCoordinate( Int32^ );
    			System::Void set_YCoordinate( Int32^ );		
    			System::Void changePicture( String^ );
    
    	public: TransparentPanel( System::String^ sgetImageWithPath, Int32 iXStartCoords, Int32 iYStartCoords )
    	{		
    		sImageWithPath = sgetImageWithPath;
    		iXCoords = iXStartCoords;
    		iYCoords = iYStartCoords;
    		this->Location = System::Drawing::Point( iXStartCoords, iYStartCoords );		
    		// setze Verhalten des Panels
    		//this->SetStyle(ControlStyles::UserPaint = true || ControlStyles::ResizeRedraw = true || ControlStyles::AllPaintingInWmPaint = true ); // Verhindert, dass *vor* dem Zeichnen zuerst mal der Hintergrund mit "weiss" gefüllt wird.
    		//this->SetStyle( ControlStyles::OptimizedDoubleBuffer, true );		
    	}	
    
    	// System::Drawing::Point kann keine Int32
    	public: System::Void setPanelPosition( int newXCoords, int newYCoords )
    	{
    		this->Location = System::Drawing::Point( newXCoords, newYCoords );
    	}
    
        protected: void TickHandler( System::Object^  sender, EventArgs^ e)
    	{
    		this->InvalidateEx();
    	}
    
        // Aendere Defaultbehavior durch setzen von WS_EX_TRANSPARENT
    	protected: property System::Windows::Forms::CreateParams^ CreateParams
    	{
    		virtual System::Windows::Forms::CreateParams^ get () override
    		{
    			System::Windows::Forms::CreateParams^ cCreateParams = Panel::CreateParams;
    			cCreateParams->ExStyle|=0x00000020;  // WS_EX_TRANSPARENT auf Transparenz setzen.
    			return cCreateParams;
    		}
    	}	
    
        // Annullieren des parent controls, du hast keinen Papa
        protected: void InvalidateEx()
    	{
    		if ( Parent == nullptr )
    		{
    			return;
    		}
    		System::Drawing::Region^ aRegion = gcnew System::Drawing::Region( RectangleF( this->Location, this->Size ) );
    		Parent->Invalidate( aRegion, true );
    	}
    
        // Background zeichenen, soll nichts durcheinander bringen
    	virtual void OnPaintBackground( PaintEventArgs^ pevent ) override
    	{
    		// Background nicht zeichenen, soll doch durchsichtig sein.
    	}  
    
        protected: virtual void OnPaint(PaintEventArgs^ e) override
    	{
    
    		Image^ imImage = Image::FromFile( sImageWithPath );
    		e->Graphics->DrawImage( imImage, 0, 0 );
    	}
    };
    
    Int32^ TransparentPanel::get_XCoordinate()
    {
    	return iXCoords;
    }
    
    Int32^ TransparentPanel::get_YCoordinate()
    {
    	return iYCoords;
    }
    
    System::Void TransparentPanel::set_XCoordinate( Int32^ newXCoords )
    {
    	iXCoords = newXCoords;
    }
    
    System::Void TransparentPanel::set_YCoordinate( Int32^ newYCoords )
    {
    	iYCoords = newYCoords;
    }
    
    System::Void TransparentPanel::changePicture( String^ sNewImageWithPath)
    {
    	sImageWithPath = sNewImageWithPath;
    	Invalidate();
    }
    


  • Sorry für den Doppelpost, hoffe das ist so übersichtlicher.

    Ich habe zwei Versionen Programmiert die eine ist wie hier schon gepostet mit Transparentem Panel flackert aber sehr stark. Die hier vorgeschlagenen Styles habe ich ausprobiert leider hat das das Flackern nicht mindern können.

    Daher habe ich das ganze nochmal mit DoublBuffering geschrieben, jetzt flackert es nicht mehr auch bei größeren Bewegungen nicht, aber ich bekomme es nicht mehr transparent. Statt der Transparenz wird mir immer ein schwarzes Viereck angezeigt. Ich hoffe jemand hier hat eine Idee wie man das wegbekommt.

    Für den DoubleBuffered-Code hätte ich sonst nur die Idee darunter ein Bild mit dem Spielplan zu legen, dabei ist mir unklar, wie ich errechne das ich den Ausschnit anzeige der wirklich unter dem Spielstein ist. Der Spielstein ist 60x60 der Spielplan ist 720x720 die Koordinaten der linken oberen Ecke des Panels habe ich. Hat jemand Ideen?

    Auch würde ich mich freuen, wenn jemand weiß wie ich die C# Anweisung
    Application::ApplicationExit += gcnew EventHandler( &TransparentPanel::MemoryCleanup ); in CLI schreiben muss.

    Mein Code bisher, er ist wieder von einem Tutorial aus inspiriert

    #pragma once
    
    #include "stdafx.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::ComponentModel;
    
    public ref class TransparentPanel : public System::Windows::Forms::Panel
    {		
    	public: String^ sImageWithPath;
    	public: Int32 iXCoords;
    	public: Int32 iYCoords;
    	public: Image^ imObjekt;
    	public: Image^ imObjektFloor;
    	private: static const BufferedGraphics^ NO_MANAGED_BACK_BUFFER = nullptr;
    	private: BufferedGraphicsContext^ GraphicManager;
    	private: BufferedGraphics^ ManagedBackBuffer;
    
    	public: TransparentPanel( System::String^ sgetImageWithPath, System::String^ sGetFloorImage, Int32 iXStartCoords, Int32 iYStartCoords, Int32 iWidth, Int32 iHeight )
    	{
    		//Application::ApplicationExit += gcnew EventHandler( &TransparentPanel::MemoryCleanup );
    		this->SetStyle( static_cast<ControlStyles>(ControlStyles::AllPaintingInWmPaint | ControlStyles::SupportsTransparentBackColor) , true );
    		this->UpdateStyles();
    		GraphicManager = BufferedGraphicsManager::Current;		
    		GraphicManager->MaximumBuffer = System::Drawing::Size( this->Width + 1, this->Height + 1 );
    		ManagedBackBuffer = GraphicManager->Allocate( this->CreateGraphics(), ClientRectangle );
    		sImageWithPath = sgetImageWithPath;
    		iXCoords = iXStartCoords;
    		iYCoords = iYStartCoords;
    		this->BackColor = Drawing::Color::FromArgb(0,0,0,0);
    		this->Location = System::Drawing::Point( iXStartCoords, iYStartCoords );
    		this->Size = System::Drawing::Size( iWidth, iHeight );
    		imObjekt = Image::FromFile( sImageWithPath );
    		imObjektFloor = Image::FromFile( sGetFloorImage );
    	}	
    
    	private: void MemoryCleanup( System::Object^  sender, System::EventArgs^  e )
        {
            // clean up the memory
            if ( ManagedBackBuffer != NO_MANAGED_BACK_BUFFER )
    		{
    			delete ManagedBackBuffer;
    		}
        }
    
    	protected: virtual void OnPaint( PaintEventArgs^ e ) override
        {        
    		DrawImage( ManagedBackBuffer->Graphics ); // Zeichne Bild in den Arbeitsspeicher.
    		ManagedBackBuffer->Render( e->Graphics ); // Nun Arbeitsspeicherbild auf den Bildschirm!		
    		//this->BackgroundImage = Image::FromFile( "../Grafik/invisible.png" );
    		//this->BackgroundImageLayout = System::Windows::Forms::ImageLayout::Stretch;
    		//this->BackColor = System::Drawing::Color::Maroon;
    
        }
    
    	private: void DoubleBufferedControl_Resize( System::Object^  sender, System::EventArgs^  e )
        {
            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
    		{
    			delete ManagedBackBuffer;
    		}
    
    		GraphicManager->MaximumBuffer = System::Drawing::Size(this->Width + 1, this->Height + 1);
            ManagedBackBuffer = GraphicManager->Allocate( this->CreateGraphics(), ClientRectangle );
    		this->Refresh();
        }	
    
    	private: void DrawImage( System::Drawing::Graphics^ e )
    	{	
    		e->DrawImage( imObjektFloor, 0, 0 );
    		e->DrawImage( imObjekt, 0, 0 );		
    	}
    
    	public: System::Int32 get_XCoordinate()
    	{
    		return iXCoords;
    	}
    
    	public: System::Void set_XCoordinate( Int32 newXCoords )
    	{
    		iXCoords = newXCoords;
    	}
    
    	public: System::Int32 get_YCoordinate()
    	{
    		return iYCoords;
    	}
    
    	public: System::Void set_YCoordinate( Int32 newYCoords )
    	{
    		iYCoords = newYCoords;
    	}
    
    	public: System::Void changePicture( String^ sNewImageWithPath)
    	{
    		sImageWithPath = sNewImageWithPath;
    		Invalidate();
    	}
    
    	// Background zeichenen, soll nichts durcheinander bringen
    	public: virtual void OnPaintBackground(PaintEventArgs ^args ) override sealed
    	{
    		// Background nicht zeichenen, soll doch durchsichtig sein.
    	}
    
    	// Aendere Defaultbehavior durch setzen von WS_EX_TRANSPARENT
    	protected: property System::Windows::Forms::CreateParams^ CreateParams
    	{
    		virtual System::Windows::Forms::CreateParams^ get () override
    		{
    			System::Windows::Forms::CreateParams^ cCreateParams = Panel::CreateParams;
    			cCreateParams->ExStyle|=0x00000020;  // WS_EX_TRANSPARENT auf Transparenz setzen.
    			return cCreateParams;
    		}
    	}
    
    	public: System::Void setPanelPosition( int newXCoords, int newYCoords )
    	{
    		this->set_XCoordinate( newXCoords );
    		this->set_YCoordinate( newYCoords );
    		this->Location = System::Drawing::Point( newXCoords, newYCoords );
    	}
    };
    


  • Warum zeichnest Du nicht den gesamten Spielplan? Dann hast Du das Problem mit dem Transparent nicht mehr, oder?



  • Jochen Kalmbach schrieb:

    Warum zeichnest Du nicht den gesamten Spielplan? Dann hast Du das Problem mit dem Transparent nicht mehr, oder?

    Das hat andere Gründe die man in dem Codesnipsel nicht sehen kann das Programm ist etwas länger. Ich habe es jetzt geschafft und zwar zeichne ich eine Paralellogramm darein das Bild des Spielplans und durch den Panel des Spielsteins sieht man nur den Teil der unter dem Spielstein ist. Ist nicht wirklich transparenz reicht für meine Zwecke aber föllig und es flackert nicht.

    Zwei Fragen wären aber noch offen:

    1. Wie schreibe ich aus C# in C++ CLI? Application::ApplicationExit += gcnew EventHandler( &TransparentPanel::MemoryCleanup );
    2. Wie kann ich die Farbe der Skala einer Trackbar festlegen, also gemeint sind die Striche an der Trackbar gerne auch der Nöpel den man mit der Maus clickt dazu. Das stellt sicher, dass die Skala jeder Benutzer sieht, egal welche Farbe er in seinem Windows eingestellt hat. Schwarz auf Schwarz sieht man nicht gut, auch wenn es nur die Skale und nicht der Slider selbst ist. 🙂

    Vielen Dank für eure Vorschläge und Hilfe, diese Community ist klasse. 🙂



  • Frage 1:

    Application::ApplicationExit += gcnew EventHandler( &TransparentPanel::MemoryCleanup );
    

    Genau so wenn TransparentPanel::MemoryCleanup eine statische Methode ist. Wenn sie nicht-statisch ist musst Du als erstes Argument noch das Objekt angeben, auf welchem diese Methode aufgerufen werden soll.

    ABER:
    Was soll das? Was mach MemoryCleanup genau? Möchtest Du nicht viel eher das Disposable Pattern implementern, falls das überhaupt nötig ist? Was hast Du da überhaupt freizugeben???



  • theta schrieb:

    Frage 1:

    Application::ApplicationExit += gcnew EventHandler( &TransparentPanel::MemoryCleanup );
    

    Genau so wenn TransparentPanel::MemoryCleanup eine statische Methode ist. Wenn sie nicht-statisch ist musst Du als erstes Argument noch das Objekt angeben, auf welchem diese Methode aufgerufen werden soll.

    ABER:
    Was soll das? Was mach MemoryCleanup genau? Möchtest Du nicht viel eher das Disposable Pattern implementern, falls das überhaupt nötig ist? Was hast Du da überhaupt freizugeben???

    Der Artikel war Vorbild für mein DoublBuffering. Er gibt beim Verlassen der Funktion den genutzten Speicher wieder frei, das finde ich sehr sinnvoll, oder habe ich da etwas gar nicht verstanden?



  • Er gibt beim Verlassen der Funktion den genutzten Speicher wieder frei, das finde ich sehr sinnvoll, oder habe ich da etwas gar nicht verstanden?

    Application::ApplicationExit wird beim Beenden des Prozesses ausgeführt - dann kommts sowiso nicht mehr draufan - ich würde Dispose natürlich trozdem aufrufen, aber nicht beim Beenden der Applikation, sondern wenn das Control nicht mehr gebraucht wird. Und das macht man über das Dispose(..) Pattern - so wie es im CP Artikel codiert ist, ist es nicht gut.

    EDIT
    Das wird übrigens in einem der Kommetare zu dem Artikel auch bemängelt:
    http://www.codeproject.com/KB/graphics/DoubleBuffering.aspx?msg=1876528#xx1876528xx



  • theta schrieb:

    Application::ApplicationExit wird beim Beenden des Prozesses ausgeführt - dann kommts sowiso nicht mehr draufan - ich würde Dispose natürlich trozdem aufrufen, aber nicht beim Beenden der Applikation, sondern wenn das Control nicht mehr gebraucht wird. Und das macht man über das Dispose(..) Pattern - so wie es im CP Artikel codiert ist, ist es nicht gut.

    Wenn ich das richtig verstehe ist das für mein Programm sogar noch einfacher. Die Spielsteine sind im Hauptbildschirm, der kommt nach allen Benutzerabfragen und Einstellungen und bleibt bis das Programm beendet wird oder der Thread neu gestartet wird. Also räumt das Betriebssystem auf, denke ich mal?

    theta schrieb:

    EDIT
    Das wird übrigens in einem der Kommetare zu dem Artikel auch bemängelt:
    http://www.codeproject.com/KB/graphics/DoubleBuffering.aspx?msg=1876528#xx1876528xx

    Die Kommentare zum Artikel hatte ich nicht gelesen. *schäm*

    Bisher bin ich soweit gekommen das ich mein Panel auch bewegen kann ohne das es flackert, also eine flüssige Bewegung keinen Sprung. Dafür habe ich folgende Funktion geschrieben. Hat jemand ne Idee wie die Geschwindigkeit der Bewegung langsamer kriege? Und beim ersten Schritt wird leider gaanz kurz der Hintergrund falsch angezeigt.

    public: System::Void move( Int32 iDirection, Int32 iStepsToMove )
    	{ 
    		// iDirection erwartet:	1 = nach Oben ( Norden )
    		//						2 = nach Rechts ( Osten )
    		//						3 = nach Unten ( Süden ) 
    		//						4 = nach Links ( Westen )
    		// iStepsToMove ist die Anzahl an Felder ( 1 oder 2 ) die der Spielstein bewegt werden soll.
    		Int32 iPixelToMove = 0;
    
    		switch( iStepsToMove )
    		{
    			case 1: iStepsToMove = 15;	// In 4 Pixelschritten bewegen und ein Feld sind 60 Pixel.
    					break;
    			case 2: iStepsToMove = 30;  // In 4 Pixelschritten bewegen und zwei Felder sind 120 Pixel.
    					break;
    			default: MessageBox::Show("move() erwartet den zweiten Parameter iStepsToMove zwischen 1 und 2.", "Warnung: move() falsch benutzt!", MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
    					 break;
    		}
    
    		switch( iDirection )
    		{
    			case 1: for(Int32 n=0 ; n < iStepsToMove ; n++ )
    					{
    						setPanelPosition( iXCoord, iYCoord - 4 );
    					}				
    					break;
    			case 2: for(Int32 n=0 ; n < iStepsToMove ; n++ )
    					{
    						setPanelPosition( iXCoord + 4, iYCoord );
    					}
    					break;
    			case 3: for(Int32 n=0 ; n < iStepsToMove ; n++ )
    					{
    						setPanelPosition( iXCoord, iYCoord + 4 );
    					} 
    					break;
    			case 4: for(Int32 n=0 ; n < iStepsToMove ; n++ )
    					{
    						setPanelPosition( iXCoord - 4, iYCoord );
    					}
    					break;
    			default: MessageBox::Show("move() erwartet den ersten Parameter iDirection zwischen 1 und 4.", "Warnung: move() falsch benutzt!", MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
    					 break;
    		}		
    	}
    

    Auch würde ich gern mein Objekt drehen, dazu habe ich die RotateTransform() Methode gefunden die ich in meiner onPaint nutzen könnte, da diese System::Drawing::Graphics^ e braucht, wie kann ich eine Funktion schreiben die meinen Panel dreht oder muss ich die Klasse nochmal ableiten und das dort im onPaint machen? Hier meine onPaint.

    private: void DrawImage( System::Drawing::Graphics^ e )
    	{	
    		// baue ein Paralellogramm fuer das Hintergrundbild, also den Spielplan.		
    	    Point pUlCorner = Point( ( ( iXCoord + 1 ) *(-1) ), ( ( iYCoord + 1 ) *(-1) ) );
    	    Point pUrCorner = Point( ( ( iXCoord + 1 ) *(-1) +720 ), ( ( iYCoord + 1 ) *(-1) ) );
    	    Point pLlCorner = Point( ( iXCoord *(-1) ), ( iYCoord *(-1) ) + 720 );
    	    array<Point>^ parallelogram = {pUlCorner,pUrCorner,pLlCorner};
    
    	    e->DrawImage( imObjektFloor, parallelogram );	// Zeichne erst den Spielplan ins Paralellogramm,
    		e->DrawImage( imObjekt, 0, 0 );					// dann den Roboter drauf.		
    	}
    

    Danke für eure Zeit und Hilfe 🙂


Anmelden zum Antworten