雖說大部分的邏輯交互可以通過數據綁定和命令綁定實現,但依然有些需求需要在邏輯模塊中涉及到UI.比如說--提示。
有些邏輯處理結果需要展示給用戶看,展示的方式很多樣。如,界面彈框,提示等等。由於展示都是臨時性的,通過綁定展示的話並不是好的選擇,那怎麼辦呢?我選擇用廣播監聽的方式。
大概原理是:邏輯處理完成後,廣播一個特定的信號出去,信號中附加參數,接收到的模塊自己選擇處理方式。
看下面的代碼:
public delegate void Handler(object sender, CommonEventArgs e);
public static class EventDispatcher
{
#region Private
private static Dictionary<string, Dictionary<string, Handler>> TypeHandlers = new Dictionary<string, Dictionary<string, Handler>>();
#endregion
#region FireEvent Function
public static void FireEvent(object Sender, string KeyType, string Key, CommonEventArgs e)
{
if (!TypeHandlers.ContainsKey(KeyType))
return;
Dictionary<string, Handler> Handlers = TypeHandlers[KeyType];
if (Handlers != null && Handlers.ContainsKey(Key))
{
Handlers[Key](Sender, e);
}
}
public static void FireEvent(object sender, string key, CommonEventArgs e)
{
FireEvent(sender, BaseEventKeys.DefaultKeyType, key, e);
}
public static void FireEvent(string key, CommonEventArgs e)
{
FireEvent(null, BaseEventKeys.DefaultKeyType, key, e);
}
#endregion
#region Register Function
public static void RegisterHandler(string KeyType, string Key, Handler Func)
{
if (string.IsNullOrEmpty(Key) || Func==null)
{
throw new Exception("Invalid Key or Function");
}
Dictionary<string, Handler> Handers = GetKeyTypeList(KeyType);
if (Handers.ContainsKey(Key))
{
Handers[Key] += Func;
}
else
{
Handers.Add(Key, Func);
}
}
public static void RegisterHandler(string key, Handler func)
{
RegisterHandler(BaseEventKeys.DefaultKeyType, key, func);
}
#endregion
#region UnRegister Function
public static void UnRegisterHandler(string key)
{
UnRegisterHandler(BaseEventKeys.DefaultKeyType,key,null);
}
public static void UnRegisterHandler(string KeyType, string Key, Handler Func)
{
Dictionary<string, Handler> Handlers = GetKeyTypeList(KeyType);
if (Handlers == null || !Handlers.ContainsKey(Key))
return;
if (Func == null)
Handlers.Remove(Key);
else
{
Handlers[Key] -= Func;
if (Handlers[Key]==null)
Handlers.Remove(Key);
}
}
#endregion
#region Private Function
private static Dictionary<string, Handler> GetKeyTypeList(string KeyType)
{
if (string.IsNullOrEmpty(KeyType))
{
throw new Exception("Invalid KeyType");
}
if (!TypeHandlers.ContainsKey(KeyType))
{
Dictionary<string, Handler> EventHandlers = new Dictionary<string, Handler>();
TypeHandlers.Add(KeyType, EventHandlers);
return EventHandlers;
}
return TypeHandlers[KeyType];
}
#endregion
}
EventDispatcher 的主要作用是處理信號註冊和信號廣播,這是一個靜態類,不需要實例化既可以直接使用,可用於任何模塊之間的交互。
RegisterHandler用於監聽信號,KeyType是信號域,Key是信號域下的子信號,Func是處理此信號的函數。由於信號可能很多,而且有些信號是同一種類別,所以我加上了信號域的概念。註冊的時候不填寫信號域就會使用默認的信號域。
所有的信號都存儲在TypeHandlers中,這是個哈希表,所以不用擔心信號過多時會出現性能問題。
FireEvent用於廣播信號,Sender爲發送者,KeyType爲信號域,Key是信號e則是信號所附帶的參數。
e的類型爲CommonEventArgs,以下爲它的代碼:
public class CommonEventArgs : EventArgs
{
private Dictionary<string, object> args = new Dictionary<string, object>();
public Handler Handler;
public CommonEventArgs()
{
}
public CommonEventArgs(string argName, object argValue)
{
args.Add(argName, argValue);
}
public void Add(string argName, object argValue)
{
args.Add(argName, argValue);
}
public T Get<T>(string argName)
{
return args.ContainsKey(argName) ? (T)args[argName] : default(T);
}
public object Get(string argName)
{
return args.ContainsKey(argName) ? args[argName] : default(object);
}
public T Get<T>(string argName,T defaultValue)
{
return args.ContainsKey(argName) ? (T)args[argName] : defaultValue;
}
}
這個類中將附帶信號處理所需的所有數據,數據存儲在args中,這依然是個哈希表。通過事先約定好的鍵值進行存儲。另外還有個Handler參數,這個參數可以用於回掉。當摸個信號接收到並處理完成後,可以用Handler參數反饋給發送着。
下面看一下簡單的使用方法,首先定義信號域和子信號:
public class BaseEventKeys
{
/// <summary>
/// 數據模板
/// </summary>
public static string Protocol = "Protocol";
/// <summary>
/// 默認類型
/// </summary>
public static string DefaultKeyType = "Default";
}
信號域和子信號都是string類型,DefaultKeyType爲默認信號域,Protocol則爲默認的子信號。
假定有以下邏輯需要實現:某個數據列表需要從服務器獲取數據源,當異步獲取數據成功時,在當前界面想用戶進行提示“獲取數據成功!”。
首先在BaseEventKeys中添加信號GetDataSucced:
public static string GetDataSucced= "Succed";
然後在當前頁面註冊此信號:
public sealed partial class GroupedItemsPage : MvfmText.Common.LayoutAwarePage
{
public GroupedItemsPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
EventDispatcher.RegisterHandler(BaseEventKeys.GetDataSucced, GetDataSucced);
}
void GetDataSucced(object sender, CommonEventArgs e)
{
string msg = e.Get<string>(BaseEventKeys.Protocol);
if (!string.IsNullOrEmpty(msg))
MessageBox.Show(msg);
}
}
最後當需要提醒用戶只需調用以下代碼即可: EventDispatcher.FireEvent(BaseEventKeys.GetDataSucced, new CommonEventArgs(BaseEventKeys.Protocol, "獲取數據成功!"))
是不是很簡單!使用這種模式傳遞信息的好處在於模塊之間最大化的解耦,同時又完成了模塊之間的通信。這種通信方式符合設計模式四原則中的迪米特法則。