I am trying to add a picturebox control to a panel which is created at runtime.
It is for a chess game I am working on. I want to add a picture box to each panel , an assigning the image to the control later. Here is what I have so far:
//Sets the number of rows on the chess board
for (int i = 0; i < 8; i++)
{
//Set the number of columns on the board
for (int j = 0; j < 8; j++)
{
ChessSquare sq = new ChessSquare(((char)(65 + i)).ToString(), 7- j);
sq.Color = (i + (j % 2)) % 2 == 0 ? Color.Black : Color.White;
Panel p = new Panel()
{
Size = new Size(blockSize, blockSize),
BackColor = sq.Color,
Tag = sq,
Location = new Point(blockSize * i + 15, blockSize * j + 15),
BackgroundImageLayout = ImageLayout.Stretch
};
p.MouseEnter += new EventHandler(squareMouseEnter);
p.MouseLeave += new EventHandler(squareMouseLeave);
p.Click += new EventHandler(squareMouseClick);
chessBoardPanels[i, j] = p;
groupBox1.Controls.Add(p);
}
}
//SetUp Board
SetUpBoad Setup = new SetUpBoad();
SetUpBoad(chessBoardPanels);
Since you already put the Panels into the panel-array (?)
chessBoardPanels[i, j] = p;
You can add the PictureBoxes either now or later..:
PictureBox pb = new PictureBox ();
pb.Size = ..
pb.BackColor = Color.Transparent;
chessBoardPanels[i,j].Controls.Add(pb);
To access them later you can cast their first Control to PictureBox:
PictureBox pb = (PictureBox)chessBoardPanels[i,j].Controls[0];
pb.Image = aQueenImage;
If you want to add a PictureBox only where a piece is you need to do checks:
if (chessBoardPanels[i,j].Controls.Count > 0)
{
PictureBox pb = (PictureBox)chessBoardPanels[i,j].Controls[0];
pb.Image = aQueenImage;
}
To move a piece from <i1,j1> to <i2, j2> you do as expected:
chessBoardPanels[i1,j1].Controls[0].Parent = chessBoardPanels[i2,j2];
I notice that you are hooking up mouse events. If you want to use them to move the pieces, remember that transparency will not work for overlapping controls in Winforms and so while a piece-Box is crossing Panels it will not have working tranparency around the Image.
While the pBox is nested in a Panel all is well but to move it you would have to first make it a child of the parent of those panels and only add it to the target Panel upon MouseUp; the coordinate corrections can be solved but the tranparency, if you need it, will be a bigger problem..
The usual advice it to consider drawing at least those board squares and maybe even the pieces onto a base board-Panel (or board-PictureBox)
Related
I would like to display 13 pictureBox, however, it ends up with only the last one visible.
So I was wondering if I did it in a wrong way.
The following code get image from resources folder.
var testP = new PictureBox();
for (int i = 0; i < 13; i++)
{
testP.Width = 65;
testP.Height = 80;
testP.BorderStyle = BorderStyle.None;
testP.SizeMode = PictureBoxSizeMode.StretchImage;
test[i] = getImage(testP, testPTemp[i]);
}
The following code is trying to display 13 pictureBox with shifting location.
These two codes segments should be able to perform the action.
test = new PictureBox[13];
for (var i = 0; i < 13; i++)
{
test[i].Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + testTemp[i]);
test[i].Left = 330;
test[i].Top = 500;
test[i].Location = new Point(test[i].Location.X + 0 * displayShift, test[i].Location.Y);
this.Controls.Add(test[i]);
}
Here is the getImage()
private PictureBox getImage(PictureBox pB, string i) // Get image based on the for loop number (i)
{
pB.Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + i); // Get the embedded image
pB.SizeMode = PictureBoxSizeMode.StretchImage;
return pB;
}
I'm pretty sure there are all PictureBox Controls but they have all the same location so they are lying above each other. That's why only the last one is visible to you.
I think you should replace the 0 with the i variable.
test[i].Location = new Point(test[i].Location.X + i * displayShift, test[i].Location.Y); this.Controls.Add(test[i]);
It's hard to tell the exact problem based off the code you've provided. One possible issue could be that when you are creating the PictureBoxes you only create a single instance before the for loop and then fill the array with references to that instance. Another possibility is that when you're calculating the X position of the controls, you're multiplying by 0 which will always result in 0 (meaning all the controls are at location 330).
Below is code that will achieve basically what you're trying but without all your code I can't give you a more specific example.
In Your Class
const int PICTURE_WIDTH = 65;
const int PICTURE_HEIGHT = 85;
Inside You Function
//Loop through each image
for(int i = 0; i < testTemp[i].length; i++)
{
//Create a picture box
PictureBox pictureBox = new PictureBox();
pictureBox.BorderStyle = BorderStyle.None;
pictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
//Load the image date
pictureBox.Image = (Image)Properties.Resources.ResourceManager.GetObject("_" + testTemp[i]);
//Set it's size
pictureBox.Size = new Size(PICTURE_WIDTH, PICTURE_HEIGHT);
//Position the picture at (330,500) with a left offset of how many images we've gone through so far
pictureBox.Location = new Point(330 + (i * PICTURE_WIDTH), 500);
//Add the picture box to the list of controls
this.Controls.Add(pictureBox);
}
If you need to keep a list of the picture boxes, just create a new list before the loop and add each pictureBox to the list inside the loop. If the control/window you're adding these PictureBoxes to needs to scroll left or right to see all the images set the AutoScroll property to true.
I'm supposed to create a magic square in 2D using Windows Forms Application. It should look like this:
However, the user should be able to decide the size of the square (3x3, 5x5, 7x7, etc). I already wrote the code in a Console Application, but I don't know how to add the 2D graphics.
Somebody already asked this question (How do I put my result into a GUI?), and one of the answers was to use DataGridView, but I'm not sure if that's what I'm looking for, since I can't make it look like the picture.
Any ideas or advice?
You can use a TableLayoutPanel and add buttons to panel dynamically.
If you don't need interaction with buttons, you can add Label instead.
Create square dynamically:
public void CreateSquare(int size)
{
//Remove previously created controls and free resources
foreach (Control item in this.Controls)
{
this.Controls.Remove(item);
item.Dispose();
}
//Create TableLayoutPanel
var panel = new TableLayoutPanel();
panel.RowCount = size;
panel.ColumnCount = size;
panel.BackColor = Color.Black;
//Set the equal size for columns and rows
for (int i = 0; i < size; i++)
{
var percent = 100f / (float)size;
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, percent));
}
//Add buttons, if you have your desired output in an array
//you can set the text of buttons from your array
for (var i = 0; i < size; i++)
{
for (var j = 0; j < size; j++)
{
var button = new Button();
button.BackColor = Color.Lime;
button.Font = new Font(button.Font.FontFamily, 20, FontStyle.Bold);
button.FlatStyle = FlatStyle.Flat;
//you can set the text of buttons from your array
//For example button.Text = array[i,j].ToString();
button.Text = string.Format("{0}", (i) * size + j + 1);
button.Name = string.Format("Button{0}", button.Text);
button.Dock = DockStyle.Fill;
//If you need interaction with buttons
button.Click += b_Click;
panel.Controls.Add(button, j, i);
}
}
panel.Dock = DockStyle.Fill;
this.Controls.Add(panel);
}
If you need interaction with buttons
void button_Click(object sender, EventArgs e)
{
var button = (Button)sender;
//Instead put your logic here
MessageBox.Show(string.Format("You clicked {0}", button.Text));
}
As an example, you can call
CreateSquare(3);
Screenshot:
You can create a Form and add a TableLayoutPanel with this property
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.BackColor = Color.Gold;
and this is the result
When you create Row and Column, to fit correctly set the percentage in this way:
After this you can add a Button or Label in each square.
I have a few buttons to add on the form. In the code I'm setting up some button properties:
class DigitButton : Button
{
private static int digitBtnTag;
public DigitButton()
: base()
{
this.Size = new Size(30, 30);
this.Tag = digitBtnTag;
this.Text = (this.Tag).ToString();
this.Margin = new Padding(2);
this.Padding = new Padding(2);
digitBtnTag++;
}
}
In the MainForm.cs I have
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
digitPanel.Controls.Add(dgtBtns[i]);
}
So when I launch a program I see all my buttons in the one place: (0;0) on digitPanel despite property Margin. So why don't all these buttons automaticly "push" each other in the different directions? And how to make it?
Have you tried using a FlowLayout Panel ?
Also, this video might help:
Windows Forms Controls Lesson 5: How to use the FlowLayout Panel
that's not the way controls works in c#. i'm guessing you programed at java a bit because the layout in jave works that whay, but in c# just do
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Location = new Point(50, 50 * i); // Multiplying by i makes the location shift in every loop
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
digitPanel.Controls.Add(dgtBtns[i]);
}
you'll have to figure out the location parameters by trying and see
You need to define Left and Top then add the button height or width each time you loop to position your buttons correctly i.e.
int bTop=0;
int bLeft=0;
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
dgtBtns[i].Top = bTop;
bTop += dgtBtns[i].Height;
digitPanel.Controls.Add(dgtBtns[i]);
}
Im having a problem with setting custom border of Control. This border can be done with DrawRectangle, DrawBorder or anything else as long as I get this behaviour
Obviously, the darkest border is where previouse border was. Other borders are around it trying to mimic fade out(or whatever). Now, the most challenging thing is I cannot override OnPaint or extend any other Control. This HAS to work on all Controls!
This is part of my extender provider whith which I set these borders when control has focus(like Google Chrome).
So far I have come up with this...
When adding controls in extender provider dictionary I hook up on Enter and Leave events of control. In there I get the parent of the control that is firing the event and on that form I draw these 3 rectangles. That way I sorted painting on non client area. The thing that remains is painting the actual border of control. I have tried and tried but to no avail.
I also hooked up on paint event of that control but ControlPaint.DrawBorder() is not working.
Okay, so this is method that is getting called on Enter and leave.
private void BojajGlow(Graphics gfx, Graphics gfxCtrl, Control parent, Control kontrola, bool novi)
{
Rectangle[] rect = new Rectangle[3];
for (int i = 0; i < 3; i++)
{
int x = kontrola.Location.X - (i + 1);
int y = kontrola.Location.Y - (i + 1);
int w = kontrola.Size.Width + 2 * (i + 1) - 1;
int h = kontrola.Size.Height + 2 * (i + 1) - 1;
rect[i] = new Rectangle(x, y, w, h);
}
if (novi)
{
Color boja = DohvatiOpcije(kontrola).Boja;
for (int i = 0; i < 3; i++)
{
if (i > 0)
boja = Posvjetli(95, ControlPaint.Light(boja));
Pen olovka = new Pen(boja);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
else
{
for (int i = 0; i < 3; i++)
{
Pen olovka = new Pen(parent.BackColor);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
}
From Enter event it is going to be called like this
if (((Control)sender).Parent != null)
BojajGlow(Graphics.FromHwnd(((Control)sender).Parent.Handle), Graphics.FromHwnd(((Control)sender).Handle), ((Control)sender).Parent, (Control)sender, true);
Does anyone have any valuable input on this?
In winforms you are probably going to need to create your own custom control inheriting from the TextBox control. In your control you can implement the OnPaint based on the controls state such as whether or not it has focus.
As for drawing outside of the control, dont. It will only frustrate you. Instead draw three boarders using the forms background color WITHIN your control and change them to the hightlighted color when you need them to glow.
Hope this helps.
I am creating Conway's Game of life in C# and everything works fine, except the refreshing of the panel to display the next generation. I can draw to the panel to establish grid lines for the cells, place and destroy "life" using simWindow_Paint and simWindow_MouseClick but at present i cannot update it.
private void updateGame(){
int living = 0;
int dead = 0;
for(int i = 1; i < gameHeight; i++)
for (int j = 1; j < gameWidth; j++){
//set cell location and size
Rectangle cell = new Rectangle();
cell.Height = cell.Width = 9;
int X = j - 1;
int Y = i - 1;
cell.X = Convert.ToInt32(X * 10 + 1);
cell.Y = Convert.ToInt32(Y * 10 + 1);
using (Graphics g = this.simWindow.CreateGraphics()){
if (gameArray[i, j] == true){
Brush brush = new SolidBrush(Color.Red);
g.FillRectangle(brush, cell);
++living;
} else {
Brush brush = new SolidBrush(Color.White);
g.FillRectangle(brush, cell);
++dead;
}
}
}
}
I am not sure if what i am attempting to do is possible in C#, I have already done this in Pascal, but i am not sure if C# works the same way...
Difficult to say with what you've shown us, but I suspect the problem might be in your simWindow_Paint method. This is (presumably) responding to the paint event for your panel and I suspect what you are doing is overwriting (or over painting) all the drawing you are doing in your updateGame() method.
All the drawing should be done in your paint handler. So you paint handler should know the state of the game and draw it accordingly. Your updateGame() method should just update the state of the game and then invalidate the panel (forcing a repaint).
I recommend you review the following documentation on custom painting in WinForms: http://msdn.microsoft.com/en-us/library/kxys6ytf.aspx