一直以來開發C# .Net表單程式, 比較容易搞混的觀念( 對於一路寫VB.Net的人來說啦~ )

那麼底下我們就來講解一下甚麼是委派與事件吧!

程式範例出自: Visual C# 2015 學習經典/松崗出版

作者做了些許的更改。

用途:

當你要呼叫Subroutine被呼叫Subroutine之間需要一個媒介時,委派就是很好用的方法

範例資源:

https://dotblogs.com.tw/joysdw12/2013/06/21/delegate-winfom?fbclid=IwAR1JCCfockFkVVn759EE9SDDVuaOEg1WC0NmT1XPk1y_7WWx_SdwfqpseAg


概念:

[委派] 白話說明:

工廠裡面廠長(有主控權) 將這個禮拜的任務單據, 送給底下課長(媒介) 去指派任務給基層的操作人員(參數)去操作機器。

必須要透過個有職位抬頭的人物, 去傳達這件事情, 這個事情包含了很多任務。

委派的宣告結構

以下講解的範例, 我們用國小國中數學常用的三角形, 長方形面積算法作為函式, 方法之內容。

  • [modifier] delegate [回傳型別] [委派類別] (回傳參數)
Public delegate double DArea(int d1, int d2);
  • delegate type 本身就是一種型別, 我們宣告了一個有委派性質+自定義名稱的函式類別
  • 重點, 我們丟進委派類別裡面, 相對應的參數資料形態也要一樣。

委派結構_對應其他方法

  • 方案內的檔案目錄架構

Demo 畫面


Program.cs 的程式碼

namespace DelegatedTest
{
    internal static class Program
    {
        /// <summary>
        /// 應用程式的主要進入點。
        /// </summary>
        [STAThread]
        static void Main()
        {

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());

        }

       public delegate double DArea(int d1, int d2);



        public static void GetArea(int x, int y, DArea tv)
        {
            double area = tv(x, y);

            Console.WriteLine("高:{0}, 底:{1}, 面積:{2} \n", x, y, area);
        }

    }
}

Form1.cs的程式碼


namespace DelegatedTest
{

    public partial class Form1 : Form
    {
        double TriArea; // 三角形的面積(可能有小數點)
        double RecTArea; // 長方形的面積(可能有小數點)

        public Form1()
        {
            InitializeComponent();
        }

        // 三角形算式
        public static double TriA(int H, int B)
        {
            return (H * B / 2);
        }

        // 長房形算式
        public static double RecA(int L, int W)
        {
            return (L * W);
        }


        private void caculate_bt_Click(object sender, EventArgs e)
        {
            // 判斷三角形的
            if (!string.IsNullOrEmpty(trianHiegh_textBox.Text) && !string.IsNullOrEmpty(trianBottom_textBox.Text))
            {// 2個 TextBox都有字串資料
                TriArea = TriA(Convert.ToInt16(trianHiegh_textBox.Text), Convert.ToInt16(trianBottom_textBox.Text));
                TriaArea_textBox.Text = Convert.ToString(TriArea);
            }
                else
            {
                MessageBox.Show("至少有一個 三角形的TextBox 沒有資料");

            }


            if (!string.IsNullOrEmpty(RectHiegh_textBox.Text) && !string.IsNullOrEmpty(RectWidth_textBox.Text) )
            {

                if (Convert.ToInt16((RectHiegh_textBox.Text))!= Convert.ToInt16((RectWidth_textBox.Text)))
                {
             RecTArea  = RecA(Convert.ToInt16((RectHiegh_textBox.Text)),Convert.ToInt16((RectWidth_textBox.Text)));
             RectanArea_textBox.Text = Convert.ToString(RecTArea);
                }
                else
                {
                    MessageBox.Show("至少有一個 長方形的TextBox 2邊輸入一樣的數字。");
                }

            }
            else
            {
                MessageBox.Show("至少有一個 長方形的TextBox 沒有資料");
            }
        }

      
        private void Form1_Load(object sender, EventArgs e)
        {

        }


    }
}

解釋

TriA(int1,int2)方法, RectA(int1,int2)方法,

對應委派類別函式 DArea(int1,int2)

參數資料形態是一樣的, 這樣才能確保委派過去的資料型態是一樣

TriA : 處理三角形面積算法。

RectA : 處理矩形面積算法。

<備註: >

從檔案總管的 Program.cs 引用裡面的 method
引用到 Windows Form 類別內做為使用.

底下連結有討論

1. static method 引用法
2. non-static 引用法

