A random picturebox won't move when coded in Timer - c#

I don't know if I still have missing on this but whenever you want to add PictureBox, you just find it on Toolbox and drag it on the UI. But what I did is I coded it by adding PictureBox then what I want is I have to move it to the right. I coded it on a Timer and this is how I do.
private void enemyMove_Tick(object sender, EventArgs e)
{
GameMenu menu = new GameMenu();
if (menu.cmbox_Level.Text.Equals("Easy"))
{
this.Controls.Add(menu.EnemyTank);
menu.EnemyTank.Width = 26;
menu.EnemyTank.Height = 32;
menu.EnemyTank.BackgroundImage = Properties.Resources.Tank_RIGHT_v_2;
menu.EnemyTank.BackgroundImageLayout = ImageLayout.Stretch;
menu.EnemyTank.Left += 2;
}
}
Don't ask me if the Timer is started. Yes it already started, it was coded on the Form. But somehow when I start the program it doesn't move. But then I tried adding PictureBox, this time I tried dragging it to UI then add an Image on it, then coded it by moving to the right. When I start the program it works. But what I want is that whenever I start the button it will just make the random PictureBox move to the right. Idk what's the missing in here.

I made some assumptions. If I my assumptions aren't correct, please update your post for details. Following content is copied from my comment.
A new GameMenu will always create a new EnemyTank.
A EnemyTank's default Left value is fixed;
Your enemyMove_Tick is keep creating GameMenu and EnemyTank.
That is, every tick, a EnemyTank is created and value changed to "default value plus 2" in your code.
To avoid it, you have to keep EnemyTank instance somewhere instead of keep creating it.
Here's a example.
GameMenu m_menu;
void YourConstructor()
{
// InitializeComponent();
// something else in your form constructor
m_menu = new GameMenu();
m_menu.EnemyTank.Width = 26;
m_menu.EnemyTank.Height = 32;
m_menu.EnemyTank.BackgroundImage = Properties.Resources.Tank_RIGHT_v_2;
m_menu.EnemyTank.BackgroundImageLayout = ImageLayout.Stretch;
this.Controls.Add( m_menu.EnemyTank );
}
private void enemyMove_Tick(object sender, EventArgs e)
{
if (menu.cmbox_Level.Text.Equals("Easy"))
{
m_menu.EnemyTank.Left += 2;
}
}

Related

Edit Values of Shapes in List C#

Original Question: Edit properties of Graphics object c#
I've managed to create the object models and converted the entire program to use this structure so many thanks for the template there. My only problem is, I need to use threading to implement the color-changing feature. I have implemented the following code but have no luck:
private void pictureBox6_Click(object sender, EventArgs e)
{
running = !running;
}
public void redgreenMethod()
{
while (true)
{
if (running != true) continue;
if (flag == false)
{
flag = true;
Shape x = shapes[methodCounter - 1];
x.pen = Red;
}
else
{
flag = false;
Shape x = shapes[methodCounter];
x.pen = Green;
}
Thread.Sleep(500);
}
}
Surely the code inside the "if" statement changes the pen for the most recently added shape? I've entered a breakpoint and it shows that the pen does change constantly.. however, I don't see this being implemented on the Canvas?!
Where am I going wrong?!
From my previous comment:
Most likely redgreenMethod() is running in the main UI thread and the
tight loop is preventing the controls from updating themselves. Use a
TIMER instead. Set its Interval property to 500, and handle the Tick()
event. You don't need the while loop then. Instead of running you just
toggle the Enabled state of the Timer.
Here's what that might look like in code:
private void pictureBox6_Click(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
private void timer1_Tick(object sender, EventArgs e)
{
flag = !flag;
shapes[methodCounter - 1].pen = flag ? Green : Red;
}
You made these comments in an answer to your previous question:
Once a line has been drawn via the graphics: "g.DrawLine", the
properties of this line are then inaccessible. I need to find out how
to actually access these properties so I can use them inside the
Thread method.
In short, you DON'T access the properties of the line that has already been drawn. What you would do is update the color in the Shape (which you're already doing), and then call Invalidate() against the control that needs to be redrawn, such as pictureBox6.Invalidate(). This will force the Paint() event to fire again where everything will then be re-drawn, presumably with the new color that was set in the Shape properties.

Drawing Rectangles on an image from a different class

