个人集成平台
一个基于 Prism 思想的 MVVM 平台。
介绍
我最近学习了 Prism,我认为这个框架的主要目的是实现松耦合。所以我决定构建一个使用 Prism 思想的个人集成平台。而且我可以使用这个平台来集成我过去创建的应用程序。
背景
该平台基于 'MEF','WPF MVVM 模式' 和 'Prism 思想' 技术。对于个人指示,我使用了 Prism 的思想,并对 Prism 中的组件进行了轻量级实现,例如 'ServiceLocator','Bootstrapper' 和 'Modularity'。而且我封装了 'WPF MVVM 模式' 的基本支持。
将来我会更新 Prism 的实现和 MVVM 的基本支持。
架构
我的系统架构如下:
a. Personal.Component
作为一个基本组件,我参考了 Prism 的思想,并实现了 Prism 的几个组件,包括 'Bootstrapper','ServiceLocator' 和 'Modularity',它们使用 MEF 架构。所有这些组件都是轻量级实现。
b. Personal.Presentation
作为一个基本组件,我封装了 WPF MVVM 模式,包括 'ViewModel','RelayCommand' 和 FrameworkElement 的一些行为。我会不断更新这个组件。
c. 集成实用工具
此组件支持集成的业务。集成的数据结构和服务在此组件中定义。
d. 个人集成平台
此组件是在桌面上运行的 WPF Shell。
e. NotifyIconWpf
这是一个第三方应用程序,用于在 WPF 中实现托盘图标。我把它拿来用于我的 Shell。
f. 子系统
子系统是我平台中的一个定义。它指示可以集成到我的平台中的特定功能模块。我已经将关闭子系统集成到平台中,其目的是退出应用程序。
集成
我使用 MEF 架构而不是以前的 ADD-IN 架构。而且我认为 MEF 比 ADD-IN 机制更简单。
我参考 Prism 并继续使用 IModule
作为子系统。
public interface IModule
{
int Order { get; }
void Initialize();
}
该顺序是对模块进行排序以进行显示。
MEF 架构用于从程序集或目录扫描模块。我在应用程序的配置中定义扫描模式。以下是模块扫描器接口和实现。
public interface IModuleScanner
{
void SetAssemblies(params Assembly[] assemblies);
void SetDirectories(params string[] directories);
IEnumerable<IModule> GetModules();
}
public enum ScanMode
{
Assembly = 0,
Directory = 1,
Mixed = 2
};
public class MEFModuleScanner : IModuleScanner
{
[ImportMany]
private IEnumerable<IModule> _moudles;
private AggregateCatalog _aggregateCatalog = new AggregateCatalog();
#region IModuleScaner Members
public void SetAssemblies(params Assembly[] assemblies)
{
if (assemblies != null)
{
foreach (var assembly in assemblies)
{
_aggregateCatalog.Catalogs.Add(new AssemblyCatalog(assembly));
}
}
}
public void SetDirectories(params string[] directories)
{
if (directories != null)
{
foreach (var directory in directories)
{
if (Directory.Exists(directory))
{
_aggregateCatalog.Catalogs.Add(new DirectoryCatalog(directory));
}
}
}
}
public IEnumerable<IModule> GetModules()
{
CompositionContainer container = new CompositionContainer(_aggregateCatalog);
try
{
container.ComposeParts(this);
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
}
return _moudles;
}
#endregion
}
还有一个 ModuleInitializer
负责扫描模块和初始化模块。
public class ModulesInitializer
{
private IEnumerable<IModuleScanner> _scanners;
private IEnumerable<IModule> _modules;
public ModulesInitializer(IEnumerable<IModuleScanner> scanners)
{
_scanners = scanners;
}
public ModulesInitializeMode Mode { get; set; }
public void SetScanAssemblies(Assembly[] assemblies)
{
if (_scanners != null)
{
foreach (var scanner in _scanners)
{
scanner.SetAssemblies(assemblies);
}
}
}
public void SetScanDirectories(string[] directories)
{
if (_scanners != null)
{
foreach (var scanner in _scanners)
{
scanner.SetDirectories(directories);
}
}
}
public void Scan()
{
List<IModule> moduleList=new List<IModule>();
foreach (var scanner in _scanners)
{
var modules = scanner.GetModules();
moduleList.AddRange(modules);
}
_modules = moduleList;
}
public void Initialize()
{
foreach (var module in _modules)
{
module.Initialize();
}
}
}
public enum ModulesInitializeMode
{
Assemblies = 0,
Directories = 1,
Mixed = 2
}
对于特定的子系统,它应该做的是实现特定的 IModule
并在模块上方添加 ExportAttribute
。ExportAttribute
在 MEF 架构中定义。您应该引用 'System.Component.Composition' 程序集。
[Export(typeof(IModule))]
public class ShutDownModule : Module
{
public override int Order
{
get { return int.MaxValue; }
}
protected override void Initialize(IIntegrationRepository integrationRepository)
{
integrationRepository.Register(new ShutDownElementViewModel());
}
}
MVVM 支持
对于 MVVM 支持,我现在已经实现了 'ViewModel' 和 'RelayCommand'。将来我会支持更多 MVVM 组件。
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class RelayCommand : ICommand
{
readonly Action mExecute;
readonly Func<bool> mCanExecute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
mExecute = execute;
mCanExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return mCanExecute == null ? true : mCanExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (mCanExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (mCanExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
mExecute();
}
}
public class RelayCommand<T> : ICommand
{
readonly Action<T> mExecute = null;
readonly Predicate<T> mCanExecute = null;
public event EventHandler CanExecuteChanged
{
add
{
if (mCanExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (mCanExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
mExecute = execute;
mCanExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return mCanExecute == null ? true : mCanExecute((T)parameter);
}
public void Execute(object parameter)
{
mExecute((T)parameter);
}
}
运行结果
我已经实现了一个关闭模块和一个用于测试的模拟模块。以下是运行结果。
兴趣点
在 Prism 的这种实践中,我选择了 WPF 编程,因为我的工作是系统集成。我无法接触应用程序开发。所以我练习这个是为了好玩,并且我更多地了解了这个框架。
历史
我会不断更新所有组件。