事件
基本用法
關(guān)鍵字event局蚀,聲明格式為:
public event <委托類(lèi)型> <事件對(duì)象>
事件的處理方法:適用于該委托的方法
數(shù)據(jù)的觸發(fā):
- 綁定同類(lèi)事件荸型,綁定時(shí)查蓉,可以綁定新的委托對(duì)象(更容易理解的寫(xiě)法)妆兑,也可以隱式轉(zhuǎn)換贮预,直接綁定方法。
- 手動(dòng)觸發(fā)
例子:
//新建一個(gè)EventArgs類(lèi)的子類(lèi)用于處理
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á)到時(shí)間間隔時(shí)用CheckForMessage方法處理事件排嫌。(類(lèi)型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ā)時(shí)用DisplayMessage方法處理事件
connection.MessageArrived += display.DisplayMessage;
connectionB.MessageArrived += display.DisplayMessage;
connection.Connect();
connectionB.Connect();
System.Threading.Thread.Sleep(2000);
ReadKey();
}
}
匿名委托方法
當(dāng)一個(gè)事件處理方法僅在一處調(diào)用時(shí),可以干脆寫(xiě)成匿名方法淳地,比如怖糊,如果上述示例代碼中的DisplayMessage僅調(diào)用一次的話帅容,可以寫(xiě)成以下形式:
connection.MessageArrived += delegate(Conection sender,MessageArrivedEventArgs e)
{
Console.WriteLine("Message arrived from:" + ((Connection)sender).Name);
Console.WriteLine("Message text:" + e.Message);
}
寫(xiě)成匿名方法,可以更加直觀伍伤。
EventHandler
分為默認(rèn)的EventHandler和帶有類(lèi)型的EventHandler<T>并徘,后者可以指定事件實(shí)參的類(lèi)型。
EventManager
EventManager類(lèi)在大型應(yīng)用開(kāi)發(fā)中可以非常好用的來(lái)設(shè)置全局事件扰魂,起到類(lèi)似切面編程的效果麦乞,比如,為一個(gè)已經(jīng)基本開(kāi)發(fā)完畢的應(yīng)用添加權(quán)限控制功能劝评,就可以用到姐直。該類(lèi)只有五個(gè)方法:
public static class EventManager
{
//
// 摘要:
// 為已注冊(cè)到事件系統(tǒng)的路由事件返回標(biāo)識(shí)符。
//
// 返回結(jié)果:
// 包含注冊(cè)對(duì)象的 System.Windows.RoutedEvent 類(lèi)型的數(shù)組蒋畜。
public static RoutedEvent[] GetRoutedEvents();
//
// 摘要:
// 查找使用所提供的所有者類(lèi)型注冊(cè)的事件的所有路由事件標(biāo)識(shí)符声畏。
//
// 參數(shù):
// ownerType:
// 從其開(kāi)始搜索的類(lèi)型。搜索中包含基類(lèi)姻成。
//
// 返回結(jié)果:
// 如果找到任何匹配項(xiàng)插龄,則返回匹配路由事件標(biāo)識(shí)符的數(shù)組;否則返回 null科展。
public static RoutedEvent[] GetRoutedEventsForOwner(Type ownerType);
//
// 摘要:
// 為特定路由事件注冊(cè)類(lèi)處理程序辫狼。
//
// 參數(shù):
// classType:
// 聲明類(lèi)處理的類(lèi)的類(lèi)型。
//
// routedEvent:
// 要處理的事件的路由事件標(biāo)識(shí)符辛润。
//
// handler:
// 對(duì)類(lèi)處理程序?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),為特定路由事件注冊(cè)類(lèi)處理程序砂竖。
//
// 參數(shù):
// classType:
// 聲明類(lèi)處理的類(lèi)的類(lèi)型真椿。
//
// routedEvent:
// 要處理的事件的路由事件標(biāo)識(shí)符。
//
// handler:
// 對(duì)類(lèi)處理程序?qū)崿F(xiàn)的引用乎澄。
//
// handledEventsToo:
// 如果即使已將路由事件的參數(shù)標(biāo)記為已處理時(shí)也調(diào)用此類(lèi)處理程序突硝,則為 true;如果保留不對(duì)任何標(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)注冊(cè)新的路由事件。
//
// 參數(shù):
// name:
// 路由事件的名稱(chēng)浙于。該名稱(chēng)在所有者類(lèi)型中必須是唯一的护盈,并且不能為 null 或空字符串。
//
// routingStrategy:
// 作為枚舉值的事件的路由策略羞酗。
//
// handlerType:
// 事件處理程序的類(lèi)型腐宋。該類(lèi)型必須為委托類(lèi)型,并且不能為 null。
//
// ownerType:
// 路由事件的所有者類(lèi)類(lèi)型胸竞。該類(lèi)型不能為 null欺嗤。
//
// 返回結(jié)果:
// 新注冊(cè)的路由事件的標(biāo)識(shí)符。現(xiàn)在可將該標(biāo)識(shí)符對(duì)象存儲(chǔ)為類(lèi)中的靜態(tài)字段卫枝,然后將其用作將處理程序附加到事件的方法的參數(shù)煎饼。路由事件標(biāo)識(shí)符也用于其他事件系統(tǒng) APIs。
public static RoutedEvent RegisterRoutedEvent(string name, RoutingStrategy routingStrategy, Type handlerType, Type ownerType);
}
官方文檔非常詳細(xì)了校赤,可以看到吆玖,除了查詢(xún)由Manager管理的事件,主要形式功能的方法有兩個(gè)
- RegisterClassHandler 最關(guān)鍵的方法之一痒谴,前三個(gè)參數(shù)為類(lèi)型衰伯,路由事件铡羡,事件處理委托积蔚,最后一個(gè)可缺省參數(shù)決定是否處理已經(jīng)被處理過(guò)的路由事件。
- RegisterRoutedEvent 向WPF中注冊(cè)自定義路由事件的方法烦周。
對(duì)于RegisterClassHandler的有效使用尽爆,可以大大提高應(yīng)用的開(kāi)發(fā)效率。就拿掃雷這款游戲而言读慎,掃雷的界面上每一個(gè)各自都是Button漱贱,如果我們對(duì)每個(gè)Button的Click事件都進(jìn)行分別處理,是一件沒(méi)有必要的事情夭委。而使用該方法幅狮,我們可以對(duì)所有Button控件來(lái)進(jìn)行處理。
下面舉一個(gè)全局處理權(quán)限的例子:
EventManager.RegisterClassHandler(typeof(TabControl), Selector.SelectionChangedEvent, new RoutedEventHandler(DisableTabControl));
針對(duì)全局的TabControl空間的SelectionChangedEvent去進(jìn)行處理株灸,處理方法如下崇摄,如果是在C# 6環(huán)境下,還可以寫(xiě)的再簡(jiǎ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;
}
}
}
}
}
}