Move Picturebox inside TableLayoutPanel from one cell to the other with delay - c#

I have a TableLayoutPanel inside a Form and that TableLayoutPanel has five Picturebox controls inside some of its cells. What I would like to do is, move those Pictureboxes to different cells based on a list of coordinates passed as an argument. From user perspective, those Pictureboxes will disappear from one cell and reappear in another cell.(Just like a main character in tile base game where the character disappear in one cell and reappear in an adjacent cell). The update method is inside Form class and it is called by a method from another class. The problem is, it does not display each movement. It just shows initial positions of all Pictureboxes followed by some refreshing and then the final position. It should display PictureBoxes in each coordinates before it gets to the final coordinate. I tried Thread.Sleep() but it doesn't work. How do I resolve this issue?
public partial class CheckerBoard : Form
{
....
....
public void update(Position k, List<Position> p)
{
p1_picturebox.Visible = false;
p2_picturebox.Visible = false;
p3_picturebox.Visible = false;
p4_picturebox.Visible = false;
p5_picturebox.Visible = false;
// Load images in new positions
this.board.Controls.Add(k_picturebox, k.col, knight.row);
this.board.Controls.Add(p1_picturebox, p[0].col, p[0].row);
this.board.Controls.Add(p2_picturebox, p[1].col, p[1].row);
this.board.Controls.Add(p3_picturebox, p[2].col, p[2].row);
this.board.Controls.Add(p4_picturebox, p[3].col, p[3].row);
this.board.Controls.Add(p5_picturebox, p[4].col, p[4].row);
------UPDATED---------
this.Invalidate();
Thread.Sleep(3000);
}
}
UPDATE
I fixed my code as per suggestions. However, the problem still persists. Seems that it is redrawing the whole tablelayoutpanel instead of moving picture box from one cell to the other. I can see the refreshing of the tablelayoutpanel.
private void replay(State currentState)
{
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(0.5)
};
timer.Tick += (o, e) =>
{
List<Position> pos = new List<Position>();
foreach(Position p in currentState.pawns)
{
pos.Add(p);
}
this.update(currentState.knight, pos);
currentState = currentState.next;
if (currentState == null)
{
timer.IsEnabled = false;
//this.prepareDisplay();
}
};
timer.IsEnabled = true;
}
public void update(Position knight, List<Position> pawns)
{
// Load images in new positions
this.board.Controls.Remove(knight_picturebox);
this.board.Controls.Add(knight_picturebox, knight.col, knight.row);
for(int i=0; i < pawns.Count; i++)
{
//this.board.Controls.Add(this.picBoxList[i], pawns[i].col, pawns[i].row);
this.picBoxList[i].WaitOnLoad = true;
this.board.Controls.Remove(this.picBoxList[i]);
this.board.Controls.Add(this.picBoxList[i], pawns[i].col, pawns[i].row);
}
}
UPDATE
With suspendLayout and improveLayout, redrawing is improved. But I'm wondering if it is possible not to redraw the tablelayout but just the pictureboxes so that it appears only the pictureboxes are moving.

