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