Laden...

Auf Image zeichnen? Refresh?

Erstellt von SGT_BOB vor 19 Jahren Letzter Beitrag vor 19 Jahren 5.096 Views
S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren
Auf Image zeichnen? Refresh?

Hallo Leute.

Ich hab folgendes Problem.

Ich hab eine PictureBox in der ein Bild geladen wird. Ein 32x32 großes Rechteck wird darauf gezeichnet. Dieses soll (momentan mit zwei "häßlichen" Buttons 😉) auf dem Bild "verschoben" werden. Es soll also immer 32 Pixel nach rechts oder links wandern. Funzt auch soweit.

ABER die "alten" Rechtecke werden nicht gelöscht. Warum??? Refresh???

Hier mal der Quellcode in dem ich grad herumexperimentier:


class GraphMDI: Form
{
	Image template = Image.FromFile(@"TileSet_256x768.png");
	
	PictureBox pbox = new PictureBox();
	Graphics g;
	
	Button left = new Button();
	Button right = new Button();
	
	int curPosX = 0;
	int curPosY = 0;
	
	public GraphMDI()
	{
		
		this.AutoScroll = true;
		pbox.Parent = this;
		pbox.Location = new Point(0,0);
		pbox.Size = new Size(template.Size.Width, this.Size.Height);
		pbox.Image = template;
		pbox.SizeMode = PictureBoxSizeMode.AutoSize;
		g = Graphics.FromImage(template);
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		
		left.Parent = this.pbox;
		left.Location = new Point(5,5);
		left.Size = new Size(40,20);
		left.Text = "<-";
		left.Click += new EventHandler(LeftClick);
		
		right.Parent = this.pbox;
		right.Location = new Point(50,5);
		right.Size = new Size(40,20);
		right.Text = "->";
		right.Click += new EventHandler(RightClick);
		
		this.Size = new Size(pbox.Image.Width, 300);
		Paint += new PaintEventHandler(OnPaint);
	}
	
	void OnPaint(Object obj, PaintEventArgs e)
	{
		Graphics g = e.Graphics;
		g.DrawImage(template, 0,0);
		this.Update();
		//g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("OnPaint-Methode");
	}
	
