探索应用MVVM模式到Revit二开中,发挥WPF的优势
核心代码Model基类:ObservableObject
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace MvvmFoundation.Wpf
{
/// 具有属性变化通知功能的抽象基类.
public abstract class ObservableObject : INotifyPropertyChanged
{
protected ObservableObject(){ }
//触发属性变化事件
protected void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
// DEBUG 条件编译下具有的函数
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// 同时所有属性发生更新
if (String.IsNullOrEmpty(propertyName))
return;
// 验证当前对象特定属性的有效性
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{//这里的TypeDescriptor实现的功能类似于Reflection出实例的“propertyName”标识的属性
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new ArgumentException(msg);
else
Debug.Fail(msg);
}
}
//子类重写此属性,可以控制VerifyPropertyName方法中在属性无效时的操作
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
//属性变化触发的具体事件活动
public event PropertyChangedEventHandler PropertyChanged;
}
}
属性变化事件的监听器:使用观察者模式,在属性对象修改时,自动通知依赖它的对象。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows;
namespace MvvmFoundation.Wpf
{
public class PropertyObserver : IWeakEventListener
where TPropertySource : INotifyPropertyChanged
{
//创建特定类型的属性观察器
public PropertyObserver(TPropertySource propertySource)
{
if (propertySource == null)
throw new ArgumentNullException("propertySource");
//使用弱引用,便于垃圾回收期回收
_propertySourceRef = new WeakReference(propertySource);
_propertyNameToHandlerMap = new Dictionary>();
}
// 在观察器中注册事件,而不是在Model类中
public PropertyObserver RegisterHandler(
Expression> expression,
Action handler)
{
if (expression == null)
throw new ArgumentNullException("expression");
//解析Lambda对象获得属性名
string propertyName = GetPropertyName(expression);
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentException("'expression' did not provide a property name.");
if (handler == null)
throw new ArgumentNullException("handler");
// this.GetPropertySource() 获得弱引用的引用对象,即依赖于属性的对象
TPropertySource propertySource = this.GetPropertySource();
if (propertySource != null)
{
Debug.Assert(!_propertyNameToHandlerMap.ContainsKey(propertyName), "Why is the '" + propertyName + "' property being registered again?");
_propertyNameToHandlerMap[propertyName] = handler;
//将事件添加到 弱引用类的事件管理器 ,PropertyChangedEventManager静态类
PropertyChangedEventManager.AddListener(propertySource, this, propertyName);
}
return this;
}
// 注销事件,注册的逆过程,与注册类似
public PropertyObserver UnregisterHandler(Expression> expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
string propertyName = GetPropertyName(expression);
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentException("'expression' did not provide a property name.");
TPropertySource propertySource = this.GetPropertySource();
if (propertySource != null)
{
if (_propertyNameToHandlerMap.ContainsKey(propertyName))
{
_propertyNameToHandlerMap.Remove(propertyName);
PropertyChangedEventManager.RemoveListener(propertySource, this, propertyName);
}
}
return this;
}
// 触发事件,私有方法
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
bool handled = false;
if (managerType == typeof(PropertyChangedEventManager))
{
PropertyChangedEventArgs args = e as PropertyChangedEventArgs;
if (args != null && sender is TPropertySource)
{
string propertyName = args.PropertyName;
TPropertySource propertySource = (TPropertySource)sender;
if (String.IsNullOrEmpty(propertyName))
{
foreach (Action handler in _propertyNameToHandlerMap.Values.ToArray())
handler(propertySource);
handled = true;
}
else
{
Action handler;
if (_propertyNameToHandlerMap.TryGetValue(propertyName, out handler))
{
handler(propertySource);
handled = true;
}
}
}
}
return handled;
}
//解析Lambda对象获得属性名
static string GetPropertyName(Expression> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
Debug.Assert(memberExpression != null, "Please provide a lambda expression like 'n => n.PropertyName'");
if (memberExpression != null)
{
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
return null;
}
// 获得依赖于属性的对象,即构造函数中传入的TPropertySource的具体实现类
TPropertySource GetPropertySource()
{
try
{
return (TPropertySource)_propertySourceRef.Target;
}
catch
{
return default(TPropertySource);
}
}
readonly Dictionary> _propertyNameToHandlerMap;
readonly WeakReference _propertySourceRef;
}
}
依赖命令RelayCommand类, RelayCommand 类
和我们自己实现的ICommand接口类似,不做介绍。
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace MvvmFoundation.Wpf
{
///
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
///
public class RelayCommand : ICommand
{
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
///
/// Creates a new command.
///
/// The execution logic.
/// The execution status logic.
public RelayCommand(Action execute, Predicate canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion // ICommand Members
#region Fields
readonly Action _execute = null;
readonly Predicate _canExecute = null;
#endregion // Fields
}
///
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
///
public class RelayCommand : ICommand
{
#region Constructors
///
/// Creates a new command that can always execute.
///
/// The execution logic.
public RelayCommand(Action execute)
: this(execute, null)
{
}
///
/// Creates a new command.
///
/// The execution logic.
/// The execution status logic.
public RelayCommand(Action execute, Func canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
#endregion // ICommand Members
#region Fields
readonly Action _execute;
readonly Func _canExecute;
#endregion // Fields
}
}
消息传递Messenger:由于目前我在Revit二开中跨窗口通信较少,所以看这部分代码。用到时在研究。
MSDN参考链接:
TyperDescripter
弱引用
PropertyChangedEventManager
CommandManager