事件
基本用法
關(guān)鍵字event,聲明格式為:
public event <委托類型> <事件對象>
事件的處理方法:適用于該委托的方法
數(shù)據(jù)的觸發(fā):
- 綁定同類事件,綁定時,可以綁定新的委托對象(更容易理解的寫法),也可以隱式轉(zhuǎn)換,直接綁定方法。
- 手動觸發(fā)
例子:
//新建一個EventArgs類的子類用于處理
class MessageArrivedEventArgs : EventArgs
{
private string message;
public string Message { get => message; }
public MessageArrivedEventArgs()
{
message = "No message sent";
}
public MessageArrivedEventArgs(string newMessage)
{
message = newMessage;
}
}
//事件的處理方法
class Display
{
internal void DisplayMessage(object sender, MessageArrivedEventArgs e)
{
Console.WriteLine("Message arrived from:" + ((Connection)sender).Name);
Console.WriteLine("Message text:" + e.Message);
}
}
class Connection
{
//事件聲明
//EventHandler是系統(tǒng)自建的用于處理事件的委托
public event EventHandler<MessageArrivedEventArgs> MessageArrived;
public String Name { get; set; }
private Timer pollTimer;
public static Random random = new Random();
public Connection()
{
pollTimer = new Timer(100);
//達(dá)到時間間隔時用CheckForMessage方法處理事件。(類型EvenHandler<MessageArrivedArgs>已經(jīng)隱式轉(zhuǎn)換)
pollTimer.Elapsed += CheckForMessage;
}
public void Connect() => pollTimer.Start();
public void Disconnect() => pollTimer.Stop();
private void CheckForMessage(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Checking for new messages.");
if (random.Next(9) == 0)
{
//觸發(fā)事件。
MessageArrived?.Invoke(this, new MessageArrivedEventArgs(DateTime.Now.ToLongTimeString()));
}
}
}
//主程序
class MainControll
{
static void Main(string[] args)
{
Connection connection = new Connection();
Connection connectionB = new Connection();
connection.Name = "First connection";
connectionB.Name = "Second connection";
Display display = new Display();
//事件觸發(fā)時用DisplayMessage方法處理事件
connection.MessageArrived += display.DisplayMessage;
connectionB.MessageArrived += display.DisplayMessage;
connection.Connect();
connectionB.Connect();
System.Threading.Thread.Sleep(2000);
ReadKey();
}
}
匿名委托方法
當(dāng)一個事件處理方法僅在一處調(diào)用時,可以干脆寫成匿名方法,比如,如果上述示例代碼中的DisplayMessage僅調(diào)用一次的話,可以寫成以下形式:
connection.MessageArrived += delegate(Conection sender,MessageArrivedEventArgs e)
{
Console.WriteLine("Message arrived from:" + ((Connection)sender).Name);
Console.WriteLine("Message text:" + e.Message);
}
寫成匿名方法,可以更加直觀。
EventHandler
分為默認(rèn)的EventHandler和帶有類型的EventHandler<T>,后者可以指定事件實(shí)參的類型。
EventManager
EventManager類在大型應(yīng)用開發(fā)中可以非常好用的來設(shè)置全局事件,起到類似切面編程的效果,比如,為一個已經(jīng)基本開發(fā)完畢的應(yīng)用添加權(quán)限控制功能,就可以用到。該類只有五個方法:
public static class EventManager
{
//
// 摘要:
// 為已注冊到事件系統(tǒng)的路由事件返回標(biāo)識符。
//
// 返回結(jié)果:
// 包含注冊對象的 System.Windows.RoutedEvent 類型的數(shù)組。
public static RoutedEvent[] GetRoutedEvents();
//
// 摘要:
// 查找使用所提供的所有者類型注冊的事件的所有路由事件標(biāo)識符。
//
// 參數(shù):
// ownerType:
// 從其開始搜索的類型。搜索中包含基類。
//
// 返回結(jié)果:
// 如果找到任何匹配項(xiàng),則返回匹配路由事件標(biāo)識符的數(shù)組;否則返回 null。
public static RoutedEvent[] GetRoutedEventsForOwner(Type ownerType);
//
// 摘要:
// 為特定路由事件注冊類處理程序。
//
// 參數(shù):
// classType:
// 聲明類處理的類的類型。
//
// routedEvent:
// 要處理的事件的路由事件標(biāo)識符。
//
// handler:
// 對類處理程序?qū)崿F(xiàn)的引用。
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static void RegisterClassHandler(Type classType, RoutedEvent routedEvent, Delegate handler);
//
// 摘要:
// 使用處理事件數(shù)據(jù)已標(biāo)記為已處理的事件的選項(xiàng),為特定路由事件注冊類處理程序。
//
// 參數(shù):
// classType:
// 聲明類處理的類的類型。
//
// routedEvent:
// 要處理的事件的路由事件標(biāo)識符。
//
// handler:
// 對類處理程序?qū)崿F(xiàn)的引用。
//
// handledEventsToo:
// 如果即使已將路由事件的參數(shù)標(biāo)記為已處理時也調(diào)用此類處理程序,則為 true;如果保留不對任何標(biāo)記為已處理的事件調(diào)用處理程序的默認(rèn)行為,則為 false。
public static void RegisterClassHandler(Type classType, RoutedEvent routedEvent, Delegate handler, bool handledEventsToo);
//
// 摘要:
// 向 Windows Presentation Foundation (WPF) 事件系統(tǒng)注冊新的路由事件。
//
// 參數(shù):
// name:
// 路由事件的名稱。該名稱在所有者類型中必須是唯一的,并且不能為 null 或空字符串。
//
// routingStrategy:
// 作為枚舉值的事件的路由策略。
//
// handlerType:
// 事件處理程序的類型。該類型必須為委托類型,并且不能為 null。
//
// ownerType:
// 路由事件的所有者類類型。該類型不能為 null。
//
// 返回結(jié)果:
// 新注冊的路由事件的標(biāo)識符。現(xiàn)在可將該標(biāo)識符對象存儲為類中的靜態(tài)字段,然后將其用作將處理程序附加到事件的方法的參數(shù)。路由事件標(biāo)識符也用于其他事件系統(tǒng) APIs。
public static RoutedEvent RegisterRoutedEvent(string name, RoutingStrategy routingStrategy, Type handlerType, Type ownerType);
}
官方文檔非常詳細(xì)了,可以看到,除了查詢由Manager管理的事件,主要形式功能的方法有兩個
- RegisterClassHandler 最關(guān)鍵的方法之一,前三個參數(shù)為類型,路由事件,事件處理委托,最后一個可缺省參數(shù)決定是否處理已經(jīng)被處理過的路由事件。
- RegisterRoutedEvent 向WPF中注冊自定義路由事件的方法。
對于RegisterClassHandler的有效使用,可以大大提高應(yīng)用的開發(fā)效率。就拿掃雷這款游戲而言,掃雷的界面上每一個各自都是Button,如果我們對每個Button的Click事件都進(jìn)行分別處理,是一件沒有必要的事情。而使用該方法,我們可以對所有Button控件來進(jìn)行處理。
下面舉一個全局處理權(quán)限的例子:
EventManager.RegisterClassHandler(typeof(TabControl), Selector.SelectionChangedEvent, new RoutedEventHandler(DisableTabControl));
針對全局的TabControl空間的SelectionChangedEvent去進(jìn)行處理,處理方法如下,如果是在C# 6環(huán)境下,還可以寫的再簡單點(diǎn)。
private void DisableTabControl(object sender, RoutedEventArgs e)
{
if (sender is TabControl)
{
var tabControl = sender as TabControl;
foreach (var item in tabControl.Items)
{
if (item is TabItem)
{
var tabItem = item as TabItem;
var valueGot = GlobalParams.FunctionDictionary.TryGetValue(tabItem.Header.ToString(), out string auth);
if (valueGot && !GlobalParams.AuthSet.Contains(auth))
{
tabItem.Visibility = Visibility.Hidden;
if (tabItem == tabControl.SelectedItem)
{
tabControl.SelectedItem = null;
}
}
}
}
}
}