我试图在一个类中创建一个方法,并尝试通过单击按钮从另一个类(窗体)调用
这是我的第一堂课:
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); } } }
这是我的第二堂课:
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; }
程序运行正常,我在TextBox
只输出20作为输出。 我想要的是每次循环运行时的输出。
像1,2,3,.........20
和停止。
可能就像一个柜台。 我也试过使用Timer
但不能这样做。
编辑:
@Mong Zhu我已经交叉检查了代码,仍然得到了exception。
这里有完整的代码供您参考:
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); } } }
和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; } }
如果您要将某些对象的进度报告回表单,则可以使用IProgress
界面。 这里和这里都有很好的解释,但要将它转换为您给定的代码,它看起来像这样:
public partial class Form1 : Form { private async void button1_Click(object sender, EventArgs e) { Progress reporter = new Progress (number => { textBox1.Text = number.ToString(); }); await Task.Run(() => MyClass1.MyMethod(reporter)); } } public class Class1 { public static int MyMethod(IProgress reporter) { for (int i = 1; i <= 20; ++i) { reporter.Report(i); //Thread.Sleep(100); } return i; } }
注意
-
Class1
不需要任何Form1
知识。 - 由于
Class1.MyMethod
是静态的,因此不需要它的实例。 如果要在Class1中修改字段/属性,则需要一个实例。 如果这是正确的,那真的取决于你。 -
IProgress
需要.NET Framework 4.5
问题是您只将最后一个值传递给GUI。 你可以做的是将你想要用于显示的文本框传递给你的计数方法MyMethod
。 在那里你可以分配值。 您需要做的最后一件事是告诉应用程序使用Application.DoEvents();
更新它的事件Application.DoEvents();
所以你的方法看起来像这样:
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; }
别忘了包括:
using System.Threading.Tasks; using System.Windows.Forms;
你在Class1.cs
Form1
的调用如下所示:
private void button1_Click(object sender, EventArgs e) { _enemy = new Class1(this); int y = Class1.MyMethod(textBox1); }
免责声明:@Default指出应该避免使用Application.DoEvents() 。 因此,另一种方法可能是优选的方法是使用计时器。 它有一个Tick
事件,可以像你的for循环一样工作。 这个是System.Windows.Forms.Timer
。 您可以在Form1
类中使用它:
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 } }
编辑
好吧,让事情变得更复杂但彻底。 您询问:
即在其他地方调用方法并在其他地方打印。 在其他地方我指的是另一个class级
如果你想在其他地方打印它会在其他地方;)我的意思是图形用户界面的责任是显示东西。 所以它应该继续展示。 你的方法的责任是计算,所以它应该继续计算。 要在C#中结合这两个职责, 事件的概念是一个强大的概念。 它允许您发送事件和传输数据。
您需要的第一件事是在Class1
发出计数信号的事件:它有2个部分。 一个委托,它定义在触发事件时将调用的方法的结构,以及可以在另一个类中注册的委托类型的事件。 在你的情况下Form1
。
public class Class1 { public delegate void Counter(int c); // this delegate allows you to transmit an integer public event Counter CountEvent; public Class1() { }
我从Class1
删除了Form1 _form
的实例。 因为你不需要它来执行任务。 这也使您的Class1
独立于GUI的实现。 (如果您明天决定更改TextBox
的名称或选择Label
来显示计数器,则Class1
中将不会进行任何更改,仅在Form1
!)现在您可以在Form1
中注册/订阅该事件创建事件触发时将调用的事件处理程序:
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(); } }
由于我们不希望GUI在计数时冻结,因此我们将使用System.Threading.Thread在后台计数并通过事件传输数据。 现在这会导致问题,因为textBox1
是由主线程创建的,如果你试图通过另一个线程访问它,它将崩溃。 因此,您需要使用BeginInvoke方法来显示通过事件传输的计数变量。
唯一剩下的就是实现计数方法。 如您所见,我删除了static
关键字。 因为这样就必须将事件声明为static
,这意味着它只存在一次。 如果您尝试从第二个类订阅此事件,这将导致困难。
不是我们把你的循环放在一个线程中让线程运行。 在每次迭代时,它将触发事件并传输您的计数数据:
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(); }
开始这个方法是最简单的部分。 只需指定间隔,开始和结束,并调用方法,就像调用普通方法一样:
private void button1_Click(object sender, EventArgs e) { MyCounterClass.MyCountMethod(300, 0, 10); }
Etvoilà,你有一个可以计算并指示计数进度的类。 它独立于图形用户界面。 它必须依赖于Form1
。 每个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); _enemy.LoopInteration += OnLoopInteration; _enemy.MyMethod(); _enemy.LoopInteration -= OnLoopInteration; } private void OnLoopInteration(object sender, LoopCounterArgs e) { textBox1.Text = Convert.ToString(e.Iteration); } } }
第二种forms:
namespace WindowsFormsApplication2 { public class Class1 { public event EventHandler 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; }
然后,处理自定义事件args的新类:
namespace WindowsFormsApplication2 { public class LoopCounterArgs : EventArgs { public int Iteration { get; set; } public LoopCounterArgs(int iteration) { Iteration = iteration; } } }
我没有测试过,所以可能包含一些错误,但应该在那里...
您可能想要重新考虑textBox1.Text语句,因为它可以快速工作,该值可能显示为20,而实际上它已经为您完成了所有迭代。