【问题标题】:I am trying to create a method in one class and trying to call from another class (form) on click of a button我正在尝试在一个类中创建一个方法,并尝试通过单击按钮从另一个类(表单)调用
【发布时间】:2017-02-21 20:50:35
【问题描述】:

这是我的第一堂课:

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 我已经交叉检查了代码,仍然得到异常。

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

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吗?
  • 不知道是什么?你能详细说明一下吗?
  • 它基本上是一个合同,它说生产者(你的表单)想知道消费者(class1)在做什么。它也被描述为herehere。这不完全是您的问题标题所说的,但阅读您的问题似乎表明了这一点。
  • @Default 你能帮帮我吗? (带代码)。我需要的是 class1 中的一个逻辑,它将为计数器创建一个循环。我需要通过单击文本框内的按钮在 form1 中调用该 class1。但它可能不一定是一个文本框,它可能是一个进度条或其他任何东西。基本上我不想从 class1 转移任何 GUI。
  • 你检查了链接吗?我的建议在这两个链接中都有详细的解释。如果那不能回答您的问题,那么不,我无法帮助您编写代码。如果那是您正在寻找的东西,那么可以肯定,我可以添加一个答案。但请先阅读链接!

标签: c# winforms class


【解决方案1】:

如果您希望将某个对象的进度报告回您的表单,您可以改用IProgress&lt;T&gt; 接口。 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&lt;T&gt; 需要 .NET Framework 4.5

【讨论】:

    【解决方案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);
    
    }
    

    免责声明:@Default 指出的Application.DoEvents() should be avoided。 因此,另一种方法可能是更可取的方法是使用计时器。它有一个 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# 中结合这两个职责,events 的概念是一个强大的概念。它允许您发送事件信号和传输数据。

    您需要的第一件事是在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方法来显示通过事件传递的计数变量。

    剩下的就是实现counting方法了。如您所见,我删除了 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);
    }
    

    等等,你有一个可以计数并指示计数进度的类。它独立于图形用户界面。它必须依赖于Form1。每个班级都在履行自己的职责。 希望对你有帮助

    【讨论】:

    • @Default 我已合并您的评论。谢谢你的评论
    • 感谢@Mong Zhu 的回复。我非常感谢您在这里解释的方式。
    • Mong Zhu 所以第一种方法按预期工作。即一个类中的方法和另一个类中的调用。但正如你所说,我们不应该使用它。现在,在您喜欢使用的第二种方法中,我的查询没有按预期进行,因为所有事情都发生在同一个班级中。您能否付出一些努力并建议我如何使用我预期的方法来实现相同的目标。即在其他地方调用方法并在其他地方打印。在其他地方我的意思是另一个类。 @梦珠
    • @mehtaankit 我付出了一些努力并进行了编辑。最后一个选项基本上是如何使用线程和事件处理数据并将其显示在 WinForms GUI 上的高级版本。如果我的回答对您有所帮助,那么您可以考虑通过单击灰色复选标记使其变为绿色来接受它;)
    • 按钮点击的代码在哪里?我的意思是计数器应该在单击按钮时起作用?
    【解决方案3】:

    也许考虑一个事件?

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

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

    namespace WindowsFormsApplication2
    {
        public class LoopCounterArgs : EventArgs
        {
            public int Iteration { get; set; } 
    
            public LoopCounterArgs(int iteration)
            {
                Iteration = iteration;
            }
        }
    }
    

    我没有对此进行测试,因此可能包含一些错误,但应该差不多...

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

    【讨论】:

    • 感谢您宝贵的时间和回复。不幸的是,我在这行代码中遇到错误: LoopInteration?.Invoke(this, new LoopCounterArgs(j));显示一个语法错误,说 expeted ':'
    • Remove: // 不要用 new Form1() 初始化它;私人Form1 _form1; && _form1 = 表格;我刚刚创建了一个删除行的 WIndows 应用程序,它可以工作 - 这是一个示例:1drv.ms/u/s!AjKkamHEU-ahibRrvB9-r2xaLfW1Bg
    猜你喜欢
    • 1970-01-01
    • 2019-06-18
    • 2013-10-13
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多