Простое пользовательское событие

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

Я создал форму, статический класс и настраиваемое событие. То, что я пытаюсь достичь, - это когда я нажимаю кнопку Form, вызывается функция статического класса, а затем func будет время от времени повышать событие, чтобы сообщить текущий статус. Form1 будет слушать, если событие поднято, и если оно есть, оно изменит текст метки1

Вот что я до сих пор

public partial class Form1 : Form
{
    public EventHandler<Progress> progress; 

    public Form1()
    {
        InitializeComponent();
        progress += SetStatus;
    }

    private void SetStatus(object sender, Progress e)
    {
        label1.Text = e.Status;
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
         TestClass.Func();
    }

 }

Файл 2

class TestClass
{
    public static void Func()
    {
        //time consuming code
        Report status 
        // time consuming code
        report status
    }
}

public class Progress : EventArgs
{
    public string Status { get; private set; }

    private Progress() {}

    public Progress(string status)
    {
        Status = status;
    }
}

Теперь я не понимаю, как я могу создать событие из TestClass, чтобы Form1 мог обрабатывать событие и изменять метку. Text

Ответ 1

Это простой способ создать пользовательские события и поднять их. Вы создаете делегат и событие в классе, из которого вы бросаете. Затем подпишитесь на событие из другой части вашего кода. У вас уже есть собственный класс аргументов аргументов, поэтому вы можете использовать его для создания других классов аргументов аргументов. N.B: Я не скомпилировал этот код.

public partial class Form1 : Form
{
    private TestClass _testClass;
    public Form1()
    {
        InitializeComponent();
        _testClass = new TestClass();
        _testClass.OnUpdateStatus += new TestClass.StatusUpdateHandler(UpdateStatus);
    }

    private void UpdateStatus(object sender, ProgressEventArgs e)
    {
        SetStatus(e.Status);
    }

    private void SetStatus(string status)
    {
        label1.Text = status;
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
         TestClass.Func();
    }

}

public class TestClass
{
    public delegate void StatusUpdateHandler(object sender, ProgressEventArgs e);
    public event StatusUpdateHandler OnUpdateStatus;

    public static void Func()
    {
        //time consuming code
        UpdateStatus(status);
        // time consuming code
        UpdateStatus(status);
    }

    private void UpdateStatus(string status)
    {
        // Make sure someone is listening to event
        if (OnUpdateStatus == null) return;

        ProgressEventArgs args = new ProgressEventArgs(status);
        OnUpdateStatus(this, args);
    }
}

public class ProgressEventArgs : EventArgs
{
    public string Status { get; private set; }

    public ProgressEventArgs(string status)
    {
        Status = status;
    }
}

Ответ 2

Вы не создали событие. Для этого напишите:

public event EventHandler<Progress> Progress;

Затем вы можете вызвать Progress из класса, где он был объявлен как нормальная функция или делегировать:

Progress(this, new Progress("some status"));

Итак, если вы хотите сообщить о прогрессе в TestClass, событие должно быть там тоже, и оно также должно быть статическим. Вы можете подписаться на него в своей форме:

TestClass.Progress += SetStatus;

Кроме того, вы должны, вероятно, переименовать Progress в ProgressEventArgs, чтобы он понял, что это такое.

Ответ 3

Как уже упоминалось, в поле прогресса требуется событие ключевого слова

public event EventHandler<Progress> progress;

Но я не думаю, что там, где вы действительно хотите свое событие. Я думаю, что вы действительно хотите событие в TestClass. Как выглядит следующее? (Я никогда не пытался настроить статические события, поэтому я не уверен, что следующее будет компилироваться или нет, но я думаю, что это дает вам представление о шаблоне, к которому вы должны стремиться.)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        TestClass.progress += SetStatus;
    }

    private void SetStatus(object sender, Progress e)
    {
        label1.Text = e.Status;
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
         TestClass.Func();
    }

 }

public class TestClass
{
    public static event EventHandler<Progress> progress; 

    public static void Func()
    {
        //time consuming code
        OnProgress(new Progress("current status"));
        // time consuming code
        OnProgress(new Progress("some new status"));            
    }