I am attempting to generate a set of rectangles on an image. To achieve this, I call a method that is present in a seperate class (Because multiple forms need to call this function.) Let's say I have Form A and Form B that both need to draw said set of rectangles:
From Form A it works just fine, from Form B it doesn't draw anything, but it doesn't return an exception either.
To make sure I haven't missed anything, I went as far as to copy and paste the function calls from both forms so the two of them are identical. I have also triple checked any semantic errors but have been unable to find any.
The function call from Form A is as follows:
private void PbPreview_Click(object sender, EventArgs e)
{
if (NewPage) //This bool is true when the user is displaying a new page(Image)
{
StartingY = MousePosition.Y - 76; //Save the Y position of the click in a float variable
Form1.MainController.DrawRect(StartingY, PbPreview.Image); //Function call
NewPage = false; //Set the new Page bool to false to prevent overdrawing
}
}
The function call from Form B is as follows:
private void PbFactuur_Click(object sender, EventArgs e)
{
if (NewPage) //Same use as the NewPage bool from above
{
MouseY = MousePosition.Y - 76; //Saving mouse position
Form1.MainController.DrawRect(MouseY, PbFactuur.Image); //Function call
NewPage = false; //Set new page to false to prevent overdrawing
MessageBox.Show("I have executed the function"); //Debug info
}
}
And here is the code that is present within the function:
public void DrawRect(float Ypos, Image DrawSubject)
{
try
{
foreach (Rectangle R in Form1.nieuwBedrijf.Rects)
{
Rectangle TempRect = R;
TempRect.Y = Convert.ToInt32(Ypos);
Graphics G = Graphics.FromImage(DrawSubject);
G.DrawRectangle(Pens.Black, TempRect.X * Form1.nieuwBedrijf.ScaleX, TempRect.Y * Form1.nieuwBedrijf.ScaleY, TempRect.Width * Form1.nieuwBedrijf.ScaleX, 1920);
}
}
catch
{
MessageBox.Show("No rectangles have been defined yet.");
}
}
Sidenote: Rects is a list of user defined rectangles.
The expected result would be that at the location where the user clicks, the set of rectangles will appear. But in reality, nothing appears at all.
The application doesn't return any sort of error message, and with the use of breakpoints and messageboxes I have been able to verify that the function does execute.
I hope anyone is able to point me to a potential solution to this problem.
Many thanks in advance
~Melvin
After some tinkering around I have found the following solution:
The rectangles were actually being drawn, but it was not being shown on screen. To have the rectangles show up on screen, I had to add the following line of code:
private void PbFactuur_Click(object sender, EventArgs e)
{
if (NewPage) //Same use as the NewPage bool from above
{
MouseY = MousePosition.Y - 76; //Saving mouse position
Form1.MainController.DrawRect(MouseY, PbFactuur.Image); //Function call
NewPage = false; //Set new page to false to prevent overdrawing
MessageBox.Show("I have executed the function"); //Debug info
Refresh();//<----- This line fixed the problem
}
}

Make buttons in List send unique data to method

