Form Updating while Calculation Runs - c#

Started learning to code.
Have a console writing a 10 by ten grid of numbers randomly increasing in value.
Tried to do same in a form (DataGridView).
It works fine but there is no window until calcs are finished (I had to put a limit in - instead of an infinite loop).
I came here and read about refresh and doevent - but they dramatically slow everything down. But at least I can see the calcs happening.
I've tried to get my head around backgroundworker and I'm afraid I can't. If I'm in a loop to calculate - how do I separate those calcs from a screen update.
EDIT : Followed Nico's help(Thanks!!) and got this. Much better, but still laggy with big numbers. But a rocket with small numbers.
Any help to make faster?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace stackoverflowTest
{
public partial class Form1 : Form
{
Random rnd = new Random((Int32)DateTime.Now.Ticks);
private static int numSides = 8;
int numDice=2;
private int numRolls = 100000000;
private int max = 1;
private int min = 0;
private int diff = 0;
private double pdiff = 0d;
int[] array = new int[numSides *numSides];
public Form1()
{
InitializeComponent();
}
private void Form1Load(object sender, EventArgs e)
{
SetupDataForm1();
SetupDataForm2();
var bw = new BackgroundWorker();
bw.DoWork += BwDoWork;
bw.RunWorkerAsync(); //Start the worker
}
private void BwDoWork(object sender, DoWorkEventArgs e)
{
for (int rolls = 1; rolls < numRolls+1; rolls++)
{
// Roll two dice and increase that slot in the table
int y = Dice.Roll(numSides, rnd);
int x = Dice.Roll(numSides, rnd);
int k = Convert.ToInt32(dataGridView1.Rows[x].Cells[y].Value);
dataGridView1.Rows[x].Cells[y].Value = k + 1;
//Enter table into an array to work out max/min etc later
for (int i = 0; i < (numSides * numSides); i++)
{
int row = i / numSides;
int col = i % numSides;
array[i] = Convert.ToInt32(dataGridView1.Rows[row].Cells[col].Value);
}
max = array.Max();
min = array.Min();
diff = max - min;
if (max > 0) pdiff = (((double)diff / (max)) * 100);
dataGridView2.Rows[0].Cells[0].Value = rolls;
dataGridView2.Rows[1].Cells[0].Value = max;
dataGridView2.Rows[2].Cells[0].Value = min;
dataGridView2.Rows[3].Cells[0].Value = diff;
dataGridView2.Rows[4].Cells[0].Value = pdiff.ToString("0.000");
dataGridView2.Rows[5].Cells[0].Value = (array.Average()).ToString("0");
dataGridView2.Rows[6].Cells[0].Value = ((array.Average()/rolls)*100).ToString(("0.0000000"));
}
}
private void SetupDataForm1()
{
dataGridView1.Font = new Font("Microsoft Sans Serif", 6F);
dataGridView1.RowTemplate.Height = 11;
//Add Columns
for (int i = 0; i < numSides; i++)
{
dataGridView1.Columns.Add(i.ToString(), (i+1).ToString());
dataGridView1.Columns[i].Width = 35;
}
// Add Rows
for (int i = 0; i < numSides; i++)
{
dataGridView1.Rows.Add();
if (i % 2 != 0)
{
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.LightGray;
}
dataGridView1.Rows[i].HeaderCell.Value = ((i+1)*10).ToString();
}
}
private void SetupDataForm2()
{
dataGridView2.Font = new Font("Microsoft Sans Serif", 8F);
dataGridView2.RowTemplate.Height = 16;
//Add Columns
for (int i = 0; i < 1; i++)
{
dataGridView2.Columns.Add(i.ToString(), "");
dataGridView2.Columns[i].Width = 65;
}
// Add Rows
for (int i = 0; i < numSides; i++)
{
dataGridView2.Rows.Add();
if (i % 2 != 0)
{
dataGridView2.Rows[i].DefaultCellStyle.BackColor = Color.LightGray;
}
}
dataGridView2.Rows[0].HeaderCell.Value = "Rolls";
dataGridView2.Rows[1].HeaderCell.Value = "Max";
dataGridView2.Rows[2].HeaderCell.Value = "Min";
dataGridView2.Rows[3].HeaderCell.Value = "Diff";
dataGridView2.Rows[4].HeaderCell.Value = "%";
dataGridView2.Rows[5].HeaderCell.Value = "Avg";
dataGridView2.Rows[6].HeaderCell.Value = "Av%";
}
public class Dice
{
public static int Roll(int numberOfSides, Random rnd)
{
return rnd.Next(0, numberOfSides);
}
}
}
}