I hope this works. Assuming that you have made the temporary list of the names of the pictureboxes into List from where you can retrieve the names and find them to move to another position. So, basically in my method you are simply shifting their positions to another cells in your tablelayout panel.
You can optimize it by using "foreach" loop to retrieve pictureboxes instead of iterating the same Controls.Find() function for each control.name of picturebox.
public void update(Position k, List<Position> p)
{
PictureBox p1 = this.Controls.Find("/* the picturebox name*/", true).FirstOrDefault() as PictureBox;
PictureBox p2 = this.Controls.Find("/* the picturebox name*/", true).FirstOrDefault() as PictureBox;
PictureBox p3 = this.Controls.Find("/* the picturebox name*/", true).FirstOrDefault() as PictureBox;
PictureBox p4 = this.Controls.Find("/* the picturebox name*/", true).FirstOrDefault() as PictureBox;
PictureBox p5 = this.Controls.Find("/* the picturebox name*/", true).FirstOrDefault() as PictureBox;
this.board.Controls.Remove(p1);
this.board.Controls.Remove(p2);
this.board.Controls.Remove(p3);
this.board.Controls.Remove(p4);
this.board.Controls.Remove(p5);
//p1_picturebox.Visible = false;
//p2_picturebox.Visible = false;
//p3_picturebox.Visible = false;
//p4_picturebox.Visible = false;
//p5_picturebox.Visible = false;
// Load images in new positions
//this.board.Controls.Add(k_picturebox, k.col, knight.row);
this.board.Controls.Add(p1, p[0].col, p[0].row);
this.board.Controls.Add(p2, p[1].col, p[1].row);
this.board.Controls.Add(p3, p[2].col, p[2].row);
this.board.Controls.Add(p4, p[3].col, p[3].row);
this.board.Controls.Add(p5, p[4].col, p[4].row);
//alternatively for the whole above code you may optimize like below.
int i = 0;
foreach(Control c in PictureBoxList) //PictureBoxList is retrieved as List<Control>
{
PictureBox p1 = this.Controls.Find(c.Name, true).FirstOrDefault() as PictureBox;
p1.Name = c.Name; //assign new name from the same list which contains pictureboxes and can get name by using c.Name
this.board.Controls.Remove(p1);
this.board.Controls.Add(p1, p[i].col, p[i].row);
i++;
}
------UPDATED-------- -
this.Invalidate();
Thread.Sleep(3000);
}
Update:
Have you tried setting the WaitOnLoad property of Picturebox to true?
Update2:
Try invalidating the board and then the new pictureBoxes particularly.

Related

C# code displays picture boxes vertically instead of horizontally