I made a application which will place out buttons in a grid where the user specifies how big the playfield should be.
I create the buttons in a list, specify some data like backgroundimage, size, and location. I then need to, in some way make the different buttons execute different code. I figured I could do this in one method, (if there aren't any good ways to programmatically create methods), if I could somehow make the buttons send a unique piece of information to the method to identify which button is pressed.
public void buttonplacer()
{
int nbrofbtns = Form2.puzzlesize * Form2.puzzlesize;
List<Button> btnslist = new List<Button>();
for (int i = 0; i < nbrofbtns; i++)
{
Button newButton = new Button();
btnslist.Add(newButton);
this.Controls.Add(newButton);
newButton.Name = "btn" + i.ToString();
newButton.Width = btnsidelength;
newButton.Height = btnsidelength;
newButton.Top = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i / Form2.puzzlesize)));
newButton.Left = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i)) - Math.Floor((Convert.ToDouble(i)) / (Form2.puzzlesize)) * (Form2.puzzlesize));
newButton.BackgroundImage = Lights_out_.Properties.Resources.LightsOutBlack;
newButton.Click += new EventHandler(Any_Button_Click);
}
}
void Any_Button_Click(object sender, EventArgs e)
{
}
(If you want to know I'm doing a game called "Light's out")
Thanks in advance!
The Any_Button_Click method receives an object sender that is the button that got clicked. You just need to cast it to a Button:
void Any_Button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
// do stuff here
}
You can use the button's Location property to figure out where it sits on the game board, or you can assign an arbitrary object to the button with any information you choose at initialization time using the Tag property like this:
button.Tag = "someHelpfulString";
or like this:
button.Tag = new Tuple<int, int>(xpos, ypos);
(where xpos and ypos are positions in the button grid)
or like this:
button.Tag = new ButtonInfoObject(foo, bar, baz);
(Here it's up to you to define the ButtonInfoObject class.)
As an alternative to other answers, and in particular to somewhat address the part "good ways to programmatically create methods", there is part of the C# language called Lambda Expressions. To keep long story short, you could write something along these lines:
newButton.Click += (s, e) =>
{
//here you have access to all variables accessible in current scope,
//including "newButton" and "i";
//you could, for example, call some method passing "i" as an argument
//or just put that method's code inside this block
};
The only downside of this approach is that you need to take some extra care if you're planning to unregister the handler at some later point (see this question or this question for reference).
EDIT
As pointed in comments I overlooked the fact that i stays in scope for the whole for loop, so using it inside lambda is pretty much pointless (all handlers will use it's final value). To make it behave like expected one can simply define a variable inside the loop so it goes out of scope at the end of each iteration and is stored separately for each handler:
var btnNo = i;
newButton.Click += (s, e) =>
{
//use "btnNo" instead of "i"
//you can still safely use "newButton" reference
//since it's defined inside the loop
}
Use the sender object to get the button's name that was pressed:
void Any_Button_Click(object sender, EventArgs e)
{
switch ((sender as Button).Name)
{
case "btn0":
//...
break;
case "btn1":
//...
break;
//...
}
}

Text animation, creating typewriting-like effect in c#

This is what I did in MainPage.xaml.cs to create the text animation effect:
private readonly double TEXT_TIMER = 30.0;
private int index;
private void updateText(String text)
{
_text = text;
index = 0;
MainTextBlock.Text = "";
_textTimer.Tick += _textTimer_Tick;
_textTimer.Interval = TimeSpan.FromMilliseconds(TEXT_TIMER);
_textTimer.Start();
}
private void _textTimer_Tick(object sender, EventArgs e)
{
if (index < _text.Length)
{
string s = _text[index].ToString();
MainTextBlock.Text += s;
index++;
}
else
{
_textTimer.Stop();
}
}
I have a list of texts/strings and also a button, say NextButton on the MainPage.xaml. The updateText method is in the click event of NextButton, and what it does is to fetch a text from the list of texts/strings and update the textblock using the animation effect.
But I realised that as I continued to click on the NextButton, it was as if the value of TEXT_TIMER was reducing and the animation effect happened more rapidly until there was no animation anymore (i.e. the text just appeared in the textblock without any effects).
Anybody got any idea why this is happening and how I can fix it?
EDIT:
I have included code to stop the timer after the textblock has been updated with the hopes that solves possible multiple Tick callbacks but nothing still.
It's not that TEXT_TIMER value is reducing, but _textTimer is not stopping, so you have multiple timer ticks.
Try adding _textTimer.Stop() at the start of updateText() method.
On Dan Bryant's advice:
My program flow was registering for the Tick event multiple times beacuse I put it into the updateText method which got called multiple times during program run. All I had to do was move that line of code where the Tick event is registered, _textTimer.Tick += _textTimer_Tick; into the constructor, i.e after InitializeComponent() and problem solved!

Slide object using For Loop (C#)

I did quite a bit of searching around and didn't find anything of much help.
Is it possible to "slide" or "move" using C#, an object from one Location to another using a simple For loop?
Thank you
I would suggest you rather use a Timer. There are other options, but this will be the simplist if you want to avoid threading issues etc.
Using a straight for loop will require that you pump the message queue using Application.DoEvents() to ensure that windows has the opportunity to actually render the updated control otherwise the for loop would run to completion without updating the UI and the control will appear to jump from the source location to the target location.
Here is a QAD sample for animating a button in the Y direction when clicked. This code assumes you put a timer control on the form called animationTimer.
private void button1_Click(object sender, EventArgs e)
{
if (!animationTimer.Enabled)
{
animationTimer.Interval = 10;
animationTimer.Start();
}
}
private int _animateDirection = 1;
private void animationTimer_Tick(object sender, EventArgs e)
{
button1.Location = new Point(button1.Location.X, button1.Location.Y + _animateDirection);
if (button1.Location.Y == 0 || button1.Location.Y == 100)
{
animationTimer.Stop();
_animateDirection *= -1; // reverse the direction
}
}
Assuming that the object you're talking about is some kind of Control you could just change the Location property of it.
So something like this:
for(int i = 0; i < 100; i++)
{
ctrl.Location.X += i;
}
Should work I think.

Categories

Resources