C# code displays picture boxes vertically instead of horizontally - c#

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);

Related

How to load image to PictureBox list

I am loading images like this:
image = Image.FromFile(//here goes path to image);
Than i have list of pictureBoxes like this
List<PictureBox> pictureBoxes = new List<PictureBox>();
Than i load picture boxes in pictureBox list like this
// i is set to one
for (; i < this.images.Count; i++)
{
pictureBoxes.Add((PictureBox)Controls.Find("pictureBox" + i.ToString(), true)[0]);
}
And now I want to load that image in pictureBox[0]. Then I load another image and I want to add it to pictureBox[1] and so on. I am trying to do this more than 3 days. Does anyone know how to do it?
After you filled your picturebox list, just find the first empty one.
var emptyPb = pictureBoxes.Where(x => x.Image == null).FirstOrDefault();
if(emptyPb==null)
{
throw new Exception("No empty picturebox could be found!");
return;
}
emptyPb.Image = images[0];
//Set the number of images you have.
int imageCount = 3;
//Create path.
string path = #"C:\ImageFolder\";
//Create the List of PictureBox
List<PictureBox> pictureBoxes = new List<PictureBox>();
//This for will create the name foreach image.
//Example: "pic1", "pic2", "pic3".
//Assuming that you have the names of each file image like the example.
for (int i = 1; i < imageCount + 1; i++)
{
//Create a PictureBox foreach image
PictureBox pictureBox = new PictureBox()
{
Name = $"pic{i}",
//Assign the image file.
Image = Image.FromFile(path + $"pic{i}.jpg")
};
//Add the new pictureBox to the list pictureBoxes
pictureBoxes.Add(pictureBox);
}

Dynamic dialog boxes

I am working with Winforms and I have a question about making these more dynamic. For instance I can create a winform that has a group of text boxes that displays data however how would I make this dynamic? In terms of the number of text boxes the user can see depends on what data is found?
I know I can do something along the lines of the below:
TextBox tb = new TextBox();
In my scenario I have an application that reads through a bunch of documents and if a $ is found then a prompt appears asking the user to input a proper value, however, if the document has a lot of values that need updating then this is a lot of dialog boxes. So a good way to resolve this is to have the dialog box appear at the end (after the file has been read) with all the values that need updating and the user can update all these at once.
The problem that I see is that the number of values that need to be displayed can be anything from 1 on wards, which means the loop would need to account for this.
My current code is as below;
foreach (FileInfo fi in rgFiles)
{
current++;
fileProcessBar.Value = current / count * 60 + 40;
string[] alllines = File.ReadAllLines(fi.FullName);
for (int i = 0; i < alllines.Length; i++)
{
if (alllines[i].Contains("$"))
{
// prompt
int dollarIndex = alllines[i].IndexOf("--");
Regex regex = new Regex(#"(--.{1,100})");
var chars = regex.Match(alllines[i]).ToString();
string PromptText = chars.Replace("-", "");
string promptValue = CreateInput.ShowDialog(PromptText, fi.FullName);
if (promptValue.Equals(""))
{
}
else
{
alllines[i] = alllines[i].Replace("$", promptValue);
File.WriteAllLines(fi.FullName, alllines.ToArray());
}
}
}
prompt method:
public static string ShowDialog(string text, string caption)
{
Form prompt = new Form()
{
Width = 600,
Height = 150,
FormBorderStyle = FormBorderStyle.FixedDialog,
Text = caption,
StartPosition = FormStartPosition.CenterScreen
};
Label textLabel = new Label() { Left = 50, Top = 15, Width = 500, Text = text };
TextBox textBox = new TextBox() { Left = 50, Top = 52, Width = 500 };
Button confirmation = new Button() { Text = "Add", Left = 450, Width = 100, Top = 72, DialogResult = DialogResult.OK };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textBox);
prompt.Controls.Add(confirmation);
prompt.Controls.Add(textLabel);
prompt.AcceptButton = confirmation;
prompt.MaximizeBox = false;
return prompt.ShowDialog() == DialogResult.OK ? textBox.Text : "";
}
My question is how can a winform be more dynamic as in terms of size and what is displayed? how can I create a new form without specifying size and position? but still not being a jumbled mess?
Make a new Form with a certain size. Then add a FlowLayoutPanel to the form which has almost the same width as the form and almost the same height. Leave enough space for the button that you need:
In the panel properties set the fields AutoSize to true and AutoSizeMode to GrowAndShrink:
Don't forget to specify the FlowDirection:
this.panel.FlowDirection = FlowDirection.TopDown;
Now you need just a method which adds your prompting controls to the controls of the FlowLayoutPanel (which will order them in an automatic fashion) :
public void AddToCanvas(string text)
{
this.flowLayoutPanel1.Controls.Add(new Label() {Text = text});
this.flowLayoutPanel1.Controls.Add(new TextBox());
Resize();
}
And a resize method to adjust the form to the amount of current controls inside it:
public void Resize()
{
Size s = new Size();
s.Height = this.flowLayoutPanel1.Height + this.button_Accept.Height +
(this.flowLayoutPanel1.Controls.Count * 10) + y_offset;
s.Width = this.flowLayoutPanel1.Width + 10;
this.MaximumSize = s;
this.Size = s;
}
With this input:
random text
$ Name
$ Address
random text
$ Age
random text
$ Planet
$ Continent
random text
$ StarSystem
I get the following Result:
EDIT:
Create a variable after you have read the contents of one file (before you loop through the lines):
DynamicPrompt dp = new DynamicPrompt("YourCaption");
for (int i = 0; i < alllines.Length; i++)
{
if (alllines[i].Contains("$"))
{
Start the loop and if you get to the important line call
dp.AddToCanvas(PromptText);

Accessing certain instance of a Winform control

In this program im writing I use a function to create multiple instances of a PictureBox. This is the code:
public void serverCard()
{
//Definitions
PictureBox cardBack = new PictureBox();
//Specifics for card
cardBack.Size = new Size(cardSizeX, cardSizeY);
cardBack.BackColor = Color.White;
cardBack.Left = startX;
cardBack.Top = startY;
serverArea.Controls.Add(cardBack);
//differences in pos
startX += cardBack.Width + 5;
if (startX > this.Width - cardSizeX)
{
startY += cardBack.Height + 5;
startX = 5;
}
}
How would I access a specific instance of the PictureBox.
For Example: I create 5 PictureBoxes called "cardBack" using this function. I want to change the position of the second Picture Box that was created, how would I go about this.
1) You could either give each PictureBox a different name (may be "cardBack" + ID_in_int)
int picBox_ID = 1;
public void serverCard()
{
PictureBox cardBack = new PictureBox();
cardBack.Name = "cardBack" + picBox_ID;
picBox_ID++;
and pull them out of the Controls by name:
PictureBox temp = serverArea.Controls.OfType<PictureBox>().FirstOrDefault(x=>x.Name == "cardBack2");
2) or you could have a separate collection of type: List<PictureBox> where you would store them additionally
List<PictureBox> picCollection = new List<PictureBox>();
public void serverCard()
{
PictureBox cardBack = new PictureBox();
picCollection.Add(cardBack);
and access them in the way you want. May be the order could be of interest.
3) another possibility could be to create a new class that has a property of type PictureBox and another property int ID. You could have a collection filled with these objects and each object could have a unique ID and the corresponding PictureBox. You can still put the picture boxes into the Controls and filter the collection according to your needs.
Create a method that will return instance of PictureBox
public PictureBox CreatePictureBox ()
{
// your code from question here
}
then define a field in your form
private Dictionary<string, PictureBox> pboxes = new Dictionary<string, PictureBox>();
Any time you want to create a new PictureBox put it inside pboxes collection:
pboxes.Add("box1", CreatePictureBox());
Now you can access to your boxes like this:
pboxes["box1"].Width += 20;

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

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.

Add picture boxes dynamically in row

I am trying to show images in a row..For this I am trying to add picture boxes dynamically. Image location is stored in databses. my code is as
int iCtr = 0;
for (int i = 0; i < dt.Rows.Count; i++)
{
PictureBox picture = new PictureBox
{
Name = "pictureBox"+i,
Size = new Size(316, 320),
Location = new Point(1, iCtr * 1100 + 1),
Visible = true
};
// string fname = dt.Rows[2]["FileName"].ToString();
picture.ImageLocation = dt.Rows[i]["FileName"].ToString();
//#"..\Images\80knhk00003.jpg";
pnlDisplayImage.Controls.Add(picture);
iCtr++;
}
where dt is datable.
with this I can see only last image but not all images. Even last image is very small and complete image is not shown.(i.e. I can view only one corner of actual image).
How can I give size to image so that it can be viewed completely?
And how can I display images in row?
Please help
Thanks
Can you try something like this? To get the scaling, see PictureBoxSizeMode.
List<PictureBox> pictureBoxList = new List<PictureBox>();
for (int i = 0; i < dt.Rows.Count; i++)
{
PictureBox picture = new PictureBox
{
Name = "pictureBox" + i,
Size = new Size(316, 320),
Location = new Point(i * 316, 1),
BorderStyle = BorderStyle.FixedSingle,
SizeMode = PictureBoxSizeMode.Zoom
};
picture.ImageLocation = dt.Rows[i]["FileName"].ToString();
pictureBoxList.Add(picture);
}
foreach (PictureBox p in pictureBoxList)
{
pnlDisplayImage.Controls.Add(p);
}
instead of Image property, set the BackGroundImage property to your picture location, and then set the BackGroundImageLayout to the value "Stretch" then you will see full image.

Categories