sự kiện tùy chỉnh đơn giản


75

Tôi đang cố gắng tìm hiểu các sự kiện tùy chỉnh và tôi đã cố gắng tạo một sự kiện nhưng có vẻ như tôi gặp sự cố

Tôi đã tạo một Biểu mẫu, lớp tĩnh và sự kiện tùy chỉnh. Những gì tôi đang cố gắng đạt được là khi tôi nhấn nút Biểu mẫu sẽ gọi hàm lớp tĩnh và sau đó func thỉnh thoảng sẽ xuất hiện một sự kiện để báo cáo trạng thái hiện tại. Form1 sẽ lắng nghe nếu sự kiện được nêu ra và nếu có, nó sẽ thay đổi Văn bản của nhãn1

Đây là những gì tôi có cho đến nay

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

 }

Tệp 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;
    }
}

Bây giờ điều tôi không hiểu là, làm cách nào tôi có thể tăng một sự kiện từ TestClass để Form1 có thể xử lý sự kiện và thay đổi nhãn.


TestClass của bạn sẽ phải cung cấp một sự kiện và Biểu mẫu sẽ phải đăng ký sự kiện đó.
Henk Holterman


vâng, đó là tốt nhưng tôi không hiểu làm thế nào tôi có thể tăng lên một sự kiện từ một lớp khác
Bill

1
@Bill, bạn không thể trực tiếp làm điều đó. Đó là do thiết kế. Nếu bạn thực sự muốn, bạn có thể tạo một phương pháp công khai RaiseProgress()để nâng cao sự kiện, nhưng tôi không chắc đó là một ý tưởng hay.
svick

Câu trả lời:


138

Đây là một cách dễ dàng để tạo các sự kiện tùy chỉnh và nâng cao chúng. Bạn tạo một đại biểu và một sự kiện trong lớp mà bạn sắp xếp. Sau đó, đăng ký sự kiện từ một phần khác của mã của bạn. Bạn đã có một lớp đối số sự kiện tùy chỉnh để bạn có thể xây dựng dựa trên đó để tạo các lớp đối số sự kiện khác. NB: Tôi chưa biên dịch mã này.

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
Một điểm quan trọng khác đối với các sự kiện được nói về liên kết . Để hủy đăng ký sự kiện, nó là - = thay vì + =. Ví dụ _testClass.OnUpdateStatus - = UpdateStatus; Đặt mã hủy đăng ký ở đâu là một câu hỏi khác, nhưng có những câu hỏi về tràn ngăn xếp khác giải quyết vấn đề đó.
themartinmcfly,

1
Một sự lựa chọn tên kém cỏi. Cả hai lớp đều có một UpdateStatushàm có thể gây nhầm lẫn. Tôi muốn gọi hàm Form1bằng một tên khác ProcessUpdateStatusEventcho rõ ràng. Nếu không giải thích rất tốt.
Bogdan Alexandru

Thuộc tính chỉ đọc trên ProgressEventArgs không biên dịch. Loại bỏ thuộc tính này và điều này hoạt động tốt.
Loren Shaw

Mã này khó hiểu hơn mức cần thiết và không an toàn cho chuỗi. Hãy xem giải thích này codeblog.jonskeet.uk/2015/01/30/… và câu trả lời từ @Volomike.
Xan-Kun Clark-Davis

20

Bạn chưa tạo sự kiện. Để làm điều đó, hãy viết:

public event EventHandler<Progress> Progress;

Sau đó, bạn có thể gọi Progresstừ bên trong lớp nơi nó được khai báo như hàm hoặc đại biểu bình thường:

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

Vì vậy, nếu bạn muốn báo cáo tiến độ TestClass, sự kiện cũng phải ở trong đó và nó cũng phải tĩnh. Bạn có thể đăng ký nó từ biểu mẫu của bạn như sau:

TestClass.Progress += SetStatus;

Ngoài ra, bạn có thể nên đổi tên Progressthành ProgressEventArgs, để rõ ràng nó là gì.


15

Các sự kiện khá dễ dàng trong C #, nhưng các tài liệu MSDN theo ý kiến ​​của tôi khiến chúng khá khó hiểu. Thông thường, hầu hết các tài liệu bạn thấy đều thảo luận về việc tạo một lớp kế thừa từ EventArgslớp cơ sở và có lý do cho điều đó. Tuy nhiên, đây không phải là cách đơn giản nhất để tạo sự kiện và đối với ai đó muốn thứ gì đó nhanh chóng, dễ dàng và trong thời gian ngắn, sử dụngAction loại hình chính là tấm vé của bạn.

Tạo sự kiện & đăng ký tham gia

1. Tạo sự kiện của bạn trên lớp của bạn ngay sau khi classkhai báo.

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

2. Tạo phương thức lớp xử lý sự kiện trong lớp của bạn.

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

