Select drawn figure within panel box - c#

I am working on an 'use-case-diagram-form' where an user can select an element and a modus
Just a simple form. It al works fine, made a class for each actor element and each use-case element. Both are added in a list after beeing created.
But somehow I just can't figure out how to select a created element and after do something with it.
classes i made:
class Actor
{
private static int _id;
private Panel _panel;
public string Name { get; private set; }
public int X { get; private set; }
public int Y { get; private set; }
public Actor(Panel panel, string name, int x, int y)
{
_id++;
_panel = panel;
Name = name;
X = x;
Y = y;
}
public void DrawActor()
{
// draw Actor
var graphics = _panel.CreateGraphics();
var pen = new Pen(Color.Black, 2);
graphics.DrawEllipse(pen, X - 10, Y - 30, 20, 20);
graphics.DrawLine(pen, X, Y - 10, X, Y + 20);
graphics.DrawLine(pen, X - 15, Y, X + 15, Y);
graphics.DrawLine(pen, X, Y + 20, X - 15, Y + 35);
graphics.DrawLine(pen, X, Y + 20, X + 15, Y + 35);
// rectangle around actor
graphics.DrawRectangle(pen, (X - 20), (Y - 30), 40, 80);
// setup font
var stringFont = new Font("Arial", 10);
// measure string
var textWith = graphics.MeasureString(Name, stringFont).Width;
// label
var label = new Label();
var actorText = (_id < 10 ? "0" : "") + _id.ToString() + "-" + Name;
label.Text = actorText;
label.Location = new Point(X - (Convert.ToInt32(textWith)/2), Y + 40);
label.AutoSize = true;
label.BorderStyle = BorderStyle.FixedSingle;
_panel.Controls.Add(label);
}
class UseCase
{
private static int _id;
private Panel _panel;
private string _name;
private int _x;
private int _y;
public UseCase(Panel panel, string name, int x, int y)
{
_id++;
_panel = panel;
_name = name;
_x = x;
_y = y;
}
public void DrawUseCase()
{
var graphics = _panel.CreateGraphics();
var pen = new Pen(Color.Black, 2);
graphics.DrawEllipse(pen, _x , _y , 120, 50);
// setup font
var stringFont = new Font("Arial", 10);
// measure string
var textWith = graphics.MeasureString(_name, stringFont).Width;
// label
var label = new Label();
var useCaseText = (_id < 10 ? "0" : "") + _id.ToString() + "-" + _name;
label.Text = useCaseText;
label.Location = new Point(_x - (Convert.ToInt32(textWith) / 2) + 60, _y + 20);
label.AutoSize = true;
label.BorderStyle = BorderStyle.FixedSingle;
_panel.Controls.Add(label);
}
}
Github repository:
https://github.com/JimVercoelen/use-case-helper
Thanks

Your code has several issues, all of which will go away once you learn how to draw properly in winforms!
There are many posts describing it but what you need to understand that you really have these two options:
Draw onto the surface of the control. This is what you do, but you do it all wrong.
Draw into a Bitmap which is displayed in the control, like the Picturbox's Image or a Panel's BackgroundImage.
Option two is best for graphics that slowly add up and won't need to be corrected all the time.
Option one is best for interactive graphics, where the user will move things around a lot or change or delete them.
You can also mix the options by caching drawn graphics in a Bitmap when they get too numerous.
Since you started with drawing onto the surface let's see how you should do it correctly:
The Golden Rule: All drawing needs to be done in the control's Paint event or be triggered from there always using only the Paint event's e.Graphics object!
Instead you have created a Graphics object by using control.CreateGraphics. This is almost always wrong.
One consequence of the above rule is that the Paint event needs to be able to draw all objects the user has created so far. So you will need to have class level lists to hold the necessary data: List<ActorClass> and List<UseCaseClass>. Then it can do maybe a
foreach(ActorClass actor in ActorList) actor.drawActor(e.Graphics)
etc.
Yes this fully repainting everything looks like a waste but it won't be a problem until you need to draw several hundreds of object.
But if you don't do it this way, nothing you draw persists.
Test it by running your present code and doing a Minimize/Maximize sequence. Poof, all drawings are gone..
Now back to your original question: How to select an e.g. Actor?
This really gets simple as you can can iterate over the ActorList in the MouseClick event (do not use the Click event, as it lacks the necessary parameters):
foreach (ActorClass actor in ActorList)
if (actor.rectangle.Contains e.Location)
{
// do stuff
break;
}
This is a simple implementation; you may want to refine it for the case of overlapping objects..
Now you could do things like maybe change the color of the rectangle or add a reference to the object in a currentActor variable.
Whenever you have made any changes to your lists of things to draw, like adding or deleting a object or moving it or changing any (visible) properties you should trigger an update via the Paint event by calling Invalidate.
Btw: You asked about a PictureBox in the title but use only a Panel in the code. Using a PictureBoxis recommended as it is doublebuffered and also combines two Bitmaps to let you use both a caching Image and a BackgroundImage with maybe a nice paper..
As far as I can see your code so far lacks the necessary classes. When you write them add a Draw routine and either a reference to the Label you add or simply use DrawString to draw the text yourself..
Update:
After looking at your project, here a the minimal changes to make the drawing work:
// private Graphics graphics; // delete!!
Never try to cache a Graphics object!
private void pl_Diagram_Paint(object sender, PaintEventArgs e)
{
pen = new Pen(Color.Black, 1);
DrawElements(e.Graphics); // pass out the 'good' object
//graphics = pl_Diagram.CreateGraphics(); // delete!
}
The same; pass the real Graphics object into the drawing routine instead!
// actor
if (rb_Actor.Checked)
{
if (e.X <= 150)
{
var actor = new Actor(name, e.X, e.Y);
_actors.Add(actor);
pl_Diagram.Invalidate(); // trigger the paint event
//DrawElements();
}
}
// use case
if (rb_Use_Cases.Checked)
{
var useCase = new UseCase(name, e.X, e.Y);
_useCases.Add(useCase);
pl_Diagram.Invalidate(); // trigger the paint event
//DrawElements();
}
Instead of calling the routine directly we trigger the Paint event, which then can pass a good Graphics object to it.
public void DrawElements(Graphics graphics)
{
foreach (var actor in _actors)
{
DrawActor(graphics, actor);
}
foreach (var useCase in _useCases)
{
DrawUseCase(graphics, useCase);
}
}
We receive the Graphics object and pass it on..
private void DrawActor(Graphics graphics, Actor actor)
and
graphics.DrawEllipse(pen, (useCase.X - 60), (useCase.Y - 30), 120, 60);
After these few changes the drawing persists.
Replacing the Panel by a Picturebox is still recommended to avoid flicker during the redraw. (Or replace by a double-buffered Panel subclass..)

Related

Dynamically created PictureBox rendering problem

The end goal is a somewhat playable memory game. Currently, I'm stuck on a rendering problem. I have the following classes:
Field, which is an abstract UserControl:
public abstract class Field : UserControl
{
protected PictureBox _pictureBox;
public Field()
{
_pictureBox = new PictureBox();
_pictureBox.Image = Properties.Resources.empty;
_pictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
_pictureBox.BorderStyle = BorderStyle.FixedSingle;
this.Controls.Add(_pictureBox);
}
// ...
// some abstract methods, not currently important
}
MemoryField, which derives from Field:
public class MemoryField : Field
{
public MemoryField(Form parent, int xPos, int yPos, int xSize, int ySize)
{
_pictureBox.ClientSize = new Size(xSize, ySize);
_pictureBox.Location = new Point(xPos, yPos);
_pictureBox.Parent = parent;
}
// ...
}
And finally, MainForm which is an entry point for my application:
public partial class MainForm : Form
{
private readonly int fieldWidth = 100; // 150 no rendering problems at all
private readonly int fieldHeight = 100;
public MainForm() { InitializeComponent(); }
private void MainForm_Load(object sender, EventArgs e)
{
for (int y = 0; y < 6; y++) // 6 rows
{
for (int x = 0; x < 10; x++) // 10 columns
{
Field field = new MemoryField(this,
x * (fieldWidth + 3), // xPos, 3 is for a small space between fields
labelTimer.Location.Y + labelTimer.Height + y * (fieldHeight + 3), // yPos
fieldWidth,
fieldHeight);
this.Controls.Add(field);
}
}
}
}
Here's where my problem lies:
In those for loops I'm trying to generate a 6x10 grid of Fields (with each containing a PictureBox 100x100 px in size). I do that almost successfully, as my second field is not rendered correctly:
Only thing I found that works (fixes the problem completely) is making field bigger (i.e. 150px). On the other hand, making it smaller (i.e. 50px) makes the problem even bigger:
Maybe useful information and things I've tried:
My MainForm is AutoSize = true; with AutoSizeMode = GrowAndShrink;
My MainForm (initially) doesn't contain any components except menuStrip and label
I tried changing PictureBox.Image property, that didn't work.
I tried creating the grid with just PictureBox controls (not using Field as a PictureBox wrapper), that did work.
I tried placing labelTimer in that "problematic area" which does fix the problem depending on where exactly I put it. (because field positioning depends on labelTimer 's position and height)
I tried relaunching visual studio 2017, didn't work.
Of course, I could just change the size to 150px and move on, but I'm really curious to see what's the root of this problem. Thanks!
The easiest thing to do to fix the problem is something you've already tried - using directly a PictureBox instead of a Field. Now, considering that you only use Field to wrap a PictureBox, you could inherit from PictureBox instead of just wrapping it.
Changing your classes to these will fix the issue as you've noticed:
public abstract class Field : PictureBox {
public Field() {
Image = Image.FromFile(#"Bomb01.jpg");
SizeMode = PictureBoxSizeMode.StretchImage;
BorderStyle = BorderStyle.FixedSingle;
Size = new Size(100, 100);
}
// ...
// some abstract methods, not currently important
}
public class MemoryField : Field {
public MemoryField(Form parent, int xPos, int yPos, int xSize, int ySize) {
ClientSize = new Size(xSize, ySize);
Location = new Point(xPos, yPos);
}
// ...
}
The real reason it was not working has to do with both sizing and positioning of each Field and their subcomponents. You should not set the Location of each _pictureBox relatively to its parent MemoryField, but rather change the Location of the MemoryField relatively to its parent Form.
You should also set the size of your MemoryField to the size of its child _pictureBox otherwise it won't size correctly to fit its content.
public class MemoryField : Field {
public MemoryField(Form parent, int xSize, int ySize) {
_pictureBox.ClientSize = new Size(xSize, ySize);
// I removed the setting of Location for the _pictureBox.
this.Size = _pictureBox.ClientSize; // size container to its wrapped PictureBox
this.Parent = parent; // not needed
}
// ...
}
and change your creation inner loop to
for (int x = 0; x < 10; x++) // 10 columns
{
Field field = new MemoryField(this,
fieldWidth,
fieldHeight);
field.Location = new Point(x * (fieldWidth + 3), 0 + 0 + y * (fieldHeight + 3)); // Set the Location here instead!
this.Controls.Add(field);
}

System.ComponentModel.Win32Exception: 'Error creating window handle.'

My problem is:
System.ComponentModel.Win32Exception: 'Error creating window handle'.
I know I can solve this problem with Dispose(), but when I use it in the program, I'm displaying another error:
System.ObjectDisposedException: 'Can not access a disposed object.
Object name: 'PictureBox'. '
I use the following code:
private void SetUpPuzzle_Click(int parts)
{
Panel P = new Panel
{
Size = new Size(200, 200),
Location = new Point(394, 62),
};
Controls.Add(P);
Control board = P;
int total = parts * parts;
var PB = new PictureBox[total];
var imgarray = new Image[total];
var img = User_Image.Image;
int W = img.Width / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int H = img.Height / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int size = 200 / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
for (int x = 0; x < parts; x++)
{
for (int y = 0; y < parts; y++)
{
var index = x * parts + y;
imgarray[index] = new Bitmap(W, H);
using (Graphics graphics = Graphics.FromImage(imgarray[index]))
graphics.DrawImage(img, new Rectangle(0, 0, W, H),
new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);
PB[index] = new PictureBox
{
Name = "P" + index,
Size = new Size(size, size),
Location = new Point(x * size, y * size),
Image = imgarray[index],
SizeMode = PictureBoxSizeMode.StretchImage
};
PB[index].MouseEnter += Images_M_E;
PB[index].MouseLeave += Images_M_L;
PB[index].MouseClick += Form_MouseClick;
*PB[index].Dispose();
*board.Controls.Add(PB[index]);
}
}
}
When I want to create 10,000 objects
This error is displayed.
My problem is:
System.ComponentModel.Win32Exception: 'Error creating window handle'.
Indeed. You are creating way too many controls for a Winforms application.
And disposing of them doesn't really help because you can't use a disposed object any longer..
To have this kind of large puzzle (10k pieces) you need to change from using PictureBoxes (or any other Controls) to display the puzzle pieces to a different approach. This has been suggested in the original question but then you only wanted to have 100 pieces, remember?
The most common approach is this: Keep a list of images (when they are <= 256x256 pixels do put them into an ImageList!) and draw them in the board's Paint event. This will get rid of all the overhead involved with PictureBoxes.
(Aside: One may think this will not be performant with all the DrawImage calls. But all those PictureBoxes also need to draw all the pixels on all their surfaces, so that is no issue. But they also have to carry the overhead of being (under the hood) fully functional windows (see the error message!), which is why the system can only have a limited number of them; always try to keep the number of controls < 1k!)
You will have to move the placement logic to the board's Paint event and will also have to change the event model..:
Instead of having each PictureBox respond to its own events you will have to find a way to do all the work in the board's events. This will have to be diffenrent, depending on the event.
Since we don't know which event you have and what they do and which data they need for their work, it is hard to give all the necessary details, so I'll just point out a few things..:
There will not be a Enter or Leave event you can use. Instead you need to detect entering an area of a piece by testing for it in the MouseMove event. If you keep a List<Rectangle> you can use Rectangle.Contains(e.Location) for this test.
You can detect a MouseClick but then will have to find out which area was clicked. If your Enter and Leave logic from the MouseMove is working you can use its result to know where the Click went.
Similar ideas can be used for all other events; some are simple, some need a little calculation but they will all be fast and pretty easy to implement..
To optimize performance try to make the image n the right size and use Format32bppPArgb as the pixel format, because it is faster to display.
Another option is to pull the pixel data right from the original image in the Paint event with the same calculations you use now to create them. (There is a DrawImage overlay that uses two Rectangles, one to determine the target and one for the source area..) This saves GDI handles, at least if you can't use an ImageList.
Always plan for growth! For a better implementation do create a Piece class. It should hold a Rectangle and an integer index into the ImageList's Images collection. It could also have a method Switch(Piece otherPiece) which would either switch the Rectangles or the indices.
Good luck :-)
I met this exception because endless loop creating new UI control and set its properties. After looped many times, this excption was thrown when change control visible property. I found both User Object and GDI Object (From Task Manager) are quite large.
I guess your issue is similar reason that system resources are exhaust by those UI controls.
I comment PB[index].Dispose(); and it's work.
private void SetUpPuzzle(int parts)
{
// Comment ***********
//Panel P = new Panel
//{
// Size = new Size(200, 200),
// Location = new Point(394, 62),
//};
//Controls.Add(P);
//Control board = P; ***********
int total = parts * parts;
var PB = new PictureBox[total];
var imgarray = new Image[total];
var img = User_Image.Image;
int W =Convert.ToInt32(img.Width / Math.Sqrt(parts));
int H = Convert.ToInt32(img.Height / Math.Sqrt(parts));
int size = Convert.ToInt32(200 / Math.Sqrt(parts));
for (int x = 0; x < parts; x++)
{
for (int y = 0; y < parts; y++)
{
var index = x * parts + y;
imgarray[index] = new Bitmap(W, H);
using (Graphics graphics = Graphics.FromImage(imgarray[index]))
graphics.DrawImage(img, new Rectangle(0, 0, W, H),
new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);
PB[index] = new PictureBox
{
Name = "P" + index,
Size = new Size(size, size),
Location = new Point(x * size, y * size),
Image = imgarray[index],
SizeMode = PictureBoxSizeMode.StretchImage
};
PB[index].MouseEnter += Form1_MouseEnter;
PB[index].MouseLeave += Form1_MouseLeave;
PB[index].MouseClick += Form1_MouseClick;
//Comment
//PB[index].Dispose(); < -----------------
// Add PB in Panel in form
panel1.Controls.Add(PB[index]);
}
}
// after add all refresh panel
panel1.Refresh();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void Form1_MouseEnter(object sender, EventArgs e)
{
throw new NotImplementedException();
}
Then Call the SetUpPuzzle method in your button like :
private void button1_Click(object sender, EventArgs e)
{
SetUpPuzzle(10);
}

Seat reserving software: Drawing lots of Seats in C# instantly

I'm building Seat reserving software using C# and I am confusing how I draw lots of seats instantly.
I'm trying three way which is..
Using Usercontrol
public void DrawUsercontrol(int x, int y)
{
int space = 4;
int SeatLimit = 165;
int RowSeatLimit = 15;
for (var i = 1; i < SeatLimit; i++)
{
UserControl1 ctrl = new UserControl1();
ctrl.Size = new System.Drawing.Size(25, 25);
ctrl.Location = new Point(x + space, y);
if (i % RowSeatLimit == 0)
{
x = 1;
y = y + 25 + space;
}
x = x + 25 + space;
ctrl.label1.Text = i.ToString();
ctrl.label1.Click += new EventHandler(label1_Click);
panel1.Controls.Add(ctrl);
}
}
Using "Panel" control
public void DrawingPanel(int x, int y)
{
Panel myPanel = new Panel();
int width = 16;
int height = 16;
myPanel.Size = new Size(width, height);
myPanel.BackColor = Color.White;
myPanel.Location = new Point(x, y);
Label mylabel = new Label();
mylabel.Text = "4";
myPanel.Controls.Add(mylabel);
myPanel.BackColor = Color.YellowGreen;
// this.Controls.Add(myPanel);
panel1.Controls.Add(myPanel);
}
Using Graphics and draw Rectangle
public void DrawingSquares(int x, int y)
{
SolidBrush myBrush = new SolidBrush(System.Drawing.Color.Red);
Graphics graphicsObj;
graphicsObj = this.panel1.CreateGraphics();
Rectangle myRectangle = new Rectangle(x, y, 30, 30);
graphicsObj.FillRectangle(myBrush, myRectangle);
graphicsObj.Dispose();
}
I refer first option but it's too slow.
And how can I decide?
Your problem is that you are adding only one control at a time. Adding a control forces a full refresh (software GDI+ rendering is quite slow) of the parent panel (best case) and perhaps the whole form (worst case).
Try creating all your controls and adding them in one line using Panel.Controls.AddRange. This will only prompt one refresh.
You should also only add these controls when the form is first shown and when the number of seats change - it is an expensive (and relatively slow) operation.
Consider creating a UserControl for each seat so that you don't have to manage the seat labels and seat borders separately - this way you can just have one list. If you add the seats in order, the index of an item in the list will map to its seat number! You probably wont get a performance increase from this but your code will be easier to work with.

Modify Fill Colour from Outside a Paint Event Handler in C# and Visual Studio 2012

What I Would Like to Do
Create and fill a rectangle, initially with a grey colour, then randomize that rectangle's fill colour with the press of a button.
What I Cannot Seem to Do
Modify properties of the aforementioned rectangle after it is initially drawn.
Random Colour Generator
public static readonly Random random = new Random();
public static readonly object syncLock = new object();
public static int RandomNumber( int min, int max )
{
lock(syncLock) {
return random.Next(min, max);
}
}
public Color randColourPicker() {
Color randColour = Color.FromArgb(RandomNumber(0, 255), RandomNumber(0, 255), RandomNumber(0, 255));
return randColour;
}
Some randColourPicker() Calls
public void randomizeColours() {
this.BackColor = randColourPicker();
clrRandLabel.ForeColor = randColourPicker();
randomizeButton.BackColor = randColourPicker();
randomizeButton.ForeColor = randColourPicker();
loopCheckbox.ForeColor = randColourPicker();
}
Rectangle Painting
private void clrRandForm_Paint( object sender, PaintEventArgs e ) {
int x = 266;
int y = 105;
int width = 274;
int height = 172;
Rectangle clrRect = new Rectangle(x, y, width, height);
SolidBrush greyBrush = new SolidBrush(Color.FromArgb(75, 75, 75));
SolidBrush randBrush = new SolidBrush(randColourPicker());
Graphics clrGraphic = mainBox.CreateGraphics();
clrGraphic.FillEllipse(greyBrush, clrRect);
greyBrush.Dispose();
randBrush.Dispose();
clrGraphic.Dispose();
}
More Info
I have a button, and it's click event is bound to execute the randomizeColours() call, which in turn, swaps around the colours of various elements of the UI. I also have a check box that, when checked, runs through randomizeColours() on loop with a timer. I would like to do something similar, but with the fill colour of clrRect, but I cannot seem to figure out how I would access clrGraphic.FillRectangle(greyBrush, clrRect) outside of the PaintEvent function, so I can modify it to clrGraphic.FillRectangle(randBrush, clrRect). randBrush is a brush I created with a random colour using randColourPicker().
Programs Used
Microsoft Visual Studio Express 2012
P.S.
Sorry if this question is a duplicate, I couldn't quite figure out what to search for... Also, sorry if my code is cringe-worthy to some, I'm not really that good with the OOP approach or C-styled languages. :)
To change the Rectangle color you have to draw it again. You can keep your paint event function but with some changes.
First, declare a global boolean First_time (initially set with true) that will check if you you want the greybrush or the Randbrush.
boolean First_Time;
Then in your Paint event :
private void clrRandForm_Paint( object sender, PaintEventArgs e ) {
int x = 266;
int y = 105;
int width = 274;
int height = 172;
Rectangle clrRect = new Rectangle(x, y, width, height);
SolidBrush greyBrush = new SolidBrush(Color.FromArgb(75, 75, 75));
SolidBrush randBrush = new SolidBrush(randColourPicker());
Graphics clrGraphic = mainBox.CreateGraphics();
if(First_time)
clrGraphic.FillEllipse(greyBrush, clrRect);
else
clrGraphic.FillEllipse(randBrush, clrRect);
First_Time = false;
greyBrush.Dispose();
randBrush.Dispose();
clrGraphic.Dispose();
}
And then in your button event, call:
this.Invalidate(); // or this.Refresh()
So that the Paint event is called again xD.
I hope my advise was usefull.

Can't paint 2 of the same control on winForm

I'm making a simple game in winform (tic-tac-toe), and I'm having some problem to paint block control.
Here is the class I made, that represent a block in the game (without game logic, is only UI).
public class UI_Block : Control
{
private Rectangle block;
private SIGNS sign;
public SIGNS Sign
{
get {return sign;}
set
{
if (sign == SIGNS.EMPTY)
sign = value;
}
}
public UI_Block( ) {
sign = SIGNS.EMPTY;
}
public void SetBlockOnBoard(int x, int y)
{
this.Location = new Point( x , y );
this.Size = new Size(Parent.Width /3, Parent.Height / 3);
block = new Rectangle(this.Location, this.Size);
}
public void DrawSign(Graphics g)
{
Pen myPen = new Pen(Color.Red);
if (sign == SIGNS.O)
{
drawO(g,new Pen(Brushes.Black));
}
if (sign == SIGNS.X)
{
drawX(g, new Pen(Brushes.Red));
}
}
protected override void OnPaint(PaintEventArgs e)
{
DrawSign(e.Graphics);
base.OnPaint(e);
}
//Draw X
private void drawX(Graphics g, Pen myPen)
{
//draw first daignol
Point daignolStart = new Point { X = this.Location.X , Y = this.Location.Y };
Point daignolEnd = new Point { X = this.Size.Width , Y = this.Size.Height };
g.DrawLine(myPen, daignolStart, daignolEnd);
//draw second daignol
daignolStart = new Point { X = Size.Width , Y = this.Location.Y };
daignolEnd = new Point { X = Location.X, Y = Size.Height };
g.DrawLine(myPen, daignolEnd, daignolStart);
}
//Draw O
private void drawO(Graphics g, Pen myPen)
{
g.DrawEllipse(myPen, block);
}
}
I added them both to the winForm class and to see how it looks like when I paint them:
public partial class Form1 : Form
{
UI.UI_Block block;
UI.UI_Block blockX;
public Form1()
{
InitializeComponent();
block = new UI.UI_Block();
blockX = new UI.UI_Block();
Controls.Add(block);
Controls.Add(blockX);
}
protected override void OnLoad(EventArgs e)
{
block. SetBlockOnBoard(0, 0);
blockX.SetBlockOnBoard(0, block.Height);
block.Sign = SIGNS.X;
blockX.Sign = SIGNS.O;
base.OnLoad(e);
}
protected override void OnPaint(PaintEventArgs e)
{
//block.DrawSign(e.Graphics);
//block.DrawSign(e.Graphics);
base.OnPaint(e);
}
}
I tried few things, like not using the onPaint event and I still get the same result.
Here what I see when I run it:
Any idea why I can't paint both of them?
You are not drawing the contents of your control in it's visible area, so it is drawing fine but you can't see it.
Every control has it's own coordinate space (client coords), which starts at 0,0 regardless of where it is positioned within the parent control. You are placing the control in it's parent correctly by setting its Location, but then you are also using the Location to offset the graphics, so they are essentially offset twice.
(If you make your control bigger you'll be able to see the X being drawn further down the screen)
To fix this, do all your drawing in the client coordinate space of your control, i.e. draw in the area (0, 0, width, height)
(P.S. You could just draw all 9 tiles in the parent control, which is a more efficient approach than creating 9 child controls. But what you are doing will work fine)

Categories

Resources