проста нестандартна подія


75

Я намагаюся вивчити власні події, і намагався створити, але, здається, у мене проблема

Я створив форму, статичний клас та спеціальну подію. Я намагаюся досягти, коли натискаю кнопку Form, викликатиме функцію статичного класу, а потім func час від часу піднімає подію, щоб повідомити про поточний стан. Form1 прослухає, якщо подія порушена, і якщо вона відбудеться, вона змінить текст label1

Ось те, що я маю на сьогодні

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 міг обробляти події та змінювати мітку.


Ваш TestClass повинен надати подію, а Форма - на неї підписатися.
Henk Holterman


так, це добре, але я не розумію, як я можу підняти подію з іншого класу
Білл

1
@Bill, ти не можеш цього зробити безпосередньо. Це за задумом. Якщо ви дійсно хочете, ви можете створити загальнодоступний метод, RaiseProgress()який піднімає подію, але я не впевнений, що це гарна ідея.
svick

Відповіді:


138

Це простий спосіб створити власні події та підняти їх. Ви створюєте делегата та подію в класі, з якого ви кидаєте. Потім підпишіться на подію з іншої частини вашого коду. Ви вже отримали власний клас аргументу події, тому ви можете використовувати його для створення інших класів аргументів подій. NB: Я не скомпілював цей код.

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

3
Про інший важливий момент подій йдеться у посиланні . Щоб скасувати підписку на події, це - = замість + =. Наприклад _testClass.OnUpdateStatus - = UpdateStatus; Де розмістити код відписки - інше питання, але є й інші запитання щодо переповнення стека, які вирішують це.
themartinmcfly

1
Невдалий вибір імен. Обидва класи мають UpdateStatusфункцію, яка може заплутати. Я волів би викликати функцію Form1з іншим іменем, як ProcessUpdateStatusEventдля ясності. Інакше дуже гарне пояснення.
Богдан Александру

Атрибут readonly на ProgressEventArgs не компілюється. Видалення цього атрибуту, і це працює чудово.
Loren Shaw

Цей код є більш заплутаним, ніж потрібно, і не захищений від потоків. Перегляньте це пояснення codeblog.jonskeet.uk/2015/01/30/… та відповідь від @Volomike.
Xan-Kun Clark-Davis

20

Ви не створили подію. Для цього напишіть:

public event EventHandler<Progress> Progress;

Потім ви можете зателефонувати Progressз класу, де він був оголошений як нормальна функція або делегат:

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

Отже, якщо ви хочете повідомити про прогрес TestClass, подія також повинна бути там, і вона також повинна бути статичною. Ви можете передплатити його у своїй формі так:

TestClass.Progress += SetStatus;

Крім того, вам слід перейменувати Progressна ProgressEventArgs, щоб було зрозуміло, що це таке.


15

Події в C # досить легкі, але, на мій погляд, документи 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 рядки. Але ви можете змінити їх на будь-який прийнятний тип, або використати більш-менш типи, або навіть видалити <...>out і нічого не передати своєму обробнику подій.

І знову ж таки, якби у вас було кілька власних обробників подій і ви підписали їх на свою подію разом з +=оператором, тоді ваш тригер подій викликав би їх усіх послідовно.

Визначення тих, хто викликає події

Але що, якщо ви хочете ідентифікувати того, хто викликає цю подію, у своєму обробнику подій? Це корисно, якщо вам потрібен обробник події, який реагує на умови, засновані на тому, хто викликав / ініціював подію. Є кілька способів зробити це. Нижче наведено приклади, які показані в порядку їх швидкості роботи:

Варіант 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;

Така сама обережність щодо подій, що відмовляються від підписки, є і тут. Якщо у вашому користувальницькому обробнику подій більше немає жодних подій, і ви активуєте його знову, ваша програма видасть виняток.


1
Як хтось, хто шукає "швидкого і простого" рішення, цю відповідь я сподівався знайти.
Артур Геберт

9

Як уже зазначалося, поле прогресу потребує ключового слова подія

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.