This is my first class:
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
/*_enemy = new Class1(this);
int y = Class1.MyMethod(0);
textBox1.Text = Convert.ToString (y);*/
}
private Class1 _enemy;
private void button1_Click(object sender, EventArgs e)
{
_enemy = new Class1(this);
int y = Class1.MyMethod();
textBox1.Text = Convert.ToString(y);
}
}
}
and this is my second class:
namespace WindowsFormsApplication2
{
public class Class1
{
public Class1( Form1 form )
{
_form1 = form;
}
public static int MyMethod()
{
int i = 0;
for (int j = 1; j <= 20; j++)
{
i = j;
//Thread.Sleep(100);
}
return i;
}
}
// DON'T initialize this with new Form1();
private Form1 _form1;
}
The program is running correctly and I am getting only 20 as output in the TextBox
. What I want is the output each time the loop runs.
Like 1,2,3,.........20
and stop.
Like a counter maybe. I also tried using Timer
but couldn't do that.
EDIT:
@Mong Zhu I have cross checked the code, still getting the exception.
For your reference here are the complete codes:
Form1.cpp
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
Class1 MyCounterClass;
private void Form1_Load(object sender, EventArgs e)
{
MyCounterClass = new Class1();
// register the event. The method on the right hand side
// will be called when the event is fired
MyCounterClass.CountEvent += MyCounterClass_CountEvent;
}
private void MyCounterClass_CountEvent(int c)
{
if (textBox1.InvokeRequired)
{
textBox1.BeginInvoke(new Action(() => textBox1.Text = c.ToString()));
}
else
{
textBox1.Text = c.ToString();
}
}
public Form1()
{
InitializeComponent();
}
private Class1 _enemy;
private void button1_Click(object sender, EventArgs e)
{
MyCounterClass.MyCountMethod(300, 0, 10);
}
}
}
and class1.cpp
namespace WindowsFormsApplication2
{
public class Class1
{
public delegate void Counter(int c); // this delegate allows you to transmit an integer
public event Counter CountEvent;
public Class1()
{
}
public void MyCountMethod(int interval_msec, int start, int end)
{
System.Threading.Thread t = new System.Threading.Thread(() =>
{
for (int i = start; i <= end; i++)
{
// Check whether some other class has registered to the event
if (CountEvent != null)
{
// fire the event to transmit the counting data
CountEvent(i);
System.Threading.Thread.Sleep(interval_msec);
}
}
});
// start the thread
t.Start();
}
// DON'T initialize this with new Form1();
private Form1 _form1;
}
}
If you are looking to report progress from some object back to your form you can use the IProgress<T>
interface instead. It is well explained here and here but to translate it to your given code it would look something like this:
public partial class Form1 : Form
{
private async void button1_Click(object sender, EventArgs e)
{
Progress<int> reporter = new Progress<int>(number =>
{
textBox1.Text = number.ToString();
});
await Task.Run(() => MyClass1.MyMethod(reporter));
}
}
public class Class1
{
public static int MyMethod(IProgress<int> reporter)
{
for (int i = 1; i <= 20; ++i)
{
reporter.Report(i);
//Thread.Sleep(100);
}
return i;
}
}
Note that
Class1
does not need any knowledge of Form1
.
- Since
Class1.MyMethod
is static you do not require an instance of it. If you want to modify fields/properties in Class1 you need an instance. It's really up to you if this is correct or not.
IProgress<T>
requires .NET Framework 4.5
The problem is that you only pass the last value to the GUI. What you could do is to pass the textbox which you want to use for display into your counting method MyMethod
. There you could assign the value. One last thing that you need to do is to tell the application to update it's events with Application.DoEvents();
So your method would look like this:
public static int MyMethod(TextBox t)
{
int i = 0;
for (int j = 1; j <= 20; j++)
{
i = j;
t.Text = j.ToString();
Application.DoEvents();
Thread.Sleep(200);
}
return i;
}
don't forget to include:
using System.Threading.Tasks;
using System.Windows.Forms;
in you Class1.cs
The call in Form1
would look like this:
private void button1_Click(object sender, EventArgs e)
{
_enemy = new Class1(this);
int y = Class1.MyMethod(textBox1);
}
Disclaimer: Application.DoEvents() should be avoided as pointed out by @Default.
Therefore another approach and probably a preferable one would be to use a timer. It has a Tick
event which could work like your for-loop. This one is System.Windows.Forms.Timer
. You can use it in the Form1
class:
public partial class Form1 : Form
{
Timer t = new Timer();
public Form1()
{
InitializeComponent();
t.Interval = 200; // set the interval
t.Tick += T_Tick; // register to the event
}
int i = 0; // this is your counting variable
private void T_Tick(object sender, EventArgs e)
{
if (i<=20) // this takes care of the end
{
this.textBox1.Text = i.ToString();
i++; // count up
}
else
{
t.Stop(); // stop the timer if finished
i = 0; // for the next time if you want to restart the timer
}
}
private void button1_Click(object sender, EventArgs e)
{
t.Start(); // now just start your timer
}
}
EDIT
Ok lets make things a little more complicated but thorough. You asked:
i.e. call a method somewhere else and print somewhere else. By somewhere else I mean another class
If you want to print it somewhere else it will be somewhere else ;) What I mean is that the responsibility of the graphical user interface is to display things. So it should remain displaying things. The responsibility of your method is to count up, so it should remain counting things up. To combine these two responsibilities in C# the concept of events is a powerfull one. It allows you to signal events and transmit data.
The first thing you need is an event to signal the counting in Class1
:
It has 2 Parts. A delegate which defines the structure of the method that will be called when the event is fired, and the event of the type of the delegate that can be registered in another class. In your case the Form1
.
public class Class1
{
public delegate void Counter(int c); // this delegate allows you to transmit an integer
public event Counter CountEvent;
public Class1()
{
}
I removed the instance of Form1 _form
from Class1
. Because you don't need it for the task. Also this makes your Class1
independent of the implementation of the GUI. (If you decide tomorrow to change the name of the TextBox
or choose a Label
to display the counter, there will be no changes to make in the Class1
, only in Form1
!) Now you can register/subscribe to the event in the Form1
and create the eventhandler which will be called when the event is fired:
Form1
Class1 MyCounterClass;
private void Form1_Load(object sender, EventArgs e)
{
MyCounterClass = new Class1();
// register the event. The method on the right hand side
// will be called when the event is fired
MyCounterClass.CountEvent += MyCounterClass_CountEvent;
}
private void MyCounterClass_CountEvent(int c)
{
if (textBox1.InvokeRequired)
{
textBox1.BeginInvoke(new Action(() => textBox1.Text = c.ToString()));
}
else
{
textBox1.Text = c.ToString();
}
}
Since we don't want the GUI to freeze when it is counting we will use a System.Threading.Thread to count in the background and transmit the data via the event. Now this will lead to problems, because the textBox1
is created by the main thread and if you try to access it through another one it will crash. So you need to use the BeginInvoke method to display the counting variable which is transmitted via the event.
The only thing that is left is to implement the counting method. As you see I removed the static
keyword. Because that would make it necessary to declare the event also as static
and that would mean that it exist only one time. This will lead to difficulties if you would try to subscribe to this event from a second class.
Not we take your loop and put it in a Thread and let the thread run. At each iteration it will fire the event and transmit your counting data:
public void MyCountMethod(int interval_msec, int start, int end)
{
System.Threading.Thread t = new System.Threading.Thread(() =>
{
for (int i = start; i <= end; i++)
{
// Check whether some other class has registered to the event
if (CountEvent != null)
{
// fire the event to transmit the counting data
CountEvent(i);
System.Threading.Thread.Sleep(interval_msec);
}
}
});
// start the thread
t.Start();
}
To start the method is the easiest part. Just specify the interval, start and end and call the method as you would call a normal method:
private void button1_Click(object sender, EventArgs e)
{
MyCounterClass.MyCountMethod(300, 0, 10);
}
Et voilà, you have a class which can count up and signal the counting progress. And it is independent of the graphical user interface. It has to dependencies on Form1
. And every class remains taking care of their own responsibilities.
Hope it helps
Maybe think about an event?
namespace WindowsFormsApplication2 {
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
/*_enemy = new Class1(this);
int y = Class1.MyMethod(0);
textBox1.Text = Convert.ToString (y);*/
}
private Class1 _enemy;
private void button1_Click(object sender, EventArgs e)
{
_enemy = new Class1(this);
_enemy.LoopInteration += OnLoopInteration;
_enemy.MyMethod();
_enemy.LoopInteration -= OnLoopInteration;
}
private void OnLoopInteration(object sender, LoopCounterArgs e)
{
textBox1.Text = Convert.ToString(e.Iteration);
}
}
}
second form:
namespace WindowsFormsApplication2
{
public class Class1
{
public event EventHandler<LoopCounterArgs> LoopInteration;
public Class1( Form1 form )
{
_form1 = form;
}
public void MyMethod()
{
for (int j = 1; j <= 20; j++)
{
LoopInteration?.Invoke(this, new LoopCounterArgs(j));
//Thread.Sleep(100);
}
}
}
// DON'T initialize this with new Form1();
private Form1 _form1;
}
then, new class to handle the custom event args:
namespace WindowsFormsApplication2
{
public class LoopCounterArgs : EventArgs
{
public int Iteration { get; set; }
public LoopCounterArgs(int iteration)
{
Iteration = iteration;
}
}
}
I haven't tested this so may contain some bugs, but should be pretty much there...
You might want to re-think the textBox1.Text statement as it'll work that quickly, the value may appear as 20, when in fact it has done all the iterations for you.