Winforms, GDI+ and basic conceptual fightings

This forum is meant for anything you would like to share with other visitors
User avatar
Chris
Posts: 5630
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Winforms, GDI+ and basic conceptual fightings

Post by Chris »

Hi Karl,

Didn't you like my solution? Seems to work well for what you need here...
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
TerryB1
Posts: 7
Joined: Thu Jan 30, 2025 12:08 pm
Location: United Kingdom

Re: Winforms, GDI+ and basic conceptual fightings

Post by TerryB1 »

Hi Karl

I'd be interested in how you get on. Whatever you find easiest is the main thing.

Had you said 6 or any number of "Viewports on a Canvas" rather than "Panels on a Canvas" - that would have worked in just the way you were thinking (I think!).

And yes I know there was a lot of ill-informed comment doing the rounds a few years back - still is I suspect.

Best Regards

Terry
FFF
Posts: 1743
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Re: Winforms, GDI+ and basic conceptual fightings

Post by FFF »

Hi Chris,
(and again i lost my answer, needing too much time to write it. May i say, i hate this site's editor?)

sorry, had a very busy time and could only throw a quick glance to your code, before other tasks teared me away…

Yes, that should work. Hadn’t thought about tackling the job backwards, dividing big rectangles into small squares.

Again, i learned by typing my questions, for what i didn’t understand in the code below:

Code: Select all

METHOD DoPaint(oGraphics AS Graphics) AS VOID

  LOCAL oContainer AS System.Drawing.Drawing2D.GraphicsContainer
  LOCAL oRect AS Rectangle

  oRect := Rectangle{SELF:x - (SELF:boxes * size / 2) , SELF:y - size / 2, SELF:boxes * size, size}

  oContainer := oGraphics:BeginContainer()
  oGraphics:TranslateTransform(SELF:x,SELF:y)
  oGraphics:RotateTransform(SELF:rotation)
  oGraphics:TranslateTransform(-SELF:x,-SELF:y)
  oGraphics:DrawRectangle(oPen, oRect)
  FOR LOCAL n := 1 AS INT UPTO SELF:boxes
   oGraphics:DrawLine(oPen, oRect:Left + n * size, oRect:Top, oRect:Left + n * size, oRect:Bottom)
   oGraphics:DrawString(n:ToString(), oFont , oBrush, oRect:Left + n * size - size / 2, oRect:Top + size / 4)
  NEXT
  oGraphics:EndContainer(oContainer)
 RETURN

So, is it correct to say, the oGraphics operations move the origin from the left upper corner of the rectangle to its middle, rotate it, then retranslates the origin, preserving the rotation, so that subsequent drawings of divisions and letters has not to bother with angles?

So far, i thought i got it, but that can’t be the whole truth, as the container helds not the rectangle, but the whole client screen - so…??

Thx for your patience!
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
FFF
Posts: 1743
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Re: Winforms, GDI+ and basic conceptual fightings

Post by FFF »

Chris,
i reset the location of the picture box to {0,0}, to make it easier to view. Now using only your first BigBox and giving it a 30°rotation, part of the box is outside the client screen. So there is still some misunderstanding on my side, i fear.
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 5630
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Winforms, GDI+ and basic conceptual fightings

Post by Chris »

Hi Karl,

The way I wrote the code, the x,y coordinates you pass for each (big) box represent the position of the _middle_ of the box (not it's upper left corner), in order to make it easier to position it. So if the box is very close to the edge and it is rotated, then it makes sense that part of it goes out of the edge.

Yes, the code works exactly how you described it regarding rotation. Basically the BeginContainer/EndContainer thing is only used to reset the whole viewport of the Graphics object (which is used for drawing multiple boxes) to its original setting, so when you start drawing another big box, you will not have to take into account also previous rotations (for previously drawn boxes). You can remove both calls to those methods and instead restore the viewport to its original state by doing the reverse transformations at the end:

Code: Select all

oGraphics:TranslateTransform(SELF:x,SELF:y)
oGraphics:RotateTransform(-SELF:rotation)
oGraphics:TranslateTransform(-SELF:x,-SELF:y)
Using the same approach, you can also do a similar transformation to rotate back the viewport before drawing the numbers, so they are drawn always "level". I'm sure there's a better (with less code) way to do this, but this also works :

Code: Select all

USING System.Windows.Forms
USING System.Drawing
USING System.Collections.Generic

[STAThreadAttribute];
FUNCTION Start( ) AS VOID
	DrawForm{}:ShowDialog()


CLASS DrawForm INHERIT System.Windows.Forms.Form
	PROTECT oPictureBox1 AS System.Windows.Forms.PictureBox
	PROTECT aBoxes AS List<BigBox>

	CONSTRUCTOR()
	
		SUPER()

		SELF:oPictureBox1 := System.Windows.Forms.PictureBox{}

		SELF:ClientSize := System.Drawing.Size{704 , 448}

		SELF:oPictureBox1:Location := System.Drawing.Point{0 , 0}
		SELF:oPictureBox1:Size := System.Drawing.Size{679 , 422}
		SELF:oPictureBox1:Paint += SELF:PictureBox1_Paint
		SELF:Controls:Add(SELF:oPictureBox1)
		
		SELF:aBoxes := List<BigBox>{}
		SELF:aBoxes:Add(BigBox{3,80,80,0})
		SELF:aBoxes:Add(BigBox{5,200,200,0})
		SELF:aBoxes:Add(BigBox{5,300,100,45})
		SELF:aBoxes:Add(BigBox{10,400,200,-30})
	RETURN

	METHOD PictureBox1_Paint(sender AS System.Object , e AS System.Windows.Forms.PaintEventArgs) AS VOID
		FOREACH oBox AS BigBox IN SELF:aBoxes
			oBox:DoPaint(e:Graphics)
		NEXT
END CLASS

CLASS BigBox
	PROTECT x,y,boxes AS INT
	PROTECT rotation AS REAL4

	STATIC PROTECT size := 40 AS INT
	STATIC PROTECT oPen := Pen{Color.Black} AS Pen
	STATIC PROTECT oFont := Font{"Arial", size/2} AS Font
	STATIC PROTECT oBrush := SolidBrush{Color.Black} AS SolidBrush
	CONSTRUCTOR(nBoxes AS INT, x AS INT, y AS INT, nRotation AS REAL4)
		SELF:boxes := nBoxes
		SELF:x := x
		SELF:y := y
		SELF:rotation := nRotation
	RETURN
	METHOD DoPaint(oGraphics AS Graphics) AS VOID
		LOCAL oContainer AS System.Drawing.Drawing2D.GraphicsContainer
		LOCAL oRect AS Rectangle
		LOCAL oFormat AS StringFormat

		oFormat := StringFormat{}
		oFormat:Alignment := StringAlignment.Center
		oFormat:LineAlignment := StringAlignment.Center
		

		oRect := Rectangle{SELF:x - (SELF:boxes * size / 2) , SELF:y - size / 2, SELF:boxes * size, size}

		oGraphics:TranslateTransform(SELF:x,SELF:y)
		oGraphics:RotateTransform(SELF:rotation)
		oGraphics:TranslateTransform(-SELF:x,-SELF:y)

		oGraphics:DrawRectangle(oPen, oRect)
		FOR LOCAL n := 1 AS INT UPTO SELF:boxes
			oGraphics:DrawLine(oPen, oRect:Left + n * size, oRect:Top, oRect:Left + n * size, oRect:Bottom)

			LOCAL nTextX,nTextY AS INT
			nTextX := oRect:Left + (n-1) * size + size / 2
			nTextY := oRect:Top + size / 2

			// reverse rotate for the numbers
			oGraphics:TranslateTransform(nTextX,nTextY)
			oGraphics:RotateTransform(-SELF:rotation)
			oGraphics:TranslateTransform(-nTextX,-nTextY)

			oGraphics:DrawString(n:ToString(), oFont , oBrush, nTextX , nTextY, oFormat)

			// restore previous rotation
			oGraphics:TranslateTransform(nTextX,nTextY)
			oGraphics:RotateTransform(SELF:rotation)
			oGraphics:TranslateTransform(-nTextX,-nTextY)

		NEXT

		// restore original rotation (none)
		oGraphics:TranslateTransform(SELF:x,SELF:y)
		oGraphics:RotateTransform(-SELF:rotation)
		oGraphics:TranslateTransform(-SELF:x,-SELF:y)

	RETURN
	
END CLASS

Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Post Reply