为 Silverlight 添加缺失的功能并不难






4.14/5 (9投票s)
为 Silverlight 添加一些缺失的部分。
引言
最近,我的好朋友 Josh Smith 宣布他正在整理一套 MVVM 基础类。一如既往的 Josh 风格,这些类非常有用且设计巧妙,代表了他过去几年为帮助 MVVM 所做的优秀类库中的一部分。
这些类中的大多数可以直接在 WPF 和 Silverlight 中开箱即用,但问题在于缺少一个关键部分。Josh 最近开发了一个用于观察实现 INotifyPropertyChanged
接口的对象的类,它非常出色;但由于 Silverlight 不支持用于监控的基础机制,即 PropertyChangedEventManager
类,因此它在 Silverlight 中几乎完全无用。现在,如果是在一个不如 .NET 优秀的框架中,我会担心——但 .NET 赋予我们添加缺失功能的自由,而且如果我不喜欢捣鼓,我就不是一个 WPF 追随者。
在继续之前,我要说的是 Silverlight 工具包中有一个替代实现,即 WeakEventListener
。这个类的问题(我谨慎地使用“问题”这个词)是它要求你使用 Lambda 表达式。这也意味着 Josh 编写的类需要进行修改,所以我决定看看实现 PropertyChangedEventManager
在 Silverlight 中工作有多难。
由于我正在致力于一个明确的功能集(即我只需要处理 Josh 类中的功能),因此我不需要重新创建底层框架类中的所有功能。然而,我希望它能够与弱事件模式一起工作,所以,废话不多说——以下是在 Silverlight 中的功能
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.Generic;
namespace System.Windows
{
/// <summary>
/// Provides an implementation so that you can use the
/// "weak event listener" pattern to attach listeners
/// for the <see cref="PropertyChanged" /> event.
/// </summary>
public class PropertyChangedEventManager
{
#region Members
private Dictionary<string, List<WeakReference>> _list;
private static object SyncLock = new object();
private static PropertyChangedEventManager _manager = null;
#endregion
#region Public methods
/// <summary>
/// Adds the specified listener to the list of listeners on the specified source.
/// </summary>
/// <param name="source">The object with the event.</param>
/// <param name="listener">The object to add as a listener.</param>
/// <param name="propertyName">The name of the property that exists on
/// source upon which to listen for changes.</param>
public static void AddListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
Instance.PrivateAddListener(source, listener, propertyName);
}
/// <summary>
/// Removes the specified listener from the list of listeners on the
/// specified source.
/// </summary>
/// <param name="source">The object with the event.</param>
/// <param name="listener">The object to remove as a listener.</param>
/// <param name="propertyName">The name of the property that exists
/// on source upon which to listen for changes.</param>
public static void RemoveListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
Instance.PrivateRemoveListener(source, listener, propertyName);
}
#endregion
/// <summary>
/// Get the current instance of <see cref="PropertyChangedEventManager"/>
/// </summary>
private static PropertyChangedEventManager Instance
{
get
{
if (_manager == null)
_manager = new PropertyChangedEventManager();
return _manager;
}
}
/// <summary>
/// Begin listening for the <see cref="PropertyChanged"/> event on
/// the provided source.
/// </summary>
/// <param name="source">The object on which to start listening
/// for <see cref="PropertyChanged"/>.</param>
private void StartListening(INotifyPropertyChanged source)
{
source.PropertyChanged += new PropertyChangedEventHandler(this.PropertyChanged);
}
/// <summary>
/// Stop listening for the <see cref="PropertyChanged"/> event on the
/// provided source.
/// </summary>
/// <param name="source">The object on which to start listening for
/// <see cref="PropertyChanged"/>.</param>
private void StopListening(INotifyPropertyChanged source)
{
source.PropertyChanged -= new PropertyChangedEventHandler(this.PropertyChanged);
}
/// <summary>
/// The method that handles the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="args">A <see cref="PropertyChangedEventArgs"/> that
/// contains the event data.</param>
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
List<WeakReference> list = _list[args.PropertyName];
if (list != null)
{
// We have the listeners. Deal with them
foreach (WeakReference item in list)
{
IWeakEventListener eventItem = item.Target as IWeakEventListener;
if (eventItem != null && item.IsAlive)
{
eventItem.ReceiveWeakEvent(this.GetType(), sender, args);
}
}
}
}
/// <summary>
/// Private method to add the specified listener to the list of listeners
/// on the specified source.
/// </summary>
/// <param name="source">The object with the event.</param>
/// <param name="listener">The object to add as a listener.</param>
/// <param name="propertyName">The name of the property that exists
/// on source upon which to listen for changes.</param>
private void PrivateAddListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
if (_list == null)
{
_list = new Dictionary<string, List<WeakReference>>();
}
lock (SyncLock)
{
WeakReference reference = new WeakReference(listener);
if (_list.ContainsKey(propertyName))
{
_list[propertyName].Add(reference);
}
else
{
List<WeakReference> list = new List<WeakReference>();
list.Add(reference);
_list.Add(propertyName, list);
}
// Now, start listening to source
StartListening(source);
}
}
/// <summary>
/// Private method to remove the specified listener from the list of listeners
/// on the specified source.
/// </summary>
/// <param name="source">The object with the event.</param>
/// <param name="listener">The object to remove as a listener.</param>
/// <param name="propertyName">The name of the property that exists on
/// source upon which to listen for changes.</param>
private void PrivateRemoveListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
if (_list != null)
{
lock (SyncLock)
{
if (_list.ContainsKey(propertyName))
{
// Stop responding to changes
StopListening(source);
// Remove the item from the list.
WeakReference reference = null;
foreach (WeakReference item in _list[propertyName])
{
if (item.Target.Equals(listener))
{
reference = item;
}
}
if (reference != null)
{
_list[propertyName].Remove(reference);
}
}
}
}
}
}
}
唯一缺失的是弱事件侦听器接口(IWeakEventListener
)
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace System.Windows
{
/// <summary>
/// Provides event listening support for classes that expect to receive events
/// through the WeakEvent pattern and a WeakEventManager.
/// </summary>
public interface IWeakEventListener
{
/// <summary>
/// Receives events from the centralized event manager.
/// </summary>
/// <param name="managerType">The type of the
/// WeakEventManager calling this method.</param>
/// <param name="sender">Object that originated the event.</param>
/// <param name="e">Event data.</param>
/// <returns>true if the listener handled the event. It is considered an error by the
/// WeakEventManager handling in WPF to register a listener for an event that the
/// listener does not handle. Regardless, the method should return false if it receives
/// an event that it does not recognize or handle.
/// </returns>
bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e);
}
}
就是这样——这就是添加“缺失”功能所需的一切。很简单,不是吗?
你可以从 这里 下载源代码。请注意,你需要在解压缩文件之前将文件名从 .doc 更改为 .zip。
