C# Dynamic Form Help - c#

I'm trying to make something of a Tile-Map editor in C# using picture-boxes (the simplest way i could think to throw tiles into a form)
The code that Generates the picture boxes is:
public void Generate_Tiles(int width, int height)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
System.Windows.Forms.PictureBox picturebox = new PictureBox();
picturebox.BackColor = SystemColors.ActiveCaption;
picturebox.Cursor = Cursors.Cross;
picturebox.Location = new System.Drawing.Point((x*32) + 4, (y*32) + 4);
picturebox.Name = x+y+"tile";
picturebox.Size = new System.Drawing.Size(32, 32);
picturebox.TabIndex = 0;
picturebox.TabStop = false;
picturebox.Click += new System.EventHandler(TileBox_Clicked));
map.Controls.Add(picturebox);
}
}
MessageBox.Show("Done");
}
That part works, as long as i use a small number of picture boxes at a time (8 by 8 seems to be the maximum that it wants to display in a decent amount of time)
I want to perform some action when the user clicks on a specified picture-box, which is why i have a onclick method, that's where i run into problems, all the picture-boxes are called... picture-box. As far as i can tell, there's no way for me to tell which picture-box the user clicked.
I will probably need to remake the way the dynamic form works anyway, since i cant get very many picture boxes, but I think the main problem will still be there, as long as i want it to be dynamic (which i do), not all of the tile-maps will be the same size.
Ive never done anything like this, and I've looked for ways to override the onclick event... which i couldn't find, and i couldn't find a good tile-engine that's up to date to use (except for XNA, but that's a little over the top for a simple tile-editor, i think)
Im likely going in the opposite direction then what i need to be doing.

The sender in your event handler will be the PictureBox which was clicked.
void TileBox_Clicked(object sender, EventArgs e)
{
PictureBox pictureBox = sender as PictureBox;
if(pictureBox != null)
{
//do stuff with your pictureBox
}
}

Can't you just cast the sender object to a PictureBox in your event handler, and work with that?

The first parameter in your event handler is the clicked control (which will be the PictureBox.
private void pictureBox1_Click(object sender, System.EventArgs e)
{
PictureBox pb = (PictureBox)sender;
// ...
}

I've taken this approach with a map tile editor as well. As others have said, you can use the sender parameter and cast it to a PictureBox to get the PictureBox object that was clicked. But I think you'll find (you probably already have) that a decent map size will really slow things down.
You might reconsider XNA. It seems complicated, and it's not as easy to get something working as just generating a bunch of PictureBox objects. But it's fast, and it's you can get a 2D map being displayed fairly quickly. You can also extend it easier, for example if you want your map to have different editable layers (terrain, etc.).

There are actually lots of ways to do this. I think the easiest is to use the sender of your event handler, which will point you back to the clicked button:
private void PictureBox_Click(object sender, EventArgs e)
{
var box = sender as PictureBox;
if (box != null)
{
//...
You can also create and add event handlers dynamically, which is an interesting option:
//...
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
System.Windows.Forms.PictureBox picturebox = new PictureBox();
pictureBox.Click += new Action<object, EventArgs>(
(sender, e) => { doStuff(pictureBox); }
);
In the above code, doStuff gets pictureBox as a parameter, so it knows which object it's supposed to work on.

Related

Fire an event simultaneously for multiple dynamic controls in c# winform

I have N number of dynamically added PictureBoxes in FlowLayoutPanel.
When I create them I attach event handlers to them. For example:
for(int i=0;i<x;i++) {
var pe= new PictureBox();
pe.MouseUp+=mouseup;
pe.MouseDown+=mouseDown;
pe.MouseMove+=mouseMove;
pe.Paint+=paint;
}
My goal is to fire those events for all picture boxes whenever I work with any one of them. For example, if I move one picturebox (1st/2nd/3rd/.../n ) all others will move automatically, if I zoom any box, others will zoom automatically. How can I fire events simultaneously for all pictureboxes when I work with anyone.
If I try for example:
void mouseWheel(object sender, MouseEventArgs e) {
var control=(PictureBox)sender;
var parent=control.parent;
var pictureBoxes=parent.ofType<PictureBox>();
foreach(pb in pictureBoxes) {
//do something
}
}
It only works for the picture box I am working with.
You need to call a method instead of raising the event.
Create some methods and put logic on the methods, then in the event handler, first extract information that you need, then call suitable method with parameters.
For example:
void pictureBox_MouseWheel(object sender, MouseEventArgs e)
{
//Some parameter that you extract from eventArgs or somewhere else
int zoomFactor = e.Delta;
//Call the method on your picture boxes
foreach (var p in pictureBoxes)
{
Zoom(p, zoomFactor);
}
}
//The method that contains logic of zoom on a picture box
public void Zoom(PictureBox p, int zoomFactor)
{
//It is just an example, not a real logic
p.SizeMode = PictureBoxSizeMode.Zoom;
p.Width += (zoomFactor * 10);
p.Height += (zoomFactor * 10);
}
I supposed you have added all your pictureboxes in a List<PictureBox> when you created them.
Also if you have added your picture boxes to a Controls collection of a control, for example theControl, then you can find them later this way:
var pictureBoxes = theControl.Controls.OfType<PictureBox>().ToList();
It looks like you already have a list of picture boxes. So, try modifying your functions (for example your Zoom function) to work for all picture boxes in the list, rather than just the one picture box.
In other words, don't try to call the event handler for each picture box, make each event handler call a function, which modifies all picture boxes.

How do I resize a dynamically created text box by dragging with mouse like in paint programs?

I have created a text box, inside a PictureBox, on mouse click at run time. Now I want to resize it using mouse drag. Is there some simple way to do this ?
Here is the code I have so far:
public partial class Form1 : Form
{
public static TextBox PTB; //declaring text box to be created
public static bool textOption; //stores the state of button , i.e whether or not text box button is clicked before or not
public Form1()
{
InitializeComponent();
this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
}
private void pictureBox1_Click(object sender, EventArgs e)
{
if (textOption == true)//if user selected option to draw text box
{
MouseEventArgs eM = (MouseEventArgs)e; //create an instance of mouse event
PTB = new TextBox();//dynamically creating text box
PTB.Location = new System.Drawing.Point(eM.X, eM.Y);//settign position of textbox where mouse was clicked
PTB.Name = "textBox1";
PTB.Size = new System.Drawing.Size(100, 20);//size of text box
this.pictureBox1.Controls.Add(PTB);//adding the textbox to the picture box
}
}
Update
I'm sorry I have totally misread your question.
The reason is probably that you seem to be under the impression that Paint programs have TextBoxes sitting on their canvas. They don't. They draw text the same way as they draw lines or circles etc..
Also: Resizing the TextBox will not change the Font size, in case that's is what you want.
Finally: A TextBox will never be transparent, it will always sit on the PictureBox and look, well, like a TextBox. Not like anything in a Paint programm..
But: If you actually do want to resize the TextBox here are a few hints:
You need some way to show the user they are on the right spot by changing the cursor to the right icon
You need to store the mouse down point (attention: it will be inside the TextBox!) and keep track of the increments in the MouseMove. As long as the Button is down all rported e.Location will still be in the TextBox coordinates.
Use the increments (not the absolute values) to increase the size of the TextBox.
It is really hard to get the resizing right on the top and left sides. (Because it will involve moving at the same time), so better don't try!
Do include moving, which is easy and will suffice for all you need.
No, this is a good deal harder than increasing font size. 200-300 lines of code, the last time I did it..
But you may find another somewhat simpler answer; look for "Resize Control with Mouse winforms c#" on Google..
I leave the old answer in place, even if it not what you were looking for..
Old answer about changing font size while placing text:
It is not very hard but you need to get it right; it is basically the same as drawing a Rectangle
with a live preview. You need these things: four events, a Point or two, a Size and a font variable..
The events are:
MouseDown
MouseMove
MouseUp
Paint
You need to store a point for the placement (in the MouseDown event) and a size you update in the MouseMove.
From that size you can calculate the maximum Font size you can fit in the Rectangle.
On MouseUp you finalize things.
In the Paint event you draw string at the down Point with the current Font size.
In the MouseMove you call Invalidate on the PictureBox to trigger the Paint event.
in the MouseMouve you should check the Button to be the left one.
For extra good UI you can also check the keyboard for space and use it to move the DownPoint..
The Click event is rather useless, btw..
Here is a minimal code example to get you started:
Point mDown = Point.Empty;
float fSize = 12f;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Font font = new Font("Consolas", fSize))
e.Graphics.DrawString("Hello World", font, Brushes.Black, mDown);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
pictureBox1.Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
fSize = Math.Abs(e.Y - mDown.Y) / 2f + 1;
pictureBox1.Invalidate();
}
I left out the MouseUp. Here you would store the final state (font, location..) of the drawn string somewhere or persist it by drawing into a Bitmap etc..
I also didn't do a full Rectangle calculation but determined the font size by simply scaling the y-movement down a little.. You could improve with a little pythagoras ;-)
The ability to resize a window is innate behavior, provided by the default window procedure built into Windows. All you have to do is give the control a resizable border. Everything else is for free, you'll get the correct cursor and dragging a corner or edge resizes the control.
using System;
using System.Windows.Forms;
class ResizeableTextBox : TextBox {
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
cp.Style |= 0x840000; // Turn on WS_BORDER + WS_THICKFRAME
return cp;
}
}
}
This sample will create the TextBox on the MouseDown event and start resizing, the final size of the TextBox will be where the mouse button is released on the MouseUp event. It will let you create multiple textboxes as well.
I realize this might not be exactly what you want but it might be a start.
private int _textBoxCounter;
private TextBox _textBoxCurrentResizing;
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += PictureBox1OnMouseDown;
pictureBox1.MouseUp += PictureBox1OnMouseUp;
pictureBox1.MouseMove += PictureBox1OnMouseMove;
}
public Point RelativeMousePosition { get { return PointToClient(MousePosition); } }
private void PictureBox1OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
ResizeTextBox();
}
private void PictureBox1OnMouseUp(object sender, MouseEventArgs mouseEventArgs)
{
EndResizeTextBox();
}
private void PictureBox1OnMouseDown(object sender, MouseEventArgs mouseEventArgs)
{
var tb = CreateTextBox();
StartResizeTextBox(tb);
}
private TextBox CreateTextBox()
{
var tb = new TextBox
{
Location = RelativeMousePosition,
Size = new Size(100, 20),
Multiline = true,
Name = "textBox" + _textBoxCounter++,
};
pictureBox1.Controls.Add(tb);
return tb;
}
private void StartResizeTextBox(TextBox tb)
{
_textBoxCurrentResizing = tb;
}
private void ResizeTextBox()
{
if (_textBoxCurrentResizing == null) return;
var width = Math.Abs(_textBoxCurrentResizing.Left - RelativeMousePosition.X);
var height = Math.Abs(_textBoxCurrentResizing.Top - RelativeMousePosition.Y);
_textBoxCurrentResizing.Size = new Size(width, height);
}
private void EndResizeTextBox()
{
_textBoxCurrentResizing = null;
}

