首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 複合應用程式架構指南:Prism 核心模式與實作全解析

     編輯:關於.NET

前言

WPF/Silverlight 雖強大於 UI 表現,但無法解決大型專案的可維護性難題。Microsoft Patterns & Practices 團隊推出 WPF Composite Application Guidance (Prism),提供模組化、區域管理、命令、事件聚合等核心服務,協助建構鬆散耦合、可獨立開發/測試/部署的複合應用程式。本文系統解析 Prism 4 大核心概念:Bootstrapper/Container、Module 初始化、Region/RegionManager、View/Presentation Model/Command/EventAggregator。

一、問題背景:單體應用程式的困境

  • 所有 UserControl 靜態宣告於主視窗 XAML,緊密耦合
  • 控制項間透過 RoutedEvent/Command 直接溝通,依賴網狀結構
  • 新增功能需修改主視窗與多處控制項,合併衝突頻繁
  • 團隊無法平行開發,測試困難

單體應用架構

二、複合應用程式核心概念

概念說明
Module功能垂直切片,含 View、ViewModel、Service,獨立組件
Shell主視窗宿主,定義 Region,不包含具體業務邏輯
RegionUI 佔位符(ContentControl/ItemsControl/Selector),Module 在運行時注入 View
Service跨模組共用功能(Logger、EventAggregator、ModuleLoader 等),透過 Container 管理

複合應用組件

三、Bootstrapper 與 Container:應用程式啟動樞紐

3.1 UnityBootstrapper 核心流程

  1. 建立 IoC Container(預設 Unity)
  2. 註冊核心服務:`ILoggerFacade`、`IEventAggregator`、`IModuleLoader`、`IModuleEnumerator`、`RegionAdapterMappings`
  3. 配置 RegionAdapter(ContentControl/ItemsControl/Selector)
  4. 建立 Shell(`CreateShell`)並顯示
  5. 初始化 Module(依賴順序拓撲排序)

Bootstrapper 初始化任務

3.2 自訂 Bootstrapper 範例


public class StockTraderRIBootstrapper : UnityBootstrapper
{
    private readonly EntLibLoggerAdapter _logger = new EntLibLoggerAdapter();

    protected override IModuleEnumerator GetModuleEnumerator()
    {
        return new StaticModuleEnumerator()
            .AddModule(typeof(NewsModule))
            .AddModule(typeof(MarketModule))
            .AddModule(typeof(WatchModule), "MarketModule")
            .AddModule(typeof(PositionModule), "MarketModule", "NewsModule");
    }

    protected override ILoggerFacade LoggerFacade => _logger;

    protected override void ConfigureContainer()
    {
        Container.RegisterType<IShellView, Shell>();
        base.ConfigureContainer();
    }

    protected override DependencyObject CreateShell()
    {
        var presenter = Container.Resolve<ShellPresenter>();
        var view = presenter.View;
        view.ShowView();
        return view as DependencyObject;
    }
}
  • `StaticModuleEnumerator`:靜態宣告模組與相依
  • `ConfigureContainer`:註冊 Shell 介面對應、自訂服務
  • `CreateShell`:使用 MVP 模式建立 Shell,確保初始化完成再顯示
  • 可替換 Container(Windsor、StructureMap、MEF),僅需覆寫 `CreateContainer`

四、Module 初始化:IModule 與依賴注入

4.1 IModule 介面

public interface IModule { void Initialize(); }

4.2 WatchModule 範例


public class WatchModule : IModule
{
    private readonly IUnityContainer _container;
    private readonly IRegionManager _regionManager;

    public WatchModule(IUnityContainer container, IRegionManager regionManager)
    {
        _container = container;
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        RegisterViewsAndServices();

        var watchListPM = _container.Resolve<IWatchListPresentationModel>();
        _regionManager.Regions["WatchRegion"].Add(watchListPM.View);

        var addWatchPresenter = _container.Resolve<IAddWatchPresenter>();
        _regionManager.Regions["MainToolbarRegion"].Add(addWatchPresenter.View);
    }

    private void RegisterViewsAndServices()
    {
        _container.RegisterType<IWatchListService, WatchListService>(new ContainerControlledLifetimeManager());
        _container.RegisterType<IWatchListView, WatchListView>();
        _container.RegisterType<IWatchListPresentationModel, WatchListPresentationModel>();
        _container.RegisterType<IAddWatchView, AddWatchView>();
        _container.RegisterType<IAddWatchPresenter, AddWatchPresenter>();
    }
}
  • 建構函式注入 `IUnityContainer`、`IRegionManager`(Bootstrapper 已註冊)
  • `RegisterViewsAndServices`:模組內部 View/Service 註冊,啟用自動連線
  • 透過 `RegionManager.Regions["Name"].Add(view)` 將 View 注入 Shell 定義的 Region

五、Region 與 RegionManager:UI 組合的關鍵

5.1 XAML 定義 Region


<ItemsControl Grid.Row="1" Grid.Column="1" x:Name="MainToolbar"
    cal:RegionManager.RegionName="MainToolbarRegion">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
  • 附加屬性 `cal:RegionManager.RegionName` 標記 Region
  • 支援 `ContentControl`(單一 View)、`ItemsControl`(多 View)、`Selector`(Tab/下拉單選)