	void LeftClick(object obj, EventArgs ea)
	{
		curPosX -= 32;
		g = Graphics.FromImage(template);
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		this.Refresh();
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
	
	void RightClick(object obj, EventArgs ea)
	{
		curPosX += 32;
		g = Graphics.FromImage(template);
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		
		this.Refresh();
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
}


Danke schon mal für die Antworten. 😁

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

P
939 Beiträge seit 2003
vor 19 Jahren

Man muss sich klar machen wo gezeichnet wird. Du zeichnest auf dem Graphicskontext eines Images. Der Refresh-Aufruf bezieht sich jedoch auf den Graphicskontext des PictureBox-Controls. Die Wirkung ist, die PictureBox-Ansicht wird zwar aktualisiert, aber mit der Ansicht des Images, die sich nicht verändert hat.

Die Image-Ansicht wird nicht automatisch gelöscht, so wie es bei Controls der Fall ist. Wenn da irgendetwas animiert werden soll, muss in jedem Frame per Hand gelöscht werden.

Gruss
Pulpapex

C
122 Beiträge seit 2004
vor 19 Jahren

Wenn ich das richtig sehe, liegt das Problem da drin, das du bei jedem Klick ein neues Quadrat machst....

//Edit: wie immer bin ich zu langsam -.-

Zwei Dinge sind unendlich: Das Universum und die menschliche Dummheit. Aber beim Universum bin ich mir nicht ganz sicher. - Albert Einstein

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

Erst mal Danke für die schnellen Antworten.

@Pulpapex:

Ich erklär glaub ich erst mal worauf das ganze hinauslaufen soll. Ich möchte aus Grafiken Tiles ausschneiden. Momentan sollen die 32x32 Pixel groß sein.

Also hab ich mir gedacht, nimmste ne PictureBox und packst das Image da rein.
Das gezeichnete Rechteck soll einfach nur zur Auswahl des zukünftigen Tiles sein.

Heißt das jetzt ich soll auf dem Image zeichnen oder auf der PictureBox. wenn ich auf der PictureBox zeichne, dann seh ich das Rechteck nicht.
Hab jetzt den Refresh bzw. das Update für die PictureBox ausgeführt, nicht mehr für die Form. Problem bleibt trotzdem bestehen.
Bin in Sachen GDI+ leider absoluter Neuling, weiß also leider nicht ganz wo drauf du hinaus willst. 🙁

Vielleicht kannst du etwas genauer werden, wenns nicht zu viel verlangt ist.

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

P
939 Beiträge seit 2003
vor 19 Jahren

Es soll also eine Art Overlay über das eigentliche Bild gezeichnet werden. Das müsste recht einfach hinzubekommen sein. Wie gesagt muss zwischen dem PictureBox-Control als Zeichenfläche und dem Image-Objekt als Zeichenfläche unterschieden werden.

Du könntest den Auswahlrahmen statt ins Image, auf das Control zeichnen. Dazu muss von PictureBox abgeleitet und die OnPaint-Methode überschrieben werden (es geht auch ohne abzuleiten, aber so ist es glaube ich besser).

public class TileEditor : PictureBox {

  protected override void OnPaint(PaintEventArgs e) {

    // Basisimplementierung ausführen.
    // Die PictureBox zeichnet ihr Image.
    base.OnPaint(e);

    // Auswahlrahmen aufs Control zeichnen (nicht aufs Image).
    // Erscheint über dem schon gezeichneten Image.
    Graphics g = e.Graphics;
    g.DrawRectangle(Pens.Red, curPosX, curPosY, 32, 32);
  }
}

Probier' es mal aus, ich hoffe es funktioniert so.

Gruss
Pulpapex

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

Hey, erst mal danke.

Das sieht doch schon ganz gut aus und vor allem verständlich 😉. Ich werds gleich mal probieren.

Danke schön.

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

Habs mal so versucht, zum mindest so wie ich es verstanden hab. 😁

Leider hat es nicht so ganz funktioniert.

Wenn ich das richtig verstanden habe, zeichnete ich vorher auf dem Image und nicht auf dem PictureBox-Steuerelement, richtig? So das hab ich glaub ich jetzt nicht mehr. Leider aber immer noch mein Problem.


using System;
using System.Drawing;
using System.Windows.Forms;


class GraphMDI: Form
{
	Image template = Image.FromFile(@"TileSet_256x768.png");
	
	PictureBox pbox = new PictureBox();
	Graphics g;
	
	Button left = new Button();
	Button right = new Button();
	
	int curPosX = 0;
	int curPosY = 0;
	
	public GraphMDI()
	{
		
		this.AutoScroll = true;
		pbox.Parent = this;
		pbox.Location = new Point(0,0);
		pbox.Size = new Size(template.Size.Width, this.Size.Height);
		pbox.Image = template;
		pbox.SizeMode = PictureBoxSizeMode.AutoSize;
		g = Graphics.FromImage(template);
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		
		left.Parent = this.pbox;
		left.Location = new Point(5,5);
		left.Size = new Size(40,20);
		left.Text = "<-";
		left.Click += new EventHandler(LeftClick);
		
		right.Parent = this.pbox;
		right.Location = new Point(50,5);
		right.Size = new Size(40,20);
		right.Text = "->";
		right.Click += new EventHandler(RightClick);
		
		this.Size = new Size(pbox.Image.Width, 300);
		Paint += new PaintEventHandler(OnPaint);
	}
	
	void OnPaint(Object obj, PaintEventArgs e)
	{
		Graphics g = e.Graphics;
		pbox.Refresh();
		//g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("OnPaint-Methode");
	}
	
	void LeftClick(object obj, EventArgs ea)
	{
		curPosX -= 32;
		g = pbox.CreateGraphics();
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
	
	void RightClick(object obj, EventArgs ea)
	{
		curPosX += 32;
		g = pbox.CreateGraphics();
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
}

Bei dem Vorschlag von Pulpaplex kommt es doch meiner Meinung nach aufs Selbe raus.
Ich erstelle mir eine Klasse die von PictureBox abgeleitet wird und überschreibe deren Methode "OnPaint()". Dann erstelle ich mir eine Instanz in meiner Hauptform und greife dann auf diese zu.

Oh man, entweder hab ich im moment zu wenig schlaf oder ich hab einfach nur ein Brett vorm Kopf. X(

Vielleicht kann sich noch mal jemand die Arbeit machen mir zu helfen und es genauer erklären. Eigentlich bin ich nicht mehr des blutige Anfänger, hab aber schon seit einiger Zeit nicht mehr viel getan.

Pleaze help !!!

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

Also so wie ich es jetzt habe funktioniert es mit dem links und rechts verschieben.
Leider zeichnet er mir beim Start noch kein Rechteck, obwohl ich dies aufrufe.


using System;
using System.Drawing;
using System.Windows.Forms;


class GraphMDI: Form
{
	Image template = Image.FromFile(@"TileSet_256x768.png");
	
	PictureBox pbox = new PictureBox();
	Graphics g;
	
	Button left = new Button();
	Button right = new Button();
	
	int curPosX = 0;
	int curPosY = 0;
	
	public GraphMDI()
	{
		
		this.AutoScroll = true;
		pbox.Parent = this;
		pbox.Location = new Point(0,0);
		pbox.Size = new Size(template.Size.Width, this.Size.Height);
		pbox.Image = template;
		pbox.SizeMode = PictureBoxSizeMode.AutoSize;
		g = pbox.CreateGraphics();

// Hier sollte doch das erste Rechteck an Pos(0,0) mit Größe(32,32) gezeichnet
// werden, oder nicht???
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		

		left.Parent = this.pbox;
		left.Location = new Point(5,5);
		left.Size = new Size(40,20);
		left.Text = "<-";
		left.Click += new EventHandler(LeftClick);
		
		right.Parent = this.pbox;
		right.Location = new Point(50,5);
		right.Size = new Size(40,20);
		right.Text = "->";
		right.Click += new EventHandler(RightClick);
		
		this.Size = new Size(pbox.Image.Width+30, this.Height);
		Paint += new PaintEventHandler(OnPaint);
	}

	
	void OnPaint(Object obj, PaintEventArgs e)
	{
		Graphics g = e.Graphics;

		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("OnPaint-Methode");
	}

// funktioniert...	
	void LeftClick(object obj, EventArgs ea)
	{
		curPosX -= 32;
		g = pbox.CreateGraphics();
		pbox.Refresh();
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
// funktioniert auch...	
	void RightClick(object obj, EventArgs ea)
	{
		curPosX += 32;
		g = pbox.CreateGraphics();
		pbox.Refresh();
		g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);
		Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
	}
}

Ich weiß daß ich nerve, aber ich würde es gerne verstehen, warum er mir das nicht beim Start schon zeichnet. ?(

Kann das Paint-Ereignis auch noch anders aufgerufen werden (von mir selbst), außer über eine Formveränderung?

Ich hätte da direkt noch eine Frage.
Gegebenenfalls ich möchte genau den aktuellen, mit dem Rechteck gewählten, Ausschnitt des Bildes(z.B.: 64,96,32,32) in die Zwischenablage oder sonst wohin kopieren. Wie würde das gehen, wo soll ich da ansetzen???

Danke nochmal für die bisherige Hilfe und auch schon mal für zukünftige Antworten. 😁

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

49.485 Beiträge seit 2005
vor 19 Jahren

Hallo SGT_BOB,

Teilantwort:

Kann das Paint-Ereignis auch noch anders aufgerufen werden (von mir selbst), außer über eine Formveränderung?

Nicht aufgerufen aber angestoßen:


Inavlidate ();
Update ();

herbivore

1.373 Beiträge seit 2004
vor 19 Jahren
  
g = pbox.CreateGraphics();  
  
// Hier sollte doch das erste Rechteck an Pos(0,0) mit Größe(32,32) gezeichnet  
// werden, oder nicht???  
g.DrawRectangle(Pens.Red, curPosX,curPosY,32,32);  
  

Eigentlich schon. Aber sobald die Form neu gezeichnet wird, wird der Inhalt der Picturebox wieder gelöscht (weil sich die PB selbst neu zeichnet). Daher sollte Code zum zeichnen immer in das Paint event.

Aber ich würde es an deiner Stelle einfach so machen: da du eh nur auf die PictureBox zeichnen willst, warum meldest du dich dann nicht bei dessen Paint event an:


pbox.Paint += new PaintEventHandler(OnPaint);

Die Button handler werden dann sehr einfach:


void LeftClick(object obj, EventArgs ea)
{
    curPosX -= 32;
    pbox.Refresh();
    Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
}
void RightClick(object obj, EventArgs ea)
{
    curPosX += 32;
    pbox.Refresh();
    Console.WriteLine("curPosX = " + curPosX+ "curPosY = " + curPosY);
}

Der Code der Paint Methode ändert sich nicht.

MfG VizOne

49.485 Beiträge seit 2005
vor 19 Jahren

Hallo zusammen,

der Vorteil eine PictureBox liegt m.E. gerade darin, dass man OnPaint gar nicht braucht. Man zeichnet in die PictureBox bzw. dessen Image, wenn es ansteht (Benutzerinteraktion, Timer, ...) und und für die Aktualisierung aus anderen Gründen (Fensterreihenfolge ändern, Fenster minimieren/wiederherstellen) sorgt die PictureBox.

herbivore

1.373 Beiträge seit 2004
vor 19 Jahren

Naja, dann fehlt ihm aber noch ein zweites image. "template" scheint ja nur das hintergrund bild zu sein. Und wird er nicht destruktiv drauf zeichnen wollen. Er müsste dann schon ein weiteres image haben, auf das er das Hintergrundbild zeichnet und darüber die Rechtecke. Geht natürlich auch und hat evtl gewisse Vorteile (das gesamte Bild inkl. Rechtecke könnte Beispielsweise gedehnt/gestaucht werden usw). Liegt immer daran, was man haben will. Ich würde es wahrscheinlich sowieso in einem eigenen Control kapseln.

MfG VizOne

49.485 Beiträge seit 2005
vor 19 Jahren

Hallo zusammen,

wenn der Hintergrund nicht zerstört werden soll, kann zwei PictureBoxen transparent übereinanderlegen. Das hatten wir schon mal: Transparente Picturebox .

herbivore

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

Also erstmal danke für die vielen Antworten und die heiße Diskussionsrunde. 🙂

Ich werd es mal aufklären, was ich genau vorhab. Es soll ein Programm zur Bearbeitung von Tilesets werden. Dabei sollen aus dem einen Bild die gewählten Teile in ein neues oder vorhandenes Bild eingefügt werden können. Die "Template" aus dem einfachen Grund, weil ich da ein Schachbrettmuster in der Größe 32x32 drauf habe. Da läßt sich leichter erkennen, ob alles passt. 😁

Wie gesagt "bewegen" kann ich das Rechteck schon. Was ich nicht gemacht hab Paint Ereignis für die PictureBox aufzurufen. Das ist schon wahr. Werds aber mal probieren.

Vielleicht hat ja noch jemand eine Idee, wie ich den aktuell ausgewählten Ausschnitt des "Originalbildes" in die Zwischenablage kopieren kann. Da ich ja immer nur einzelne Teile des Bildes brauche(z.B. 32x32 Pixel groß).

CU @ll SGT_BOB

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

S
SGT_BOB Themenstarter:in
125 Beiträge seit 2005
vor 19 Jahren

@VizOne:

Jippie... genau das war mein Problem. Manchmal sieht man den Wald vor lauter Bäumen nicht. 😁

Danke an alle.

ABER... Mein Problem von weiter oben (das mit der Zwischenablage) besteht immer noch. X(

*************************
Ich bin root, ich darf das... 😜
root>_
*************************