我试图在一个类中创建一个方法,并尝试通过单击按钮从另一个类(窗体)调用

时间:2017-02-20 13:20:28

标签: c# winforms 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);
        }
    }
}

这是我的第二堂课:

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并停止。

This is the design of the form

像计数器一样。我也尝试过使用Timer,但却无法做到。

编辑:

@Mong Zhu我已经交叉检查了代码,仍然得到了异常。

以下是完整的代码供您参考:

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;
    }
}

3 个答案:

答案 0 :(得分:3)

如果您要将某些对象的进度报告回表单,可以使用IProgress<T>界面。很好地解释了herehere,但要将其翻译为您给定的代码,它看起来像这样:

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;
    }
}

请注意

  • Class1不需要了解Form1
  • 由于Class1.MyMethod是静态的,因此您不需要它的实例。如果要修改Class1中的字段/属性,则需要一个实例。如果这是正确的话,这取决于你。
  • IProgress<T>需要.NET Framework 4.5

答案 1 :(得分:2)

问题是您只将最后一个值传递给GUI。您可以做的是将要用于显示的文本框传递给计数方法MyMethod。在那里你可以分配值。您需要做的最后一件事是告诉应用程序使用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);

}

免责声明:{@ 3}}正如@Default所指出的那样。 因此,另一种方法可能是优选的方法是使用计时器。它有一个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
    }
}

修改

好吧,让事情变得更复杂但彻底。你问:

  

即。在其他地方调用方法并在其他地方打印。在其他地方我指的是另一个班级

如果你想在其他地方打印它会在其他地方;)我的意思是图形用户界面的责任是显示东西。所以它应该继续显示。你的方法的责任是计算,所以它应该继续计算。要在C#中结合这两个职责,Application.DoEvents() should be avoided的概念是一个强大的概念。它允许您发送事件信号并传输数据。

您需要的第一件事是发出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()
    {
    }

我从Form1 _form删除了Class1的实例。因为你不需要它来执行任务。这也使您的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在计数时冻结,我们将使用events在后台计数并通过事件传输数据。现在这会导致问题,因为textBox1是由主线程创建的,如果你试图通过另一个线程访问它,它将崩溃。因此,您需要使用System.Threading.Thread方法来显示通过事件传输的计数变量。

唯一剩下的就是实现计数方法。如您所见,我删除了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。每个班级都在照顾自己的责任。 希望它有所帮助

答案 2 :(得分:1)

也许想想一个事件?

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);
        }
    }
}

第二种形式:

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;
}

然后,处理自定义事件的新类args:

namespace WindowsFormsApplication2
{
    public class LoopCounterArgs : EventArgs
    {
        public int Iteration { get; set; } 

        public LoopCounterArgs(int iteration)
        {
            Iteration = iteration;
        }
    }
}

我还没有对此进行测试,因此可能包含一些错误,但应该在那里...

您可能想要重新考虑textBox1.Text语句,因为它可以快速地工作,该值可能显示为20,而实际上它已经为您完成了所有迭代。