1. 程式人生 > >C#委託和事件(WPF實現關閉子視窗B時觸發A視窗事件)

C#委託和事件(WPF實現關閉子視窗B時觸發A視窗事件)

     遇到一個問題,子視窗關閉時,主視窗如何知道子視窗關閉了,並執行相應的處理事件。為了解決這個問題查閱資料後可以用委託和事件來完成。

一、委託

1、委託:

       委託是安全封裝方法的型別,類似於 C 和 C++ 中的函式指標。 與 C 函式指標不同的是,委託是面向物件的、型別安全的和可靠的。假如委託沒有引用一個有效的方法,就不允許呼叫這個委託。

       委託是指向一個方法的指標,而且我們採用和呼叫方法一樣的方式呼叫它。呼叫一個位委託時,執行時實際執行的是委託所引用的方法。可以動態的更改一個委託引用的方法,使呼叫一個委託的程式碼每次都執行一個不同的方法。

      委託(Delegate)特別用於實現事件和回撥方法。所有的委託(Delegate)都派生自 System.Delegate 類。

       委託用於將方法作為引數傳遞給其他方法。 事件處理程式就是通過委託呼叫的方法。 你可以建立一個自定義方法,當發生特定事件時,某個類(如 Windows 控制元件)就可以呼叫你的方法。

        委託具有以下屬性:

  • 委託類似於 C++ 函式指標,但它們是型別安全的。

  • 委託允許將方法作為引數進行傳遞。

  • 委託可用於定義回撥方法。

  • 委託可以連結在一起;例如,可以對一個事件呼叫多個方法。

2、使用委託的步驟

     

第一步:定義委託

與類一樣,委託型別必須在被用來建立變數以及型別物件之前宣告。

委託的宣告原型是 
delegate <函式返回型別> <委託名> (<函式引數>)

public delegate void MyDelegate ( );

第二步:委託的例項化,並將這個例項引用一個相匹配的方法

方法1:使用new關鍵字

<委託型別> <例項化名>=new <委託型別>(<註冊函式>)

MyDelegate myIN = new MyDelegate(WriteToScreen);//例項化委託

pubic void WriteToScreen()
{
}

方法2:使用 += 號將例項引用一個相匹配的方法。

class Myclass
{
   public delegate void MyDelegate (); 
   MyDelegate  void myIN;//建立一個委託的例項

   public Myclass()
    {
      this.myIN +=run;
    }

    Private void run()
    {
     
    }
}

第三步:呼叫委託

完整步驟舉例:

4、委託的作用

        委託時一種在C#中實現函式動態呼叫的方式,通過委託可以將一些相同型別的函式串聯起來依次執行。委託同時還是函式回撥事件機制的基礎。

        例項化委託是將委託指向或引用某個方法,也就是必須要將某一個方法作為引數傳遞委託的構造方法

二、事件

1、 事件(Event) 基本上說是一個使用者操作,如按鍵、點選、滑鼠移動等等,或者是一些出現,如系統生成的通知。應用程式需要在事件發生時響應事件。例如,中斷。事件是用於程序間通訊。

        .NET中的事件,我們可以定義並捕捉特定的事件,並安排呼叫委託來處理髮生的事件。

2、事件在類中宣告且生成,且通過使用同一個類或其他類中的委託與事件處理程式關聯。包含事件的類用於釋出事件。這被稱為 釋出器(publisher) 類。其他接受該事件的類被稱為 訂閱器(subscriber) 類。事件使用 釋出-訂閱(publisher-subscriber) 模型。

釋出器(publisher) 是一個包含事件和委託定義的物件。事件和委託之間的聯絡也定義在這個物件中。釋出器(publisher)類的物件呼叫這個事件,並通知其他的物件。

訂閱器(subscriber) 是一個接受事件並提供事件處理程式的物件。在釋出器(publisher)類中的委託呼叫訂閱器(subscriber)類中的方法(事件處理程式)。

3、宣告事件

      由於事件的設計隨同委託使用的,所以事件的型別必須是一個委託,而且在宣告前附加event關鍵字作為字首。