Handling an array of buttons

I'm creating a simple game where a melody is played, and buttons that corresponds to notes are supposed to be highlighted.
Then the user is to push the button, and during each click the buttons are to highlighted again. I would like to place all the buttons in the GUI graphically.
Can I add a highLight method to the buttons in the GUI? I know I probably could create a new class that ineherits from some button class and create the buttons in the code but I would prefer to do it graphically.
What is the neatest way to handle the button outputs? I know I could paste in code for each button like
private void button_withIndexA(object sender, EventArgs e)
{
checkIfThisNoteWasCorrect();
highLightThisButton();
setHighLightOffForAllOtherButtons();
}
However, I think it would be neater to collect all buttons in some sort of container class and make a function like
class buttonArrayHandler
{
/*constructors etc*/
private void someButtonWasClicked(object sender)
{
/*Check which button was clicked, and do stuff accordingly*/
}
}
However I don't know how to do that. Suggestions?
You could change the button colors to create a highlight effect, but if it's a game you can use images / graphics for buttons, and swap them to another graphic when clicked.
When you double click on a button / graphic / control, by default it creates a method and links it to the click action for you. Instead click on each control, then the little lightning icon, and under the click action, pick the same method for all of them.
Then in your method cast the object sender to get the original control, for example:
var clickedButton = (Button)sender;
Where (Button) may be (Graphics) or whatever type of control you used as a button.
EDIT:
If you need to access a group of controls, you can either keep a global list of names at the top of the form and loop through them:
public List<string> buttonList = new List<string>() { "button1", "button2" };
void SomeMethod()
{
foreach (var controlName in buttonList)
{
this.Controls[controlName].Text = "TEST";
}
}
Or use a fixed name and number range:
void SomeMethod()
{
for (int i = 1; i <= 2; i++)
{
this.Controls["button" + i].Text = "TEST";
}
}
I would recommend adding the buttons programatically just like Carlos487 mentioned, here is the code segment that I made and could work to your advantage:
public Form1()
{
InitializeComponent();
int topMod = 0;
for (int i = 0; i < 5; i++)
{
MakeButton(i,topMod);
topMod += 20;
}
}
public void MakeButton(int index, int margin)
{
Button currentButton = new Button();
currentButton.Text = "Note" + index;
currentButton.Top += margin;
currentButton.Click += OnButtonClick;
panel1.Controls.Add(currentButton);
}
public void OnButtonClick(object sender, EventArgs e)
{
//checkIfThisNoteWasCorrect();
//highLightThisButton();
//setHighLightOffForAllOtherButtons();
MessageBox.Show("My Action was activated!");
}
As you can see you can place the common functions within the OnButtonClick method like I did here, giving all of the buttons the same event sequence. If you do not want to go through the whole process of programming buttons then you could also just do this:
currentButton.Click += OnButtonClick;