If there is no window, you perform the calculations in the form's constructor. To make the form visible before starting the calculation, put the code in the form's Load event. Therefore double click the event in the form's properties window and a method will be created. Put your code into this method.
If you want to use a background worker, the procedure is similar. However, you need to create the Backgroundworker. E.g. in code:
private void Form1_Load(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
bw.DoWork +=
If you start typing that, Visual Studio suggests to create a method for the event. Press Tab twice to generate it and you'll basically get the following code:
private void Form1_Load(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerAsync(); //Start the worker
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
throw new NotImplementedException(); //remove this
}
Put your calculation code in the bw_DoWork method an it will be executed in the background without affecting the user interface.

Related

How to create MaterialSingleLineTextField Controls dynamically

I would like to add MaterialSingleLineTextField dynamically to a Form.
I have used MaterialSkin NuGet package:
I am trying to create multiple MaterialSkin TextBoxes dynamically on Form.Load. But no Controls are displaying in the hosing Panel.
private void Form1_Load(object sender, EventArgs e)
{
int n = 5;
int pointX = 30;
int pointY = 40;
//panel1.Controls.Clear();
for (int i = 0; i < n - 1; i++)
{
MaterialSingleLineTextField a = new MaterialSingleLineTextField();
a.Text = (i + 1).ToString();
a.Visible = true;
a.Location = new Point(pointX, pointY);
panel1.Controls.Add(a);
panel1.Show();
pointY += 20;
}
}
This code block works perfectly fine for normal TextBoxes.
Is there any way to add MaterialSingleLineTextField dynamically?
A sample Form initialization with default Theme values.
The MaterialSkinManager is initialized in the Form Constructor, setting the Theme to MaterialSkinManager.Themes.LIGHT and default color scheme. The Form base Type is set to MaterialForm.
A specified number of MaterialSingleLineTextField controls is added to a parent container (a Panel), starting from a defined Location downwards.
These Controls are anchored to the parent and the Height is set to Parent.Font.Height + 4.
It's important that you specify the Size of these Controls, otherwise you'll get a minimal size that prevents the Controls from showing their content.
As you can see in the AddTextFields() method, it's important that you dispose of the previous Controls added to the Parent container, if you want to replace the existing with new ones. Even more important using this Library.
Calling the Clear() method of a Control.Controls collection (as in the line you have commented out) doesn't dispose of anything, those controls are still alive.
public partial class Form1 : MaterialForm
{
private readonly MaterialSkinManager msManager = null;
public Form1()
{
InitializeComponent();
msManager = MaterialSkinManager.Instance;
msManager.AddFormToManage(this);
msManager.Theme = MaterialSkinManager.Themes.LIGHT;
}
private void Form1_Load(object sender, EventArgs e)
{
AddTextFields(panel1, 5, new Point(30, 10), false);
}
private void AddTextFields(Control parent, int controlsCount, Point startPosition, bool ClearExisting)
{
if (clearExisting && parent.Controls.Count > 0) {
for (int i = parent.Controls.Count - 1; i >= 0; i--) {
parent.Controls[i].Dispose();
}
}
int controlHeight = parent.Font.Height + 4;
int yIncrement = 0;
for (int i = 0; i < controlsCount; i++) {
var textField = new MaterialSingleLineTextField() {
Text = (i + 1).ToString(),
Size = new Size(parent.ClientSize.Width - startPosition.X - 4, controlHeight),
Location = new Point(startPosition.X, startPosition.Y + yIncrement),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right
};
parent.Controls.Add(textField);
yIncrement += (controlHeight + 10);
}
}
private void matBtnChangeTheme_Click(object sender, EventArgs e)
{
msManager.Theme = MaterialSkinManager.Themes.DARK;
msManager.ColorScheme = new ColorScheme(Primary.Blue600, Primary.Blue900, Primary.Blue500, Accent.LightBlue200, TextShade.WHITE);
}
private void matBtnAddControls_Click(object sender, EventArgs e)
{
AddTextFields(panel1, 7, new Point(30, 10), true);
}
}
Sample functionality:

Maximum axis x Chart c#

I'm working on a project where I work with two graphs, and on the lower graph if I see the maximum of the x-axis (24) but on the upper graph no. How can I make it appear up on 24? I am using the chart windows in visual studio, the default chart in windows forms
Chart image
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
chart1.ChartAreas[0].AxisX.Minimum = 0;
for (int i = 0; i <= 24; i++)
{
chart1.Series[0].Points.AddXY(i,1+i);
}
}
}
Change your code to (more information here):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Maximum/Minimum controls the length of the Axis
chart1.ChartAreas[0].AxisX.Maximum = 30;
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 30;
chart1.ChartAreas[0].AxisY.Minimum = 0;
// Interval controls the interval between values on the chart
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Interval = 1;
}
private void button1_Click(object sender, EventArgs e)
{
chart1.ChartAreas[0].AxisX.Minimum = 0;
for (int i = 0; i <= 24; i++)
{
chart1.Series[0].Points.AddXY(i, 1 + i);
}
}
}
Chart looks like:

code improvement - naming textboxes generated form numericupdown & button click

I have a problem with a code that is regarding adding controls with numericUpDown ( for example- if numericUpDown value equals to 3, user recievs 3 textboxes).
Thanks to stackoverflow users I improved my code.
Before improvement it looked like this:
private void button1_Click(object sender, EventArgs e)
{
if (numericUpDown1.Value == 1)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
}
else if (numericUpDown1.Value == 2)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
}
else if (numericUpDown1.Value == 3)
{
txtbx1.AutoSize = true;
Controls.Add(txtbx1);
txtbx1.Location = new Point(70, 100);
txtbx2.AutoSize = true;
Controls.Add(txtbx2);
txtbx2.Location = new Point(70, 130);
txtx3.AutoSize = true;
Controls.Add(txtbx3);
txtbx3.Location = new Point(70, 160);
}
}
After improvement:
private void button1_Click(object sender, EventArgs e)
{
int y = 100;
int x = 70;
for (int i = 0; i < numericUpDown1.Value; i++)
{
var txtbx = new TextBox();
txtbx.AutoSize = true;
Controls.Add(txtbx);
txtbx.Location = new Point(x, y);
// Increase the y-position for next textbox.
y += 30;
}
}
Now the problem is that I don't know how assign names to genarated textboxes.
(before the improvement I could name them - txtbx1, txtbx2, txtbx3...)
Code to improve:
private void button3_Click(object sender, EventArgs e)
{
try
{
double a, b, c, sum;
a = double.Parse(txtbx1.Text);
b = double.Parse(txtbx2.Text);
c = double.Parse(txtbx3.Text);
sum = a + b + c;
label1.Text = sum.ToString();
}
catch (Exception ex)
{
MessageBox.Show("error");
}
Please note that I'm a beginner, learning c# by watching youtube tutorials ;) I do realize that my question might be silly but I couldn't handle this problem by myself.
In advance thank you for your time and help.
If you need to access them afterwards, you have some options.
I'll guess that your objective is to set label1's text to the sum of the values contained in the specified textbox(es).
On the ValueChanged event of your NumericUpDown, check the delta and consequentely add or remove the required number of TextBoxes to your Form's Controls. To obtain the delta, you'll need to store the previous value of the NumericUpDown, and then subtract it from the current value. (If it was 5, and now it's 4, 4 - 5 = -1. A textbox has been removed).
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
If, however, you only have a maximum number of 3, you could take a slightly different approach. My solution works with n-textboxes.
Finally, to get the TextBoxes' values from code, you have three approaches:
Loop through your Form's controls, check for TextBoxes with their name starting with "ntextBox", and add their values together
Use LINQ to do the same
Access them singularly via "Controls.Find("ntextBoxX", false)", where X is the number of course.
I'll show the LINQ approach as I like it better.
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
I haven't tested the code, but it should work. Tell me if there are any problems.
EDIT: Tested and it works. For the sake of completeness, I'll add some event logic to the TextBox-adding loop, to make sure that their input is actually numeric.
tbox.TextChanged += HandleNTextBoxInput; // add this line
And elsewhere, add this method:
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
As I mentioned in a comment- this code seems to be maybe too advanced for me.
I have no problem with adding the controls, bu still there is a problem how to get the sum from a button click to a textbox.
I probably made some simple mistakes, or something is missing but I really don't know how to fix this problem.
My code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace testprogram
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) // I'm guessing something is missing over here
{
int sum = Controls.Cast<Control>().Sum(c => c.Name.StartsWith("ntextBox") ? int.Parse(c.Text) : 0);
}
void HandleNTextBoxInput(object sender, EventArgs e)
{
string text = ((TextBox)sender).Text;
if (!Regex.IsMatch(text, "^[1-9][0-9]*$")) //Text is NOT numeric. Remove [1-9] if you want to keep leading zeros.
{
//Set the Text to 0, for example. Or show a message box. Or whatever.
((TextBox)sender).Text = "0";
}
}
private int _oldNUDvalue = 0; //or assign it to the default value
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
{
int delta = (int)numericUpDown1.Value - _oldNUDvalue;
if (delta < 0)
{
for (int i = -delta; i > 0; i--)
{
var tbox = Controls.Find("ntextBox" + (_oldNUDvalue - i), false)[0];
Controls.Remove(tbox);
}
}
else if (delta > 0)
{
for (int i = _oldNUDvalue; i < _oldNUDvalue + delta; i++)
{
var tbox = new TextBox();
tbox.Location = new Point(15, 55 + (30 * i));
tbox.Name = "ntextBox" + i;
tbox.TextChanged += HandleNTextBoxInput;
Controls.Add(tbox);
}
}
_oldNUDvalue = (int)numericUpDown1.Value;
}
}
}
}