(1)在類的內部宣告事件,首先必須宣告該事件的委託型別。

如:

public delegate void BoilerLogHandler( );

(2)然後,宣告事件本身,使用 event 關鍵字:

// 基於上面的委託定義事件
public event BoilerLogHandler BoilerEventLog;

定義了一個名為 BoilerLogHandler 的委託和一個名為 BoilerEventLog 的事件,該事件在生成的時候會呼叫委託

4、 釋出-訂閱(publisher-subscriber) 模型

(1)訂閱事件:把方法新增到一個事件中。

釋出器:看做是一個期刊社

訂閱器:看做一個讀者

     首先,讀者訂閱期刊社的一個期刊。相當於註冊事件

     然後,當期刊要發行時,會通過電話、郵件等方式通知讀者。相當於觸發事件

     最後,當讀者收到這個通知時候,會選擇買期刊或者不買等一系列行為。相當於響應事件

舉例: https://www.cnblogs.com/yinqixin/p/5056307.html

三、委託和事件應用舉例

using System;
namespace SimpleEvent
{
  using System;
  /***********釋出器類***********/
  public class EventTest
  {
    private int value;
    public delegate void NumManipulationHandler();//宣告委託
    public event NumManipulationHandler ChangeNum;//宣告事件

    protected virtual void OnNumChanged()
    {
      if ( ChangeNum != null )
      {
        ChangeNum(); /* 第三步事件被觸發,執行事件處理方法*/
      }
     else 
      {
        Console.WriteLine( "event not fire" );
        Console.ReadKey(); /* 回車繼續 */
      }
    }

    public EventTest()  //構造方法
    {
      int n = 5;
      SetValue( n );
    }


    public void SetValue( int n )
    {
      if ( value != n )
      {
        value = n;
        OnNumChanged();
      }
    }
  }


  /***********訂閱器類***********/

  public class subscribEvent
  {
    public void printf()
    {
      Console.WriteLine( "event fire" );
      Console.ReadKey(); /* 回車繼續 */
    }
  }

  /***********觸發***********/
  public class MainClass
  {
    public static void Main()
    {
      EventTest e = new EventTest(); /* 例項化物件,第一次沒有觸發事件 */
      subscribEvent v = new subscribEvent(); /* 例項化物件 */
      e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 第一步註冊事件 */
      e.SetValue( 7 );//第二步觸發事件,事件生成時呼叫委託
      e.SetValue( 11 );//第二步觸發事件,事件生成時呼叫委託
    }
  }
}

當上面的程式碼被編譯和執行時,它會產生下列結果:

event not fire

event fire

event fire

另外一個例子:C#事件的訂閱與觸發

四、WPF實現關閉子視窗B時觸發A視窗事件

       最後總結簡單的委託事件的應用只需要五步法。

1、定義子視窗B

釋出器功能:

  //第一步,定義委託,委拖不屬於任何一個類
  public delegate void ChangeTextHandler();
  public partial class B : Window   //事件釋出類
{
    //第二步,宣告事件
    public event ChangeTextHandler ChangeTextEvent;
    public B()
     {
        InitializeComponent();
     }

     //點選子視窗B上的一個按鈕觸發事件
     private void btnCalibraEnter_Click(object sender, RoutedEventArgs e)
      {
          this.Close();//關閉視窗B
          //第四步,呼叫事件
          ChangeTextEvent();
      }
}

2、定義視窗A,通過視窗A開啟子視窗B,開啟子視窗B後立即關閉視窗A

訂閱器功能:

public partial class A : Window
{
    public A()
    {
            InitializeComponent();
    }

     
    //點選視窗A上的按鈕,關閉視窗A,並註冊事件
     private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            this.Close();//關閉當前視窗
            //例化視窗B
            B demaWin = new B();
            demaWin.ChangeTextEvent += new ChangeTextHandler(method);//第三步,註冊事件
            demaWin.Show();//開啟視窗B

        }

        //第五步,事件發生時要呼叫的方法
        private void method() 
        {
           //雖然當前視窗A已經關閉,但是事件觸發時,程式依然可以執行到這裡
        }
   
}