3. Bây giờ khi lớp của bạn được gọi, hãy yêu cầu nó kết nối sự kiện với trình xử lý sự kiện mới của bạn. Lý do +=toán tử được sử dụng là vì bạn đang thêm trình xử lý sự kiện cụ thể của mình vào sự kiện. Bạn thực sự có thể làm điều này với nhiều trình xử lý sự kiện riêng biệt và khi một sự kiện được đưa ra, mỗi trình xử lý sự kiện sẽ hoạt động theo trình tự mà bạn đã thêm chúng.

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

4. Bây giờ, khi bạn đã sẵn sàng, hãy kích hoạt (hay còn gọi là tăng) sự kiện ở đâu đó trong mã lớp của bạn như sau:

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

Kết quả cuối cùng khi bạn chạy nó là bàn điều khiển sẽ phát ra "wow this is cool". Và nếu bạn đã thay đổi "mát mẻ" bằng một ngày hoặc một chuỗi và chạy trình kích hoạt sự kiện này nhiều lần, bạn sẽ thấy kết quả xuất hiện trong một chuỗi FIFO giống như các sự kiện thường hoạt động.

Trong ví dụ này, tôi đã truyền 4 chuỗi. Nhưng bạn có thể thay đổi chúng thành bất kỳ loại nào có thể chấp nhận được hoặc sử dụng nhiều hơn hoặc ít hơn các loại hoặc thậm chí xóa<...> và không chuyển gì cho trình xử lý sự kiện của bạn.

Và, một lần nữa, nếu bạn có nhiều trình xử lý sự kiện tùy chỉnh và đăng ký tất cả chúng vào sự kiện của bạn với += toán tử, thì trình kích hoạt sự kiện của bạn sẽ gọi tất cả chúng theo trình tự.

Xác định Người gọi Sự kiện

Nhưng nếu bạn muốn xác định người gọi sự kiện này trong trình xử lý sự kiện của mình thì sao? Điều này hữu ích nếu bạn muốn một trình xử lý sự kiện phản ứng với các điều kiện dựa trên người đã nêu ra / kích hoạt sự kiện. Có một số cách để làm điều này. Dưới đây là các ví dụ được hiển thị theo thứ tự tốc độ hoạt động của chúng:

Tùy chọn 1. (Nhanh nhất) Nếu bạn đã biết nó, hãy chuyển tên dưới dạng chuỗi ký tự cho trình xử lý sự kiện khi bạn kích hoạt nó.

Tùy chọn 2. (Hơi nhanh) Thêm chuỗi này vào lớp của bạn và gọi nó từ phương thức gọi, sau đó chuyển chuỗi đó đến trình xử lý sự kiện khi bạn kích hoạt nó:

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

Tùy chọn 3. (Ít nhanh nhất nhưng vẫn nhanh) Trong trình xử lý sự kiện của bạn khi bạn kích hoạt nó, hãy lấy chuỗi tên phương thức gọi với sau:

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

Hủy đăng ký khỏi sự kiện

Bạn có thể có một tình huống trong đó sự kiện tùy chỉnh của bạn có nhiều trình xử lý sự kiện, nhưng bạn muốn xóa một trình xử lý đặc biệt ra khỏi danh sách trình xử lý sự kiện. Để làm như vậy, hãy sử dụng -=toán tử như sau:

MyEvent -= MyEventHandler;

Tuy nhiên, một lời cảnh báo nhỏ với điều này. Nếu bạn làm điều này và sự kiện đó không còn bất kỳ trình xử lý sự kiện nào nữa và bạn kích hoạt lại sự kiện đó, sự kiện đó sẽ đưa ra một ngoại lệ. (Tất nhiên là có ngoại lệ, bạn có thể mắc bẫy bằng các khối thử / bắt.)

Xóa tất cả các sự kiện

Được rồi, giả sử bạn đã vượt qua các sự kiện và bạn không muốn xử lý thêm nữa. Chỉ cần đặt nó thành null như vậy:

MyEvent = null;

Thận trọng tương tự đối với các sự kiện Hủy đăng ký cũng ở đây. Nếu trình xử lý sự kiện tùy chỉnh của bạn không còn bất kỳ sự kiện nào nữa và bạn kích hoạt lại, chương trình của bạn sẽ có một ngoại lệ.


1
Là một người đang tìm kiếm giải pháp "nhanh chóng và dễ dàng", câu trả lời này là những gì tôi hy vọng tìm thấy.
Arthur Hebert

9

Giống như đã được đề cập đến trường tiến độ cần sự kiện từ khóa

public event EventHandler<Progress> progress;

Nhưng tôi không nghĩ đó là nơi bạn thực sự muốn sự kiện của mình. Tôi nghĩ rằng bạn thực sự muốn sự kiện trong TestClass. Hình sau như thế nào? (Tôi chưa bao giờ thực sự thử thiết lập các sự kiện tĩnh vì vậy tôi không chắc liệu phần sau có biên dịch được hay không, nhưng tôi nghĩ điều này cho bạn ý tưởng về mẫu mà bạn nên hướng tới.)

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.