Click on button and that will click on Key

In C# I want to have a button clicked. I don't want to use a button event like button.click+=... I want to click on the button in Form Application, and any key of my computer will be clicked. I tried SendKeys but it doesn't work well.
I need more ideas how to do it. I tried:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form5 : Form
{
Button[,] b;
List<char> chrlist = new List<char>();
public Form5()
{
InitializeComponent();
start();
}
public void start()
{
panel1.Controls.Clear();
int m = 13;
int n = 2;
b = new Button[n, m];
int x = 0;
int i, j;
int y = 0;
// int count = 0;
int chr1=(int)'A';
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
// count++;
b[i, j] = new Button();
b[i, j].SetBounds(x, y, panel1.Size.Width / m, panel1.Size.Height / n);
b[i, j].Name = i + "," + j;
b[i, j].Text = ((char)chr1).ToString();
b[i, j].Click += new EventHandler(ButtonClick);
panel1.Controls.Add(b[i, j]);
x = x + panel1.Size.Width / m;
chr1++;
}
y = y + panel1.Size.Height / n;
x = 0;
}
}
public void ButtonClick(Object sender, EventArgs e)
{
Button a = (Button)sender;
MessageBox.Show(a.Text);
}
public void started()
{
while (true)
{
int sec = 1000;
Thread.Sleep(sec * 3);
SendKeys.SendWait("{TAB}");
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread workerThread = new Thread(started);
workerThread.Start();
}
}
}
And that didn't work in my other application.
It looks to me like you're attempting to build an onscreen keyboard.
Premise: Use SendKeys to send a letter to the active application.
Question: When you click the button in your Form to send a letter, what application has focus?
Answer: Yours. Therefore the key will be sent to your application, not the last application that was focused.
Solution: Prevent your application from getting focused.
This can be accomplished by setting the WS_EX_NOACTIVATE flag in your extended window styles via CreateParams()
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= WS_EX_NOACTIVATE;
return p;
}
}
public void ButtonClick(Object sender, EventArgs e)
{
SendKeys.Send(((Control)sender).Text);
}
Now when you click a button in your OSK, the currently focused application will STAY focused (because your application cannot receive focus) and SendKeys() will correctly target it.
*Caveats: This only works if the onscreen keyboard is a different application. In other words, the OSK cannot target other forms in your own application...it's a weird focus thing. To target your own app you'd have to manually track the last form in your application and give it focus again before sending the keys.
Here's your OSK sending keys to Notepad:
This is ALL of the code in my test app:
public partial class Form1 : Form
{
private Button[,] b;
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= WS_EX_NOACTIVATE;
return p;
}
}
public Form1()
{
InitializeComponent();
start();
}
public void start()
{
panel1.Controls.Clear();
int m = 13;
int n = 2;
b = new Button[n, m];
int x = 0;
int i, j;
int y = 0;
// int count = 0;
int chr1 = (int)'A';
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
// count++;
b[i, j] = new Button();
b[i, j].SetBounds(x, y, panel1.Size.Width / m, panel1.Size.Height / n);
b[i, j].Name = i + "," + j;
b[i, j].Text = ((char)chr1).ToString();
b[i, j].Click += new EventHandler(ButtonClick);
panel1.Controls.Add(b[i, j]);
x = x + panel1.Size.Width / m;
chr1++;
}
y = y + panel1.Size.Height / n;
x = 0;
}
}
public void ButtonClick(Object sender, EventArgs e)
{
SendKeys.Send(((Control)sender).Text);
}
}