c# – accessing a function in the Program.cs that initializes my Form1 from my Form1 – Stack Overflow


C# 中的事件處理程序:

在C#中,事件處理程序通常使用 += 或是 -=運算子來訂閱事件,並使用 delegate 匿名方法來定義處理程序。以下第一個範例:

No.1 創建一個按鈕事件做解釋範例

定義了一個委託類型 EventHandlerDelegate,它將用於定義事件的類型:

// 定義一個委託,用於處理事件
public delegate void EventHandlerDelegate(object sender, EventArgs e);

接下來,我們創建了一個自定義的按鈕類別 MyButton,並且宣告了一個事件 ButtonClicked,並將 EventHandlerDelegate 用作事件類型:

//創建一個自定義的按鈕類別

public class MyButton
{
    // 宣告一個事件,使用上方的委派類型 作為我們的事件類型。
    public event EventHandlerDelegate ButtonClicked;


    // 模擬一個按鈕點擊的方法! 
    public void SimulateButtonClick()
    {
        Console.WriteLine("按鈕被點擊了!");
        
        // 檢查是否有註冊事件處理程序,然後調用它們
        if (ButtonClicked != null)
        {

            //包含事件資料之類別的基底類別,全部清空
            EventArgs e = EventArgs.Empty;
            ButtonClicked(this, e);

        }
    }
}

SimulateButtonClick() 方法中,我們模擬了按鈕的模擬點觸事件,然後使用ButtonClicked 事件來通知註冊的事件處理事件。

最後,在 Main 方法中,我們註冊了一個事件處理程序 Button_Clicked,當按鈕點擊事件發生時,這個處理程序將被調用。使用 Invoke 方法來觸發事件處理程序也是可能的,特別是當多個執行緒涉及時,它可以更安全地調用處理程序:

public class Program
{
    public static void Main()
    {
        MyButton button = new MyButton();

        // 註冊事件處理程序
        button.ButtonClicked += Button_Clicked;

        // 模擬按鈕點擊
        button.SimulateButtonClick();
    }

    // 事件處理程序
    public static void Button_Clicked(object sender, EventArgs e)
    {
        Console.WriteLine("事件處理程序被觸發!");
    }
}

No.2 創建一個加減乘_四則運算範例

https://www.jytek.tw/post/%E5%A7%94%E8%A8%97-delegate-%E5%92%8C%E4%BA%8B%E4%BB%B6-event?fbclid=IwAR2Yail_lC75Np82fM4BGZ_mIAgT8S2SezkaMJRqc6fwLWPKGtIzjLCw1Eo

這範例中,先定義了一個委託 Calculate,這個委託具有兩個整數參數和一個整數返回值。接著定義了一個事件 StartEvent,這個事件使用 Calculate 委託作為事件類型。

delegate int Calculate(int x, int y);
static event Calculate StartEvent;

這個部分定義了委託和事件,用於計算兩個整數之間的操作。

接下來,在 Main 方法中,我們定義了兩個整數變數 xy,它們的值分別為 1 和 2。然後,我們註冊了兩個事件處理程序,分別是 AddMultiply,這兩個處理程序都是將兩個整數進行不同的操作。

int x = 1;
int y = 2;
StartEvent += Add;
StartEvent += Multiply;

接著,我們使用 Invoke 方法觸發 StartEvent 事件,傳遞 xy 作為參數。這將導致兩個事件處理程序(AddMultiply)被呼叫,並分別執行加法和乘法操作。

StartEvent.Invoke(x, y);

最後,我們使用 Console.ReadKey() 來等待使用者的輸入,以確保控制台應用程序保持運行狀態。

using System;

delegate int CalculationOperation(int x, int y);
static event CalculationOperation CalculationRequested;

static void Main(string[] args)
{
    int operand1 = 1;
    int operand2 = 2;

    // 註冊事件處理程序
    CalculationRequested += Add;
    CalculationRequested += Multiply;

    // 觸發事件並獲取計算結果
    int result = Calculate(operand1, operand2);

    Console.WriteLine("計算結果: " + result);
    Console.ReadKey();
}

static int Add(int x, int y)
{
    return x + y;
}

static int Multiply(int x, int y)
{
    return x * y;
}

static int Calculate(int x, int y)
{
    if (CalculationRequested != null)
    {
        int result = 0;
        foreach (CalculationOperation operation in CalculationRequested.GetInvocationList())
        {
            result = operation(x, y);
        }
        return result;
    }
    return 0;
}