2017-02-20 4 views
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); 
      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; 
} 

Программа работает правильно, и я получаю только 20 в качестве вывода в TextBox. То, что я хочу, это выход каждый раз, когда цикл работает.

Нравится 1,2,3,.........20 и остановиться.

This is the design of the form

как счетчик, может быть. Я также пробовал использовать Timer, но не мог этого сделать.

EDIT:

@Mong Чжу У меня есть крест проверил код, все еще получаю исключение.

Для справки здесь являются полные коды:

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

Вы ищете [IProgress] (https://blogs.msdn.microsoft.com/dotnet/2012/06/06/async-in-4-5- дающая возможность-прогресс-и-аннулирование-в-асинхронного-APIs /)? – Default

+0

Я не знаю, что это? Можете ли вы уточнить? –

+0

В основном это контракт, в котором говорится, что производитель (ваша форма) хочет знать, что делает потребитель (класс1). Это также описано [здесь] (http://simplygenius.net/Article/AncillaryAsyncProgress) и [здесь] (http://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html) , Это не совсем то, что говорит ваш вопрос, но чтение вашего вопроса, похоже, указывает на это. – Default

ответ

3

Если вы хотите сообщить о прогрессе с какого-либо объекта обратно в форму, вы можете использовать интерфейс IProgress<T>. Это хорошо объясняется here и here, но перевести его в данном коде, это будет выглядеть примерно так:

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

} 

Отказ от ответственности: Application.DoEvents() should be avoided как указано @Default. Поэтому другой подход и, вероятно, предпочтительный вариант - использовать таймер. У него есть событие Tick, которое может работать как ваш for-loop. Это 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 
    } 
} 

EDIT

Ok позволяет сделать вещи немного более сложным, но тщательно. Вы спрашивали:

i.e вызов метода в другом месте и печать в другом месте.В другом месте я имею в виду другой класс

Если вы хотите распечатать его в другом месте, это будет где-то еще;) Я имею в виду, что ответственность за графический интерфейс пользователя заключается в том, чтобы отображать вещи. Поэтому он должен оставаться на экране. Ответственность за ваш метод заключается в подсчете, поэтому он должен продолжать подсчитывать все. Чтобы объединить эти две обязанности в 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() 
    { 
    } 

я удалил экземпляр Form1 _form из Class1. Потому что вам не нужна эта задача. Также это делает ваш Class1 независимым от реализации графического интерфейса. (Если вы решили завтра изменить имя TextBox или выбрать Label, чтобы отобразить счетчик, в Class1 изменений не будет, только в Form1!) Теперь вы можете зарегистрироваться/подписаться на мероприятие в Form1 и создать EventHandler, который будет вызываться, когда событие вызывается:

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

Поскольку мы не хотим, графический интерфейс для замораживания, когда он рассчитывает, мы будем использовать System.Threading.Thread рассчитывать в фон и передавать данные через событие. Теперь это приведет к проблемам, потому чтосоздается основным потоком, и если вы попытаетесь получить доступ к нему через другой, он сработает. Поэтому вам нужно использовать метод 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); 
} 

вуаля, у вас есть класс, который может рассчитывать и сигнализировать ход подсчета. И он не зависит от графического интерфейса пользователя. Он имеет зависимости от Form1. И каждый класс по-прежнему заботится о своих собственных обязанностях. Надеюсь, это поможет

+1

@Default Я включил ваш комментарий. Спасибо за комментарий –

+0

Спасибо @Mong Zhu за ваш ответ. Я очень ценю то, как вы объяснили это здесь. –

+0

[Mong Zhu] (http://stackoverflow.com/users/5174469/mong-zhu) Итак, первый метод работает так, как ожидалось. то есть метод в одном классе и вызов другого. Но, как вы сказали, мы не должны его использовать. Теперь во втором методе, который вы предпочитаете использовать, у меня нет моего запроса, как ожидалось, потому что все происходит в одном классе. Можете ли вы приложить некоторые усилия и предложить мне, как мы можем достичь того же, используя мой ожидаемый метод. т. е. вызвать метод где-то еще и распечатать где-то еще. В другом месте я имею в виду другой класс. @MongZhu –

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

затем, новый класс для обработки арг пользовательских событий:

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

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

Я не проверял это так, может содержать некоторые ошибки, но они должны быть в значительной степени там ...

Возможно, вы захотите пересмотреть инструкцию textBox1.Text, поскольку она будет работать так быстро, значение может появиться как 20, когда на самом деле он выполнил все итерации для вас.

+0

Благодарим вас за ценное время и ответ. К сожалению, я получаю ошибку в этой строке кода: LoopInteration? .Invoke (это новый LoopCounterArgs (j)); Здесь показана синтаксическая ошибка с выражением expeted ':' –

+0

Remove: // НЕ инициализируем это новым Form1(); частный Form1 _form1; && _form1 = form; Я только что создал приложение WIndows с удаленными линиями, и он работает. Вот пример: https://1drv.ms/u/s!AjKkamHEU-ahibRrvB9-r2xaLfW1Bg – Scott

Смежные вопросы