Timer speed automatically increase

I have following code. When I click on the button picturebox move from right to left and top to bottom. When it move to the end of the panel it again start from right to left and top to bottom. Now the problem is, after first completion of picturebox from right to left timer speed gradually increase though i set it to 200 also it seems that, this line myform.counterTop = myform.counterTop + 5; the value 5 also increase gradually. After first round, it increase a little, after second it increase little more and continues like this. Please tell me why this is happening.
namespace Spaceship_Invaders
{
public partial class Form1 : Form
{
private int invaderlanded = 0;
private int invaderstopped = 0;
private int counterfortop = -60;
private int counterforleft = 415;
private int counterTop = -60;
private int counterLeft = 415;
private bool pictureboxclicked = false;
private int timerinterval = 200;
System.Windows.Forms.Timer mytimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
Image myImage = Image.FromFile("image/Untitled6.png");
pictureBox1.Image = myImage;
pictureBox1.Top = counterfortop;
pictureBox1.Left = counterforleft;
}
public class Spaceship
{
Form1 myform;
public Spaceship(Form1 form)
{
myform = form;
}
public void mspaceship()
{
myform.mytimer.Tick += new EventHandler(TimerEventProcessor);
myform.mytimer.Interval = myform.timerinterval;
myform.mytimer.Enabled = true;
myform.mytimer.Start();
}
private void TimerEventProcessor(Object myObject, EventArgs myEventArgs)
{
if (myform.pictureboxclicked)
{
myform.mytimer.Interval = 5;
myform.pictureBox1.Top = myform.counterTop;
//myform.pictureBox1.Left = myform.counterLeft;
myform.counterTop = myform.counterTop - 5;
if (myform.counterTop <-60)
{
//myform.pictureBox1.Enabled = false;
//myform.pictureBox1.Hide();
myform.pictureboxclicked = false;
myform.mytimer.Interval = myform.timerinterval;
myform.counterLeft = 415;
myform.counterTop = -60;
myform.mytimer.Stop();
}
} else {
if (myform.counterTop > 370 || myform.counterLeft < 1)
{
//myform.pictureBox1.Enabled = false;
//myform.pictureBox1.Hide();
myform.invaderlanded++;
myform.textBox2.Text = myform.invaderlanded.ToString();
myform.counterLeft = 415;
myform.counterTop = -60;
myform.pictureboxclicked = false;
myform.mytimer.Interval = myform.timerinterval;
myform.mytimer.Stop();
} else {
myform.pictureBox1.Top = myform.counterTop;
myform.pictureBox1.Left = myform.counterLeft;
myform.counterTop = myform.counterTop + 5;
myform.counterLeft = myform.counterLeft - 5;
}
}
}
}
private void button4_Click(object sender, EventArgs e)
{
Spaceship myspaceship = new Spaceship(this);
myspaceship.mspaceship();
}
Every time you call mspaceship(), you add another event handler to the timer.
The second time you click it, you have two event handlers which each move by 5 pixels.
Instead, you should only add the handler once.

Categories

Resources