AvalonDock 2.0+Caliburn.Micro+MahApps.Metro實現Metro風格插件式系統(一)

   隨着IOS7由以前UI的擬物化設計變爲現在的扁平化設計,也許扁平化的時代要來了,固然咱們是否是該吐槽一下,蘋果何時也開始跟風了,自GOOGLE和微軟界面扁平化事後,蘋果也加入了這一隊伍。express

 AvalonDock編程

   AvalonDock 是一個.NET庫,用於在停靠模式佈局(docking)中排列一系列WPF/WinForm控件。最新發布的版本原生支持MVVM框架、Aero Snap特效並具備更好的性能。bootstrap

AvalonDock 2.0版本已經發布了,新版本是用MVVM框架從新編寫,彷佛也用了Command(命令)模式。2.0版的文檔還沒有發佈,但你能夠參考Avalon.TestApp 或者2.0版源碼中的Avalon.MVVMTestApp文件夾來查看新的API。網絡

這個庫使用很簡單——只須要用AvalonDock提供的控件包含你本身的控件,頁面佈局就當即變成可停靠的(dockable)。能夠參考 入門 頁面獲取樣例代碼,瞭解不一樣控件的特性。固然你也能夠在本身的C#代碼中實例化或操做這些控件。2.0版本中,控件功能與之前一致,但控件名稱已經改變,所以建議參考前述樣例代碼直至參考文檔更新爲止。app

大名鼎鼎SharpEevelop也應用了AvalonDock,因爲SharpEevelop的框架過於龐大,而且SharpEevelop裏的AvalonDock 1.3的版本,並不支持MVVM的模式,因此就興起了本身作一個插件式系統,固然也跟一下扁平化的風,目前框架已經作好並應用到我的項目中,本着開源的思想我會把框架搭建的過程,以及遇到的種種問題分享出來。框架

Caliburn.Micro    異步

Caliburn是Rob Eisenberg在2009年提出的一個開源框架,能夠應用於WPF,Silverlight,WP7等,框架基於MVVM模式,像它的名字同樣,是企業級應用的一把利器。ide

基於WPF的框架有不少,Prism,WAF等,每一個框架都有本身側重點,像Prism側重於模塊間的組合,WAF側重於分層設計。通觀CM的設計,它的一些想法以下: 1.ActionMessage,結合了Blend中的TriggerAction,能夠把UI控件中的事件綁定到後臺方法,相似於CallMethodAction。CM對ActionMessage進行了不少擴展,包括能夠傳入多個參數,參數支持綁定,能夠經過CanExecute做執行前判斷並設置控件的Enable等。異步編程

2.Conventions,協定,這個詞聽上去有點虛,其實就是智能匹配的意思。CM制定了一系列匹配的規則,好比說View和ViewModel之間的匹配,綁定時傳入控件名能夠找到控件,傳入方法名能夠綁定到方法等等。工具

3.Screen和Conductor,做爲一個Presentation的框架,各個UI部件(Widget或者叫Pad)的管理是必不可少的。Screen就是用來表示UI部件的,它定義了一些列UI部件的生命期事件,好比Activated,DeActivated等。Conductor是用來管理Screen的,相似於傳統的Controller,不一樣的Screen能夠用一個Conductor來管理,Conductor也使用了策略模式容許更改對Screen的處理。

4.Coroutines,協同程序,定義了一組程序的執行,簡化了異步編程。好比說在網絡中下載圖片並顯示,一般來講須要顯示BusyIndicator,後臺線程去網絡讀取圖片,讀取成功後Invoke到UI線程,取消BusyIndicator,顯示圖片。CM提供了一個IResult接口,大大的簡化了異步編程,結合ActionMessage,爲AOP的擴展提供了可能。

5.配置性和擴展性,CM移除掉了原Caliburn的一些IOC實現,做爲一個通用框架,最經常使用辦法就是使用工廠模式結合配置文件提供可配置性,使用IOC來解耦組件間的依賴。CM默認是使用MEF來作IOC擴展的,你能夠自定義Bootstrapper來使用你喜歡的IOC容器,如Unity等。

6.設計時支持(Design-time support),CM中的ActionMessage是繼承自Blend中的TriggerAction的,也就是說能夠在Blend編輯ActionMessage,大大方便了使用。 (這段摘抄了周永恆大大的部分對CM框架的解析,你們想詳細瞭解的話能夠去他的博客去學習,我就不仔細說明了,之後我會用到的地方作說明)

MahApps.Metro  

  這是一個Metro樣式的開源項目,應用該項目可使你的軟件具備metro的風格,具體就很少說了。  

 

這是測試項目第一階段的運行結果

 

 