My code in C# displays picture boxes vertically where the second picture box is below the first one instead of displaying the second picture box horizontally next to the first such that when the width of the visible form overflows. the code creates a new row and continues to create picture boxes. The code reads image file paths from a folder and initializes an array of picture boxes based on the count of the images. The goal is to create a grid of picture boxes for displaying the images. How can I update the logic of the app to make it achieve the desired output.
public class ImageGrid:Form {
//declare the folder name that contains the complex images
private string _folder = "./Complex"; //declare the array of picture boxes to display in the form
private PictureBox[] _image_grid;
private List<string> _paths= new List<string>();
public ImageGrid() {
//set a title for the form
Text = "Confidence Level Checker";
//make the screen full
FormBorderStyle =
FormBorderStyle.None;
WindowState =
FormWindowState.Maximized;
//read all the image names in the folder
if (Directory.Exists(_folder)) {
//list all the files in the folder
string[] files =
Directory.GetFiles(_folder);
if (files != null && files.Length > 0) {
//add the paths to the list
foreach (var path in files) {
_paths.Add(path);
}
//create an array of picture boxes based on the file count
_image_grid = new PictureBox[files.Length];
//declare the size of each picture box
int width = 300; int height = 250;
int x = 20; int y = 20;
foreach (string path in _paths) {
PictureBox box = new PictureBox()
{
Size = new Size(width, height),
Location = new Point(x,y),
Image = Image.FromFile(path),
SizeMode = PictureBoxSizeMode.StretchImage
};
//add the picture box to the form
this.Controls.Add(box);
//update the location to draw the picture box
x += 320;
if (x + 300 > ClientSize.Width) { x = 20; y += 270; }
}
}
}
}
You can use a FlowLayoutPanel() instead to make this simpler.
Then ensure (in the form designer) that the property FlowDirection is set to LeftToRight.
You can also do this in code:
myFlowLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
You add controls to a FlowLayoutPanel the same way you do to a standard Panel:
myFlowLayoutPanel.Add(someControl);

Pictureboxes not becoming visible

Sorry, but I have another problem. In my code I can now get it to randomly assign pictures to pictureboxes but unfortunately I cannot get any of the pictureboxes to become visible, upon clicking them, this event should happen:
private void pictureBox1_Click(object sender, EventArgs e)
{
// The timer is only on after two non-matching
// icons have been shown to the player,
// so ignore any clicks if the timer is running
if (timer1.Enabled == true)
{
return;
}
PictureBox clickedpicturebox = sender as PictureBox;
if (clickedpicturebox == null)
{
// If the clicked picture is visible, the player clicked
// an icon that's already been revealed --
// ignore the click
if (clickedpicturebox.Visible == true)
return;
// If firstClicked is null, this is the first icon
// in the pair that the player clicked,
// so set firstClicked to the picturebox that the player
// clicked, make it visible, and return
if (firstClicked.Tag == null)
{
clickedpicturebox = firstClicked;
firstClicked.Tag = clickedpicturebox.Tag;
firstClicked.Visible = true;
}
// If the player gets this far, the timer isn't
// running and firstClicked isn't null,
// so this must be the second icon the player clicked
// Set its property to visible
clickedpicturebox = secondClicked;
secondClicked.Tag = clickedpicturebox.Tag;
secondClicked.Visible = true;
// If the player gets this far, the player
// clicked two different icons, so start the
// timer (which will wait three quarters of
// a second, and then hide the icons)
timer1.Start();
}
}
But for some reason, even if I strip it down to just a line that says:
PictureBox clickedpicturebox = sender as PictureBox;
clickedpicturebox.Visible = true;
It still doesn't work, could it be because I selected multiple pictures to apply the event to at the same time?
Also, incase you need it, I have the properties of the first picturebox here, all other pictureboxes are essentially the same.
//
// pictureBox1
//
this.pictureBox1.BackColor = System.Drawing.Color.Transparent;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Location = new System.Drawing.Point(5, 5);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(125, 119);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.Visible = false;
this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
EDIT: I would like to thank everyone, the problem is now resolved, I have used the labels allow me to easily interact between the foreground and background colours, allowing for easy transition between a label and a picturebox.
H W and ASh are both correct, the problem is resolved.

How to make picturebox transparent?

I am making an application in C# .NET. I have 8 picture boxes in it. I used PNG images with transparent background but in my form it is not transparent when it comes above another image.
I am using Visual Studio 2012. This is a screenshot of my form:
One way to do this is by changing the parent of the overlapping picture box to the PictureBox over which it is lapping. Since the Visual Studio designer doesn't allow you to add a PictureBox to a PictureBox, this will have to be done in your code (Form1.cs) and within the Intializing function:
public Form1()
{
InitializeComponent();
pictureBox7.Controls.Add(pictureBox8);
pictureBox8.Location = new Point(0, 0);
pictureBox8.BackColor = Color.Transparent;
}
Just change the picture box names to what ever you need. This should return:
GameBoard is control of type DataGridView;
The image should be type of PNG with transparent alpha channel background;
Image test = Properties.Resources.checker_black;
PictureBox b = new PictureBox();
b.Parent = GameBoard;
b.Image = test;
b.Width = test.Width*2;
b.Height = test.Height*2;
b.Location = new Point(0, 90);
b.BackColor = Color.Transparent;
b.BringToFront();
Try using an ImageList
ImageList imgList = new ImageList;
imgList.TransparentColor = Color.White;
Load the image like this:
picturebox.Image = imgList.Images[img_index];
I've had a similar problem like this.
You can not make Transparent picturebox easily such as picture that shown at top of this page, because .NET Framework and VS .NET objects are created by INHERITANCE! (Use Parent Property).
I solved this problem by RectangleShape and with the below code I removed background,
if difference between PictureBox and RectangleShape is not important and doesn't matter, you can use RectangleShape easily.
private void CreateBox(int X, int Y, int ObjectType)
{
ShapeContainer canvas = new ShapeContainer();
RectangleShape box = new RectangleShape();
box.Parent = canvas;
box.Size = new System.Drawing.Size(100, 90);
box.Location = new System.Drawing.Point(X, Y);
box.Name = "Box" + ObjectType.ToString();
box.BackColor = Color.Transparent;
box.BorderColor = Color.Transparent;
box.BackgroundImage = img.Images[ObjectType];// Load from imageBox Or any resource
box.BackgroundImageLayout = ImageLayout.Stretch;
box.BorderWidth = 0;
canvas.Controls.Add(box); // For feature use
}
One fast solution is set image property for image1 and set backgroundimage property to imag2, the only inconvenience is that you have the two images inside the picture box, but you can change background properties to tile, streched, etc. Make sure that backcolor be transparent.
Hope this helps
Just use the Form Paint method and draw every Picturebox on it, it allows transparency :
private void frmGame_Paint(object sender, PaintEventArgs e)
{
DoubleBuffered = true;
for (int i = 0; i < Controls.Count; i++)
if (Controls[i].GetType() == typeof(PictureBox))
{
var p = Controls[i] as PictureBox;
p.Visible = false;
e.Graphics.DrawImage(p.Image, p.Left, p.Top, p.Width, p.Height);
}
}
you can set the PictureBox BackColor proprty to Transparent

Datagridview to draw wafer map

Currently I'm using C# datagridview to draw a wafer map which contains >500 rows and >700 columns.
However, there are a few issues:
Slow performance. As i need to adjust the column width, I have to loop through and assign individually.
for (int i = 0; i < this.dgvCompleteMapGrid.Columns.Count; i++)
{
this.dgvCompleteMapGrid.Columns[i].Width = 8;
}
I only need to draw the cell border for cells with value as a wafer map has an almost round shape. I'm using cellpainting event:
if (e.Value != null) {
if (e.Value.ToString() == "")
{
e.AdvancedBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
}
else
{
if (e.Value.ToString() == "1")
{
e.CellStyle.BackColor = Color.Lime;
}
else
{
e.CellStyle.BackColor = Color.Red;
}
//check if the top border set None for neighbor empty value cell
if (e.AdvancedBorderStyle.Top.ToString() == "None")
{
e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.Single;
}
//repeat the same for left right and bottom
}
}
However, it seems like it will draw multiple border duplicate for most of the cells.
Is Datagridview recommended? I tried to draw rectangle on a panel and the performance is even worse.
Here's my Picturebox that allows for pixellated resizing (versus interpolated). E.g. similar to zooming in on a Microsoft Paint picture.
using System.Windows.Forms;
namespace WireWorld
{
public class ZoomablePicturebox : PictureBox
{
public ZoomablePicturebox()
{
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
pe.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
base.OnPaint(pe);
}
}
}
To generate the bitmap, I use something like this:
// Pick width and height for your case
Bitmap MyBitmap = new Bitmap(width, height);
using (var g = Graphics.FromImage(retVal))
g.Clear(BackColor); // Pick a background fill color
// Draw your points, whatever your loop needs to be and your point definition is
foreach (MyPointDef point in _Points)
{
MyBitmap.SetPixel(point.X, point.Y, point.Color);
}
Then I put a picture box in a panel on my form. The panel then provides the scrolling. I can set the picture and zoom as follows:
canvasPB.SizeMode = PictureBoxSizeMode.Zoom; // Ensures picture is resized as we resize picturebox
canvasPB.Image = MyBitmap;
canvasPB.Height = canvasPB.Image.Height * Zoom; // Zoom is 1, 2, 3, etc, for 1X, 2X, ...
canvasPB.Width = canvasPB.Image.Width * Zoom;
canvasPB being the name of the Picturebox on my form I'm trying to use as my canvas.

Anchoring checkbox in Datagridview

I am creating a winform based Desktop application and I am using Datagridview to populate the data.
I am using checkbox in one of the header column. The Table is quiet big to fit into the screen and am using scroll bar to move in the horizontal and vertical direction.
The checkbox need to move as the scroll bar moves. However, the problem is it remains static.
Any idea how to anchor it, so that when the scroll bar moves, the checkbox moves accordingly.
Thanks
EDIT :
Auto generated Designer Code :
this.checkheader.AutoSize = true;
this.checkheader.BackColor = System.Drawing.Color.White;
this.checkheader.FlatAppearance.BorderColor = System.Drawing.Color.White;
this.checkheader.Location = new System.Drawing.Point(49, 96);
this.checkheader.Name = "checkheader";
this.checkheader.Size = new System.Drawing.Size(15, 14);
this.checkheader.TabIndex = 21;
this.checkheader.UseVisualStyleBackColor = false;
this.checkheader.CheckedChanged += new System.EventHandler(this.checkboxHeader_CheckedChanged);
//
You could use the datagridview 'scroll'-event to do something like this:
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
if (e.ScrollOrientation.Equals(ScrollOrientation.HorizontalScroll))
{
checkBox1.Location = new Point(checkBox1.Location.X - (e.NewValue - e.OldValue), checkBox1.Location.Y);
}
if (checkBox1.Location.X < dataGridView1.Location.X + 40)
{
checkBox1.Visible = false;
}
else
{
checkBox1.Visible = true;
}
}
Better late than never ?

Categories