Calling events for n buttons on the form

In my previus post I asked how to create a set of n draggable buttons, which worked fine, but I'm now working on the following:
I placed an image on the form, and loaded a coordinate matrix, so that when I also drag the image, the buttons "follow the image", I'm trying to acomplish it by calling an event when the button is finished being dragged, calling an event and transforming "image coordinates" to "form coordinates". It works fine for just one, but the problem arises when I place n buttons, since I don't know how to "recognize" which button called the event, I'll show you how I did my stuff:
int x, y;
//Creates a set of four buttons with an icon
for (int i = 0; i < 4; i++)
{
x = rnd.Next(1, this.Width - 30);
y = rnd.Next(1, this.Height - 30);
botonCustom newboton = new botonCustom(32, 32, new Point(x, y), imageList1);
//New event for each button (Is it ok to do?)
//I tried to call the same function newboton_Move, since i do not know how to create an event for each button
newboton.Move += new EventHandler(newboton_Move);
//Name the button and writes it on a lablel
newboton.Description = EtiDiamond[i];
DiamondButton.Add(newboton);
this.Controls.Add(newboton);
}
Here is the function being called:
private void newboton_Move(object sender, EventArgs e)
{
// Here i use the coordinates transform method, i won't place the code because
// its too big and it goes against the rules :P, i think if i could somehow know which button called this...
}
Thanks for reading this
sender is the button that raised the event
var myButton = sender as Button;