言歸正傳,咱們從零開始建立項目,下面是整個測試項目的結構:

 

 MefBootstrapper

這是啓動加載類,通常咱們WPF程序是從APP.XAML裏StartupUri=「****WINDOWS.XAML」來啓動主窗體,但如今由MefBootstrapper擔當了啓動窗體的職責:

 public class MefBootstrapper : Bootstrapper<IShell>
    {
        private CompositionContainer container;

        protected override void Configure()
        {
            /*CompositionContainer 對象在應用程序中有兩種的主要用途。首先,它跟蹤哪些部分可用於組合、它們的依賴項,而且充當任何指定組合的上下文。其次,它提供了應用程序能夠啓動組合的方法、獲取組合部件的實例,或填充可組合部件的依存關係。
            部件可直接用於容器,或經過 Catalog 屬性來用於容器。在此 ComposablePartCatalog 中可發現的全部部件均可以供容器來知足導入,還包括直接添加的任何部件。*/ 
            container = new CompositionContainer(
                new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)))
                );

            var batch = new CompositionBatch();
            var dockScreenManager = new DockScreenManager();
            batch.AddExportedValue<IWindowManager>(new WindowManager());//將指定的導出加入至 CompositionBatch 物件
            batch.AddExportedValue<IDockScreenManager>(dockScreenManager);
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(container);

            container.Compose(batch);//在容器上執行組合,包括指定的 CompositionBatch 中的更改
        }

        protected override object GetInstance(Type serviceType, string key)
        {
            var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;//獲取指定類型的規範協定名稱
            var exports = container.GetExportedValues<object>(contract);//返回具備從指定的類型參數派生的協定名稱的已導出對象。若是不是正好有一個匹配的已導出對象,則將引起異常。

            if (exports.Any())
                return exports.First();

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        protected override void BuildUp(object instance)
        {
            container.SatisfyImportsOnce(instance);//知足指定的 ComposablePart 對象的導入,而無需註冊該對象以進行從新組合。
        }
    }

由上可知, MefBootstrapper繼承與CM框架提供Bootstrapper<TRootModel>,當Bootstrapper加載時,CM框架便會從MEF容器裏尋找出TRootModel類型的實例,而且show出來,也就是咱們的主窗體,以後我會把項目源碼放出來,你們能夠本身跟蹤OnStartup事件。

咱們來看看APP.XMAL

<Application x:Class="DemoApplication.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:DemoApplication"
             >
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:MefBootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

 IShell

  一個簡單的接口,爲了方便MEF導出部件

  public interface IShell
    {
    
    }

ShellView.xaml

<MetrolControls:MetroWindow x:Class="DemoApplication.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MetrolControls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        Title="ShellView" Height="500" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <ContentControl  x:Name="DockContent" Margin="0,2,0,0" Grid.Row="0"/>
    </Grid>
</MetrolControls:MetroWindow>

  能夠看到,這時咱們引用了MahApps.Metro,MahApps.Metro自定義了一個WINDOWS,在我看來比傳統的和諧那麼一點,MahApps.Metro裏還有10多種自定義控件,有興趣的能夠本身去研究

ShellViewModel

 [Export(typeof(IShell))]
    class ShellViewModel:IShell
    {
        [Import]
        public IScreen DockContent { get; set; }
    }

   一個ShellView.xaml對應一個ShellViewModel,當ShellViewModel標記爲Export時,Bootstrapper會把當前程序集全部標記爲Export的類導入CM框架的IOC容器裏,ShellViewModel至關於ShellView的Datacontext,一個View的加載過程爲,由Model找到(CM框架定義了各類查找規則)View,並把Model綁定到View的Datacontext,之後咱們UI的邏輯代碼就能夠寫在Model裏面,並與UI徹底分開,這就是咱們所說的MVVM模式。上面也有一個典型的View綁定Model裏的屬性,細心的能夠看到:

 [Import]
  public IScreen DockContent { get; set; }

  該屬性的名稱和ShellView.xaml裏的<ContentControl  x:Name="DockContent" Margin="0,2,0,0" Grid.Row="0"/> 的命名徹底同樣,奇怪的是咱們並無寫任何綁定,但DockContent是怎麼綁定到View裏面的呢,其實綁定的過程已經由CM框架幫咱們作了,CM框架會幫助咱們把Model裏和控件名稱同樣的屬性綁定在一塊兒,這就然咱們省了一些事,這只是CM框架的一些小特性。 

  好了,主窗體的說完了,下面咱們來看看怎麼把AvalonDock融合進去,上面咱們說過了,一個Model對應一個View,因此咱們要顯示一個UserControl時得生成一對Model-View,

DockView.xaml