    private static void OnProgress(EventArgs e) 
    {
       if (progress != null)
          progress(this, e);
    }
}


public class Progress : EventArgs
{
    public string Status { get; private set; }

    private Progress() {}

    public Progress(string status)
    {
        Status = status;
    }
}

Ответ 4

В С# события довольно просты, но документы MSDN, на мой взгляд, делают их довольно запутанными. Обычно в большинстве документации вы видите, что класс наследует базовый класс EventArgs и там причина для что. Тем не менее, это не самый простой способ совершить события, а для кого-то, желающего чего-то быстрого и легкого, и во время хруста, использование типа Action - это ваш билет.

Создание событий и подписка на них

1. Создайте свое событие в своем классе сразу после объявления class.

public event Action<string,string,string,string>MyEvent;

2. Создайте метод класса обработчика событий в своем классе.

private void MyEventHandler(string s1,string s2,string s3,string s4)
{
  Console.WriteLine("{0} {1} {2} {3}",s1,s2,s3,s4);
}

3. Теперь, когда ваш класс вызывается, сообщите ему, чтобы связать событие с вашим новым обработчиком событий. Причина, по которой используется оператор +=, заключается в том, что вы добавляете свой обработчик события к событию. Вы можете сделать это с помощью нескольких отдельных обработчиков событий, и когда событие будет поднято, каждый обработчик событий будет работать в последовательности, в которой вы их добавили.

class Example
{
  public Example() // I'm a C# style class constructor
  {
    MyEvent += new Action<string,string,string,string>(MyEventHandler);
  }
}

4. Теперь, когда вы будете готовы, триггер (ака поднять) событие где-то в вашем коде класса так:

MyEvent("wow","this","is","cool");

Конечный результат при запуске - это то, что консоль выдаст "ничего себе, это круто". И если вы изменили "круто" с датой или последовательностью и несколько раз запускали этот триггер событий, вы увидите, что результат выдается в последовательности FIFO, как обычно должны работать события.

В этом примере я передал 4 строки. Но вы можете изменить их на любой приемлемый тип или использовать более или менее типы или даже удалить <...> и ничего не передавать своему обработчику событий.

И снова, если у вас было несколько настраиваемых обработчиков событий и подписаны все они на ваше событие с помощью оператора +=, тогда триггер события вызвал бы их все в последовательности.

Идентификация вызывающих абонентов

Но что, если вы хотите идентифицировать вызывающего абонента для этого события в обработчике событий? Это полезно, если вы хотите, чтобы обработчик события реагировал на условия, основанные на том, кто поднял/вызвал событие. Есть несколько способов сделать это. Ниже приведены примеры, которые показываются в порядке их быстродействия:

Вариант 1. (Самый быстрый) Если вы уже знаете это, передайте имя как литеральную строку обработчику события при его запуске.

Вариант 2. (Немного быстро) Добавьте это в свой класс и вызовите его из вызывающего метода, а затем передайте эту строку обработчику события при его запуске:

private static string GetCaller([System.Runtime.CompilerServices.CallerMemberName] string s = null) => s;

Вариант 3. (наименее быстрый, но все же быстрый). В обработчике события, когда вы его вызываете, получите строку имени вызывающего метода с этим:

string callingMethod = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Name.Split('<', '>')[1];

Отменить подписку на события

У вас может быть сценарий, когда ваше пользовательское событие имеет несколько обработчиков событий, но вы хотите удалить один специальный из списка обработчиков событий. Для этого используйте оператор -= следующим образом:

MyEvent -= MyEventHandler;
Тем не менее, слово с небольшой осторожностью. Если вы делаете это, и это событие больше не имеет обработчиков событий, и вы снова запускаете это событие, оно выдает исключение. (Исключения, конечно, вы можете ловить ловушку с помощью блоков try/catch.)

Очистка всех событий

Хорошо, позвольте сказать, что вы прошли через события, и вы больше не хотите обрабатывать. Просто установите для него нулевое значение:

MyEvent = null;

То же предостережение для Unsubscribing events также присутствует здесь. Если ваш пользовательский обработчик событий больше не имеет каких-либо событий, и вы запускаете его снова, ваша программа генерирует исключение.