2 dringende Fragen zu Trackbar-Property und OnPaint beim Panel



  • Hallo Forum, 🙂

    meine Uni zwingt mich ein Projekt in CLI C++ zu machen und ich hab da zwei Fragen und hoffe jemand hier kann mir helfen. Bevor ich hier gepostet habe habe ich gesucht/ gegoogelt etc., leider ohne Erfolg.

    1. Ich habe Trackbars im Programm, dafür kann ich eine Hintergrundfarbe setzen, ich würde zusätzlich gern den Rest der Trackbar auch mit einer bestimmten Farbe setzten, z. B. die Striche der Skala der Trackbar. Aber wie?

    2. Ich habe dieses Tutorial gelesen und es in CLI C++ nachprogrammiert. In dem Panel aus dem Tutorial drehen sich Elipsen, ich bräuchte so ein transparentes Panel um verschiedene PNG-Grafiken anzuzeigen, damit man dadurch das darunterliegende Panel sehen kann.
      Ok ich versuch Frage zwei nochmal zu präzisieren. Ich habe ein paar Spielsteins im Programm die Grafiken dazu sind PNG's mit Transparenz, kann mir jemand helfen wie ich das Beispiel verwende um statt der Elipse meine Grafiken anzuzeigen?

    Hier mein CLI C++ Code vom Tutorial (Elipse auskommentiert)

    // Panel mit echter Transparenz
    // 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
    {
    	static Timer^ tTimer = gcnew Timer();
    	static Random^ rRand = gcnew Random();
    
        public: TransparentPanel()
    	{
    		tTimer->Tick += gcnew EventHandler( this, &TransparentPanel::TickHandler );
    		this->tTimer->Interval = 500;
    		this->tTimer->Enabled = true;
    	}
    
        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
    	{
    		/*Int32 h = this->Height / 2;
    		Int32 w = this->Width / 2;
    		Pen^ pPen = gcnew Pen( Color::Black, 2 );
    		Int32 x = 0;
    		Int32 y = 0;
    		for ( x = 0, y = 0 ; x < w ; x += w / 10, y += h / 10 )
    		{
    			e->Graphics->DrawEllipse( pPen, 
    									  x + rRand->Next(10) - 5, 
    									  y + rRand->Next(10) - 5, 
    									  this->Width - (2 * x), 
    									  this->Height - (2 * y) );
    		}*/
    		//pPen->Dispose(); // Stift wegschmeissen
    	}
    };
    

    Danke fürs Lesen, hoffe jemand kann helfen, CLI C++ ist ja nicht mehr so verbreitet scheint mir und das zu recht. 😉



  • Bei deinen konkreten Probleme kann ich dir leider nicht helfen.
    Allerdings noch ein paar Hinweise:
    - Warum sind tTimer und rRand static?
    - In C++/CLI wird Dispose() nicht direkt aufgerufen sondern wie folgt:

    Pen^ pPen = gcnew Pen( Color::Black, 2 );
    // nicht so: pPen->Dispose();
    // sondern so:
    delete pPen;
    


  • theta schrieb:

    Bei deinen konkreten Probleme kann ich dir leider nicht helfen.

    Schade 😞

    theta schrieb:

    Allerdings noch ein paar Hinweise:
    - Warum sind tTimer und rRand static?

    Es ist ein Prototyp ich wollte erstmal was sehen, static habe ich sie gemacht weil der Compiler gemeckert hat, ohne ging es irgendwie nicht.

    theta schrieb:

    - In C++/CLI wird Dispose() nicht direkt aufgerufen sondern wie folgt:

    Pen^ pPen = gcnew Pen( Color::Black, 2 );
    // nicht so: pPen->Dispose();
    // sondern so:
    delete pPen;
    

    Ok danke, aber der Pen zeichnet ja die Elipsen aus dem Beispiel die brauche ich nicht mehr sobald ich da ein Bild habe, hatte mich nur gewundert, dass es Dispose() gibt, man es aber nicht aufrufen kann.



  • gast__1 schrieb:

    theta schrieb:

    Allerdings noch ein paar Hinweise:
    - Warum sind tTimer und rRand static?

    Es ist ein Prototyp ich wollte erstmal was sehen, static habe ich sie gemacht weil der Compiler gemeckert hat, ohne ging es irgendwie nicht.

    Ok. static ist nicht nötig und der falsche Weg, den Code zum kompilieren zu bringen.



  • theta schrieb:

    gast__1 schrieb:

    theta schrieb:

    Allerdings noch ein paar Hinweise:
    - Warum sind tTimer und rRand static?

    Es ist ein Prototyp ich wollte erstmal was sehen, static habe ich sie gemacht weil der Compiler gemeckert hat, ohne ging es irgendwie nicht.

    Ok. static ist nicht nötig und der falsche Weg, den Code zum kompilieren zu bringen.

    Ok verstehe, ich versuche gerade in der onPaint() eine PictureBox zu machen, das will leider noch nicht ganz.



  • Und was heisst

    Ok verstehe, ich versuche gerade in der onPaint() eine PictureBox zu machen, das will leider noch nicht ganz.

    genau?



  • theta schrieb:

    genau?

    protected: virtual void OnPaint(PaintEventArgs^ e) override
    	{	
    		Image^ imImage = Image::FromFile( "../Grafik/test.png" );
    		e->Graphics->DrawImage( imImage, 0, 0 );		
    	}
    

    Das klappt soweit, ist aber keine PictureBox, allerdings flackert das Panel daher denke ich OnPaint ist evtl. die falsche Funktion dazu. Ich dachte man kann beim Konstruktor dann reingeben welche Grafik gezeichnet werden soll, schwierig ist halt das sich die Grafik im Spielverlauf ändern kann und da weiß ich noch nicht wie ich das mache, ich habe gelesen Grafiken drehen (Spielstein dreht sich) geht nur in VB zumindest wenn man es mit PictureBoxen macht.



  • Du musst im Construktor den Style richtig setzen (UserPaint, ResizeRedraw und ganz wichtig: AllPaintingInWmPaint und OptimizedDoubleBuffer)

    Siehe:
    http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx



  • Jochen Kalmbach schrieb:

    Du musst im Construktor den Style richtig setzen (UserPaint, ResizeRedraw und ganz wichtig: AllPaintingInWmPaint und OptimizedDoubleBuffer)

    Siehe:
    http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx

    Danke für den Hinweis leider kann ich DoubleBuffering nicht nutzen wenn ich das transparente Panel aus dem Tutorial verwende. Zitat aus dem Tutorial:

    Panel does little or no painting itself and does not use the double buffered style internally. Other controls which already make assumptions about the way they will display might not be suitable for this technique. For example, any control that uses double buffering internally will certainly not be suitable and will show up with a black, opaque background.

    Heißt das ohne DoubleBuffering kriege ich das flackern nicht weg? Ich versuch mal einen anderen ExStyle, hoffe das man dann Buffern kann.



  • 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?


Anmelden zum Antworten