<UserControl x:Class="DemoApplication.Views.DockView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:avalonDock="http://avalondock.codeplex.com"
             d:DesignHeight="300" d:DesignWidth="800">
    <UserControl.Resources>
        <avalonDock:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
    </UserControl.Resources>
    <Grid>
        <Grid x:Name="layoutRoot">
            <avalonDock:DockingManager  Grid.Row="1" x:Name="dockManager"  AllowMixedOrientation="True"  >
                <avalonDock:DockingManager.Theme>
                    <avalonDock:MetroTheme/>
                </avalonDock:DockingManager.Theme>
                <avalonDock:DockingManager.LayoutItemTemplate>
                    <DataTemplate>
                        <ContentControl IsTabStop="False" />
                    </DataTemplate>
                </avalonDock:DockingManager.LayoutItemTemplate>
                <avalonDock:LayoutRoot>
                    <avalonDock:LayoutPanel  Orientation="Horizontal"  >
                        <avalonDock:LayoutAnchorablePaneGroup DockWidth="200"    Orientation="Vertical"  >
                            <avalonDock:LayoutAnchorablePane  >
                                <avalonDock:LayoutAnchorable Title="left" ></avalonDock:LayoutAnchorable>
                            </avalonDock:LayoutAnchorablePane>
                        </avalonDock:LayoutAnchorablePaneGroup>
                        <avalonDock:LayoutPanel  Orientation="Vertical"  >
                            <avalonDock:LayoutDocumentPaneGroup Orientation="Horizontal">
                                <avalonDock:LayoutDocumentPane   >
                                    <avalonDock:LayoutDocument Title="main"></avalonDock:LayoutDocument>
                                </avalonDock:LayoutDocumentPane>

                            </avalonDock:LayoutDocumentPaneGroup>
                            <avalonDock:LayoutAnchorablePaneGroup DockHeight="100"   Orientation="Horizontal"  >
                                <avalonDock:LayoutAnchorablePane  >
                                    <avalonDock:LayoutAnchorable Title="bottom" ></avalonDock:LayoutAnchorable>
                                </avalonDock:LayoutAnchorablePane>
                            </avalonDock:LayoutAnchorablePaneGroup>
                        </avalonDock:LayoutPanel>
                        <avalonDock:LayoutAnchorablePaneGroup DockWidth="200"    Orientation="Horizontal"  >
                            <avalonDock:LayoutAnchorablePane >
                                <avalonDock:LayoutAnchorable Title="Right" ></avalonDock:LayoutAnchorable>
                            </avalonDock:LayoutAnchorablePane>
                        </avalonDock:LayoutAnchorablePaneGroup>
                    </avalonDock:LayoutPanel>

                </avalonDock:LayoutRoot>
            </avalonDock:DockingManager>
        </Grid>
    </Grid>
</UserControl>

 

這是VS2012設計器的顯示


這些東西的學習週期仍是有的,我就不一一去說。有些東西只可意會不可言傳。

DockViewModel

    [Export(typeof(IScreen))]
    public class DockViewModel : Screen
    {

    }

 

  咱們能夠看到ShellViewModel裏的DockContent就是IScreen類型的,因爲標記爲Import,因此程序會自動幫咱們把MEF容器裏IScreen類型注入,因此其實DockContent就是DockView,我這裏爲了方便直接用了CM框架的IScreen,若是有兩個類標記爲[Export(typeof(IScreen))],就會致使程序異常,由於有兩個實例。程序不知道該導出哪一個,因此咱們以後會定義另外一個接口,該接口只有惟一一個類即惟一的DockViewModel標記爲導出,由於咱們DockView就是惟一的,導入和導出部件這是MEF的知識,MEF是什麼你們能夠百度學習,CM框架默認是MEF做爲容器。

MEF

  Managed Extensibility Framework(MEF)是.NET平臺下的一個擴展性管理框架,它是一系列特性的集合,包括依賴注入(DI)以及Duck Typing等。MEF爲開發人員提供了一個工具,讓咱們能夠輕鬆的對應用程序進行擴展而且對已有的代碼產生最小的影響,開發人員在開發過程當中根據功能要求定義一些擴展點,以後擴展人員就可使用這些擴展點與應用程序交互;同時MEF讓應用程序與擴展程序之間不產生直接的依賴,這樣也容許在多個具備一樣的擴展需求之間共享擴展程序

第一階段就先這樣,之後我會慢慢更新,直道整個插件系統的完成

 

 

若是您看了本篇博客,以爲對您有所收穫,請點擊右下角的 [推薦]

若是您想轉載本博客,請註明出處

若是您對本文有意見或者建議,歡迎留言

感謝您的閱讀,請關注個人後續博客

做者:Zengg 出處:http://www.cnblogs.com/01codeworld/

本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索