Check state of every picturebox together on submit

Here is my question:
Im making a booking cinema system in c#, windows form
Let's say i have 5 columns of 5 rows of pictureboxes that on form load get their value, avaliable or not from the database.
The user then click on the seat he wants (and the image of the pictuebox change) and press a submit button.
How i can check the image of every picturebox (to determine if he want this seat or not) together?
I can do something like this
if (picturebox11.image=="seatchecked"){seats[]+=11;}
if (picturebox12...
But im wondering if there is another faster way to do it. (the position of the pictureboxes is fixed if that helps)
I have done this so far:
private void button1_Click(object sender, EventArgs e)
{
List<PictureBox> pb = new List<PictureBox>();
pb.Add(seat11);
pb.Add(seat12);
pb.Add(seat13);
pb.Add(seat14);
pb.Add(seat15);
pb.Add(seat21);
pb.Add(seat22);
pb.Add(seat23);
pb.Add(seat24);
pb.Add(seat25);
pb.Add(seat31);
pb.Add(seat32);
pb.Add(seat33);
pb.Add(seat34);
pb.Add(seat35);
for (int i = 0; i < 20; i++) {
pb[i].Click += pictureBox_Click;
}
}
void pictureBox_Click(object sender, EventArgs e)
{
this.pictureBox.Image = ArgyroCinema.Properties.Resources.seatred;
}
Store each PictureBox in a list and iterate through them. Also, when the user selects/deselects a seat, change the Tag property of the PictureBox as, at the moment, you're trying to compare a string to an Image (picturebox11.Image returns an Image object).
List<PictureBox> pb = new List<PictureBox>();
pb.Add(pictureBox1);
pb.Add(pictureBox2);
//etc..
Alternatively, you can use the methods suggested here to get all PictureBox objects in your form to save you having to type it out above.
Then just iterate through them and read their Tag property. In this case I've used true to represent that they want the seat but Tag is an object type, so you can use whatever type you like.
foreach(PictureBox p in allPictureBoxes)
{
if((bool)p.Tag == true)
{
//seat wanted
}
else
{
//seat not wanted
}
}
Update from comments
void pictureBox_Click(object sender, EventArgs e)
{
PictureBox pb = sender as PictureBox;
if(pb != null)
pb.Image = ArgyroCinema.Properties.Resources.seatred;
}

Categories

Resources