5.2 RegionAdapter 機制

  • `RegionAdapterBase<T>` 針對不同控制項實作 `Adapt`、`CreateRegion`
  • `ItemsControlRegionAdapter`:清空 Items、綁定 `ItemsSource = region.Views`、建立 `AllActiveRegion`
  • `SelectorRegionAdapter`:建立 `SingleActiveRegion`,支援 `IActiveAware` 切換

5.3 區域作用域

  • 預設單一 `RegionManager`,全域可見
  • `region.Add(view, name, true)` 第三參數 `createRegionManagerScope=true` 建立區域作用域
  • 子 Region 僅在該作用域可見,實現「迷你 Shell」隔離
  • 員工明細範例:每位員工一個 `EmployeeDetailsView`,內含 `TabRegion` 動態注入專案清單

六、View 與 Presentation Patterns

6.1 兩種主流模式

模式核心適用
Presentation ModelModel 擁有 UI 狀態與行為,View 純繫結渲染WPF 資料繫結/樣板/命令豐富時
Supervising ControllerPresenter 處理 UI 邏輯,View 委派並回呼簡單操作需精細控制 UI 元素啟用/顯示時

6.2 Presentation Model 範例


public class PositionSummaryPresentationModel : IPositionSummaryPresentationModel, INotifyPropertyChanged
{
    public PositionSummaryPresentationModel(IPositionSummaryView view, ...) { ... }
    public IPositionSummaryView View { get; set; }
    public ObservableCollection<PositionSummaryItem> PositionSummaryItems { get; set; }
}
  • 實作 `INotifyPropertyChanged` 通知 View 更新
  • View 介面 `IPositionSummaryView` 便於單元測試 Mock
  • Stock Trader 大量採用此模式

6.3 Supervising Controller 範例


public class TrendLinePresenter : ITrendLinePresenter
{
    private readonly IMarketHistoryService _service;
    public TrendLinePresenter(ITrendLineView view, IMarketHistoryService service) { ... }
    public ITrendLineView View { get; set; }
    public void OnTickerSymbolSelected(string symbol)
    {
        var history = _service.GetPriceHistory(symbol);
        View.UpdateLineChart(history);
        View.SetChartTitle(symbol);
    }
}

6.4 DataTemplate 驅動 View(無 UserControl)

  • View 為 `ResourceDictionary` 內 `DataTemplate` 集合
  • Model 直接加入 Region,WPF 遞歸尋找對應 Template 渲染
  • 員工明細範例:`HeaderedEmployeeData`、`EmployeeAddressMapUrl`、`EmployeesDetailsPresentationModel` 三層 Template 組合

七、命令:DelegateCommand 簡化 UI 邏輯連線

7.1 傳統 RoutedUICommand 痛點

  • 需在 View Code-behind 實作 `Execute`/`CanExecute` Handler
  • 接收端必須在邏輯樹內,複合應用跨模組失效

7.2 DelegateCommand<T> 解決方案


public class WatchListService
{
    public ObservableCollection<string> WatchItems { get; }
    public DelegateCommand<string> AddWatchCommand { get; }

    public WatchListService(IMarketFeedService feed)
    {
        WatchItems = new ObservableCollection<string>();
        AddWatchCommand = new DelegateCommand<string>(AddWatch);
    }

    private void AddWatch(string symbol)
    {
        WatchItems.Add(symbol);
        // 呼叫服務...
    }
}
  • 建構函式傳入 `Action<T> execute`、`Func<T, bool> canExecute`
  • View XAML 直接 `Command={Binding AddWatchCommand}`,無需 Code-behind
  • 自動處理 `CanExecuteChanged`、UI 啟用/禁用

八、事件聚合器:跨模組鬆散耦合通訊

8.1 核心類別

  • `EventAggregator`:單例服務,內含事件存儲庫
  • `CompositeWpfEvent<TPayload>`:繼承 `EventBase`,使用 `DelegateReference`(弱參考)避免記憶體洩漏
  • `Subscribe(Action<TPayload>, ThreadOption, Predicate<TPayload>)`:支援 UI 執行緒、背景執行緒、過濾條件

8.2 Stock Trader 範例


// 定義事件(共用組件)
public class TickerSymbolSelectedEvent : CompositeWpfEvent<string> { }

// 發布者:PositionSummaryPresentationModel
private void View_TickerSymbolSelected(object sender, DataEventArgs<string> e)
{
    _trendLinePresenter.OnTickerSymbolSelected(e.Value);
    EventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish(e.Value);
}

// 訂閱者:NewsController
public void Run()
{
    regionManager.Regions["NewsRegion"].Add(articlePM.View);
    EventAggregator.GetEvent<TickerSymbolSelectedEvent>()
        .Subscribe(ShowNews, ThreadOption.UIThread);
}
public void ShowNews(string symbol) => articlePM.SetTickerSymbol(symbol);
  • 發布者/訂閱者零耦合,僅共用事件型別定義
  • `ThreadOption.UIThread` 確保 UI 更新在正確執行緒

九、總結與資源

  • Prism 讓大型 WPF 專案具備:模組化開發、運行時 UI 組合、可測試性、團隊平行開發
  • 核心套件:`Microsoft.Practices.Prism`、`Microsoft.Practices.Prism.UnityExtensions`、`Microsoft.Practices.ServiceLocation`
  • 官方下載:WPF Composite Application Guidance
  • 快速入門、參考實作、實驗室教學完整隨包提供

掌握 Prism 四大支柱——Bootstrapper/Container、Module/Region、View/Presentation Pattern、Command/EventAggregator,即能駕馭企業級 WPF 複合應用程式架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved