1. 程式人生 > >[.Net碼農]分享非常漂亮的WPF介面框架原始碼及外掛化實現原理

[.Net碼農]分享非常漂亮的WPF介面框架原始碼及外掛化實現原理

在上文《分享一個非常漂亮的WPF介面框架》中我簡單的介紹了一個介面框架,有朋友已經指出了,這個介面框架是基於ModernUI來實現的,在該文我將分享所有的原始碼,並詳細描述如何基於ModernUI來構造一個非常通用的、外掛化的WPF開發框架。下載原始碼的同志,希望點選一下推薦。

本文將按照以下四點來介紹:

(1)ModernUI簡介;

(2)構建通用介面框架的思路;

(3)基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析;

(4)其它更有趣的東西~~。

1 ModernUI簡介

ModernUI(http://mui.codeplex.com/)是一個開源的WPF介面庫,利用該介面庫,我們可以建立很酷的應用程式。下面是ModernUI官方示例,你可以從官方網站直接下載原始碼執行,如果是.NET 4.0的話,記得要宣告“NET4”預編譯變數,否則無法編譯通過。

mui.sample

要編寫這樣的WPF介面,我們需要在一個Window上宣告選單和Tab頁面,下圖是定義選單的宣告。

image

此外,每一個Tab風格頁面,你也需要手動的為選單建立這樣的介面元素。

直接用這樣的方式來使用ModernUI,顯然不太適合團隊協作性的並行開發,因為在一個團隊的協作中,不同的人需要完成不同的功能,實現不同頁面,每個人都需要來更改主介面。

我非常希望模組化的開發方法,因為這可以儘可能的複用現有資產,使程式設計師可以聚焦在自己關注的業務邏輯上,不需要關心UI的使用。下面,我將來描述基於ModernUI實現的一個通用介面框架,這個介面框架允許程式設計師在自己的業務模組中配置需要顯示的介面元素。

2 通用介面框架實現思路

我希望能夠實現這樣的通用介面框架:

(1)程式設計師可以直接實現需要展現業務邏輯的介面,不需要關注如何使用ModernUI;

(2)程式設計師可以通過簡單的配置就可以將自己實現的業務邏輯頁面顯示在主介面中;

(3)這個介面框架可以完全複用。

當我看到ModernUI這個介面庫時,我希望將應用程式做成模組化,每一個模組能夠:

(1)通過以下配置能夠直接顯示二級選單。

image

(2)通過以下配置能夠直接顯示三級選單。

image

這樣做的好處是,開發外掛的時候可以不需要關心介面框架外掛;團隊在協作開發應用的時候,可以獨立開發並不需要修改主介面;團隊成員的外掛可以隨時整合到這個主介面;當主介面無法滿足我們的佈局時或者使用者需求無法滿足時,可以直接替換主介面框架而不需要修改任何外掛程式碼。

最終的效果如下,以下介面的幾個選單及點選選單顯示的內容由DemoPlugin外掛、DemoPlugin2外掛來提供。當外掛框架載入更多外掛時,介面上會出現更多的選單;反之,當外掛被解除安裝或者被停止時,則相應的選單將消失掉。

image       image

下面我來介紹如何實現。

3 基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析

3.1 OSGi.NET外掛框架原理簡介

OSGi.NET框架是一個完全通用的.NET外掛框架,它支援WPF、WinForm、ASP.NET、ASP.NET MVC 3.0/4.0、控制檯等任意.NET應用程式,也就是說,你可以基於該外掛框架來快速構架外掛化的應用程式。OSGi.NET外掛框架提供了外掛化支援、外掛擴充套件和麵向服務支援三大功能。

OSGi.NET外掛框架啟動時,從外掛目錄中搜索外掛,安裝並啟動這些外掛,將這些外掛組裝在外掛框架中;一個外掛可以暴露擴充套件點,允許其它外掛在不更改其程式碼情況下,擴充套件該外掛的功能;外掛間可以通過服務來進行通訊。

在一個外掛應用程式中,它首先要獲取一個入口點,這個入口點由一個外掛來提供,然後進入這個外掛的入口並執行起來。一個提供入口的外掛通常是一個主介面外掛,比如上面介紹的這個WPF介面框架。也就是說,外掛應用程式啟動起來後,會先執行這個介面框架的主介面。而主介面一般都提供了關於介面元素的擴充套件,允許其它外掛將選單、導航和內容頁面註冊到主介面,因此,當主介面執行時,它會將其它外掛註冊的介面元素顯示出來。當用戶點選介面元素時,外掛框架就會載入這個外掛的頁面,某個外掛的頁面在呈現時,則有可能會從資料庫中提取資料展示,這時候,該外掛則可能會呼叫資料訪問服務提供的通用資料訪問介面。OSGi.NET提供的三大功能,剛好能夠非常的吻合這樣的系統的啟動形式。當然,OSGi.NET除了提供外掛三大支撐功能之外,它還支援外掛動態性與隔離性。動態性,意味著我們可以在執行時來動態安裝、啟動、停止、解除安裝和更新外掛,而隔離性則意味著每一個外掛都擁有自己獨立的目錄,有自己獨立的型別載入器和型別空間。

基於OSGi.NET外掛框架,我們很容易實現外掛的動態安裝、遠端管理、自動化部署、自動升級和應用商店。下面,我來描述如何使用OSGi.NET來構建一個WPF外掛應用。

3.2 基於OSGi.NET來實現WPF外掛應用

利用OSGi.NET來建立一個WPF外掛應用非常的簡單。只需要實現:(1)建立一個外掛主程式,定義外掛目錄;(2)在主程式中利用BootStrapper實現OSGi.NET核心升級檢測與自動升級;(3)啟動外掛框架;(4)利用PageFlowService獲取主介面,然後執行主介面。下面我們看一下外掛主程式。(注:如果你安裝了OSGi.NET框架,可以直接使用專案模板來建立WPF主程式專案。)

image

在這個主程式,我們在專案的屬性將輸出路徑改為bin,並在bin目錄下建立一個Plugins目錄,然後將OSGi.NET四個標準外掛拷貝到Plugins目錄,它們分別用於:(1)外掛遠端管理,即RemotingManagement和WebServiceWrapperService,支援遠端管理控制檯除錯用;(2)外掛管理服務,即UIShell.BundleManagementService,支援對本地外掛管理和外掛倉庫訪問與下載;(3)頁面流服務,即UIShell.PageFlowService,用於獲取主介面。

下面我們來看一下App.xaml.cs原始碼,在這裡實現了外掛載入、啟動和進入主介面的功能。

複製程式碼
namespace UIShell.iOpenWorks.WPF
{
    /// <summary>
    /// WPF startup class.
    /// </summary>
    public partial class App : Application
    {
        // Use object type to avoid load UIShell.OSGi.dll before update.
        private object _bundleRuntime;

        public App()
        {
            UpdateCore();
            StartBundleRuntime();
        }

        void UpdateCore() // Update Core Files, including BundleRepositoryOpenAPI, PageFlowService and OSGi Core assemblies.
        {
            if (AutoUpdateCoreFiles)
            {
                new CoreFileUpdater().UpdateCoreFiles(CoreFileUpdateCheckType.Daily);
            }
        }

        void StartBundleRuntime() // Start OSGi Core.
        {
            var bundleRuntime = new BundleRuntime();
            bundleRuntime.AddService<Application>(this);
            bundleRuntime.Start();

            Startup += App_Startup;
            Exit += App_Exit;
            _bundleRuntime = bundleRuntime;
        }

        void App_Startup(object sender, StartupEventArgs e)
        {
            Application app = Application.Current;
            var bundleRuntime = _bundleRuntime as BundleRuntime;
            app.ShutdownMode = ShutdownMode.OnLastWindowClose;

            #region Get the main window
            var pageFlowService = bundleRuntime.GetFirstOrDefaultService<IPageFlowService>();
            if (pageFlowService == null)
            {
                throw new Exception("The page flow service is not installed.");
            }

            if (pageFlowService.FirstPageNode == null || string.IsNullOrEmpty(pageFlowService.FirstPageNode.Value))
            {
                throw new Exception("There is not first page node defined.");
            }

            var windowType = pageFlowService.FirstPageNodeOwner.LoadClass(pageFlowService.FirstPageNode.Value);
            if (windowType == null)
            {
                throw new Exception(string.Format("Can not load Window type '{0}' from Bundle '{1}'.", pageFlowService.FirstPageNode.Value, pageFlowService.FirstPageNodeOwner.SymbolicName));
            }

            app.MainWindow = System.Activator.CreateInstance(windowType) as Window;
            #endregion 

            app.MainWindow.Show();
        }

        void App_Exit(object sender, ExitEventArgs e)
        {
            if (_bundleRuntime != null)
            {
                var bundleRuntime = _bundleRuntime as BundleRuntime;
                bundleRuntime.Stop();
                _bundleRuntime = null;
            }
        }
        // Other codes
    }
}
複製程式碼

上述程式碼非常簡單,我將介紹一下每一個函式的功能。

(1)建構函式:呼叫UpdateCore和StartBundleRuntime;

(2)UpdateCore:呼叫BootStrapper程式集的CoreFileUpdater來實現核心檔案升級;

(3)StartBundleRuntime:建立一個BundleRuntime,即外掛框架,BundleRuntime預設建構函式指定的外掛目錄為Plugins;啟動BundleRuntime,即啟動外掛框架;掛載Startup和Exit事件;

(4)在App_Startup事件處理函式中,從外掛框架獲取PageFlowService服務,利用該服務獲取主介面,然後建立該介面例項,並執行;

(5)在App_Exit事件處理函式中,終止外掛框架,釋放資源。

3.3 基於ModernUI實現通用介面外掛框架

我在第2節描述了通用介面框架的思路。這個介面框架將基於OSGi.NET外掛框架三大功能之一——外掛擴充套件來實現。我將按照以下順序來描述實現。

3.3.1 OSGi.NET外掛擴充套件原理

下圖是OSGi.NET外掛擴充套件原理,在這裡,需要暴露擴充套件點的外掛暴露一個ExtensionPoint,提供擴充套件的外掛則宣告一個Extension(XML格式),如下所示。暴露擴充套件點的外掛通過OSGi.NET框架獲取所有Extension,然後對其進行處理。

外掛框架原理

依據第2節描述,通用介面框架外掛需要暴露擴充套件點和處理擴充套件。暴露擴充套件點意味著它需要定義介面擴充套件的格式。下面我來介紹擴充套件格式的XML定義。

3.3.2 介面擴充套件XML定義

根據介面框架要實現的功能,我們定義的擴充套件格式,如下所示。擴充套件點的名稱為UIShell.WpfShellPlugin.LinkGroups。通過LinkGroup來定義一級選單,通過Link來定義葉子節點選單,通過TabLink來定義三級選單的Tab佈局方式。

複製程式碼
<Extension Point="UIShell.WpfShellPlugin.LinkGroups">
  
  <LinkGroup DisplayName="一級選單" DefaultContentSource="預設顯示頁面">
    <Link DisplayName="二級選單" Source="二級選單頁面" />
    <TabLink DisplayName="三級選單Tab佈局" DefaultContentSource="預設頁面" Layout="List/Tab">
      <Link DisplayName="三級選單" Source="三級選單頁面" />
    </TabLink>
  </LinkGroup>
</Extension>
複製程式碼

介面框架外掛需要做的就是獲取這樣的XML定義,並且自動在介面上將元素創建出來並自動載入外掛提供的頁面。下面我來介紹介面框架如何實現。

3.3.3 介面框架的實現

介面框架基於ModernUI來實現,它需要完成:(1)為Extension建立擴充套件模型;(2)獲取所有擴充套件模型物件,並在主介面建立介面元素;(3)監聽擴充套件變更事件,動態變更介面元素。

首先,我們來看看擴充套件模型的構建。在這裡,定義了LinkGroupData、TabLinkData、LinkData分別對應於擴充套件的XML的元素。

image

這裡的ShellExtensionPointHandler物件則用於同OSGi.NET框架擴充套件擴充套件資訊,並將其轉換成擴充套件物件模型,然後儲存在LinkGroups屬性中。LinkGroups為ObservableCollection,當新增或者刪除LinkGroup時會丟擲Add/Remov事件。下面來看一下這個類的程式碼。

複製程式碼
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Xml;
using UIShell.OSGi;

namespace UIShell.WpfShellPlugin.ExtensionModel
{
    public class ShellExtensionPointHandler
    {
        public const string ExtensionPointName = "UIShell.WpfShellPlugin.LinkGroups";

        public IBundle Bundle { get; private set; }
        public ObservableCollection<LinkGroupData> LinkGroups { get; private set; }

        public ShellExtensionPointHandler(IBundle bundle)
        {
            Bundle = bundle;
            InitExtensions();
            if (Bundle.Context != null)
            {
                Bundle.Context.ExtensionChanged += Context_ExtensionChanged;
            }
        }

        void InitExtensions() // Init
        {
            if (Bundle.Context == null)
            {
                return;
            }
            // Get all extensions.
            var extensions = Bundle.Context.GetExtensions(ExtensionPointName);
            LinkGroups = new ObservableCollection<LinkGroupData>();

            // Convert extensions to LinkGroupData collection.
            foreach (var extension in extensions)
            {
                AddExtension(extension);
            }
        }

        // Handle ExtensionChanged event.
        void Context_ExtensionChanged(object sender, ExtensionEventArgs e)
        {
            if (e.ExtensionPoint.Equals(ExtensionPointName))
            {
                // Create LinkGroupData objects for new Extension.
                if (e.Action == CollectionChangedAction.Add)
                {
                    AddExtension(e.Extension);
                }
                else // Remove LinkGroupData objects respond to the Extension.
                {
                    RemoveExtension(e.Extension);
                }
            }
        }

        // Convert Extension to LinkGroupData instances.
        void AddExtension(Extension extension)
        {
            LinkGroupData linkGroup;
            foreach (XmlNode node in extension.Data)
            {
                if (node is XmlComment)
                {
                    continue;
                }
                linkGroup = new LinkGroupData(extension);
                linkGroup.FromXml(node);
                LinkGroups.Add(linkGroup);
            }
        }
        // Remove LinkGroupData instances of the Extension.
        void RemoveExtension(Extension extension)
        {
            var toBeRemoved = new List<LinkGroupData>();
            foreach (var linkGroup in LinkGroups)
            {
                if (linkGroup.Extension.Equals(extension))
                {
                    toBeRemoved.Add(linkGroup);
                }
            }
            foreach (var linkGroup in toBeRemoved)
            {
                LinkGroups.Remove(linkGroup);
            }
        }
    }
}
複製程式碼

這個類有以下幾個方法:

(1)InitExtensions:即從OSGi.NET框架獲取已經註冊的擴充套件資訊,將其轉換成LinkGroupData例項,並儲存;

(2)Context_ExtensionChanged事件處理函式:即當Extension被新增或者刪除時的處理函式,這在外掛安裝和解除安裝時發生,我們需要將新建的Extension轉換成LinkGroupData例項儲存起來,需要已刪除的Extension對應的LinkGroupData例項移除掉。

那接下來我們來看一下主介面如何根據擴扎模型來建立或者刪除介面元素。首先,你可以看到,這個主介面是空的沒有預先定義任何的介面元素。

image

那你一定猜到了,這個介面肯定是通過程式碼來動態建立介面元素,我們來看看程式碼先。

複製程式碼
namespace UIShell.WpfShellPlugin
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : ModernWindow
    {
        public static ShellExtensionPointHandler ShellExtensionPointHandler { get; set; }
        private List<Tuple<LinkGroupData, LinkGroup>> LinkGroupTuples { get; set; }
        
        public MainWindow()
        {
            InitializeComponent();
            LinkGroupTuples = new List<Tuple<LinkGroupData, LinkGroup>>();
            ShellExtensionPointHandler = new ShellExtensionPointHandler(BundleActivator.Bundle);
            ShellExtensionPointHandler.LinkGroups.CollectionChanged += LinkGroups_CollectionChanged;
            InitializeLinkGroupsForExtensions();
        }

        void InitializeLinkGroupsForExtensions()
        {
            foreach (var linkGroupData in ShellExtensionPointHandler.LinkGroups)
            {
                CreateLinkGroupForData(linkGroupData);
            }

            // 設定第一個頁面
            if (ShellExtensionPointHandler.LinkGroups.Count > 0)
            {
                var first = ShellExtensionPointHandler.LinkGroups[0];
                ContentSource = new Uri(first.FormatSource(first.DefaultContentSource), UriKind.RelativeOrAbsolute);
            }
        }

        void LinkGroups_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            Action action = () =>
            {
                if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
                {
                    // 新加了LinkGroupData
                    foreach (LinkGroupData item in e.NewItems)
                    {
                        CreateLinkGroupForData(item);
                    }
                }
                else if(e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
                {
                    // 刪除了LinkGroupData
                    foreach (LinkGroupData item in e.OldItems)
                    {
                        RemoveLinkGroupForData(item);
                    }
                }
            };
            
            Dispatcher.Invoke(action);
        }

        void CreateLinkGroupForData(LinkGroupData linkGroupData)
        {
            var linkGroup = new LinkGroup { DisplayName = linkGroupData.DisplayName, GroupName = linkGroupData.GroupName };
            foreach (var linkData in linkGroupData.Links)
            {
                if (linkData is LinkData)
                {
                    
                    linkGroup.Links.Add(new Link { DisplayName = linkData.DisplayName, Source = new Uri(linkData.FormatSource((linkData as LinkData).Source), UriKind.RelativeOrAbsolute) });
                }
                else if (linkData is TabLinkData)
                {
                    linkGroup.Links.Add(new Link { DisplayName = linkData.DisplayName, Source = new Uri("[email protected]Holder?LinkId=" + linkData.LinkId.ToString(), UriKind.RelativeOrAbsolute) });
                }
            }
            if (linkGroupData.IsTitleLink)
            {
                TitleLinks.Add(new Link { DisplayName = linkGroupData.DisplayName, Source = new Uri(linkGroupData.FormatSource(linkGroupData.DefaultContentSource), UriKind.RelativeOrAbsolute) });
            }
            MenuLinkGroups.Add(linkGroup);
            LinkGroupTuples.Add(new Tuple<LinkGroupData, LinkGroup>(linkGroupData, linkGroup));
        }

        void RemoveLinkGroupForData(LinkGroupData linkGroupData)
        {
            var tuple = LinkGroupTuples.Find(t => t.Item1.Equals(linkGroupData));
            if (tuple != null)
            {
                MenuLinkGroups.Remove(tuple.Item2);
                LinkGroupTuples.Remove(tuple);
            }
        }
    }
}
複製程式碼

上面的程式碼也很簡單,邏輯很清晰,我來說明一下各個方法的用處:

(1)InitializeLinkGroupsForExtensions:獲取擴充套件模型物件,並將物件轉換成介面元素LinkGroup,然後監聽擴充套件模型變更事件;

(2)LinkGroups_CollectionChanged:擴充套件模型變更事件,當有擴充套件物件新增時,需要新增新的介面元素;反之,則需要移除介面元素;

(3)CreateLinkGroupForData:為擴充套件模型建立介面元素LinkGroup;

(4)RemoveLinkGroupForData:當擴充套件模型被刪除時,需要將對應的介面元素刪除掉。

為了支援外掛化,還需要為ModernUI做一個變更,下面我將來介紹。

3.4 ModernUI外掛化支撐所做的變更

為了支援外掛化,我需要對ModernUI的ContentLoader進行擴充套件,使其支援直接從外掛載入內容頁面。詳細檢視以下程式碼。

複製程式碼
/// <summary>
/// Loads the content from specified uri.
/// </summary>
/// <param name="uri">The content uri</param>
/// <returns>The loaded content.</returns>
protected virtual object LoadContent(Uri uri)
{
    // don't do anything in design mode
    if (ModernUIHelper.IsInDesignMode)
    {
        return null;
    }
    string uriString = string.Empty;
    string paraString = string.Empty;
    Dictionary<string, string> parameters = new Dictionary<string, string>();
    if (uri.OriginalString.Contains('?'))
    {
        var uriPara = uri.OriginalString.Split('?');
        uriString = uriPara[0];
        paraString = uriPara[1];
        var parameterStrs = paraString.Split('&');
        string[] parameterStrSplitted;
        foreach (var parameterStr in parameterStrs)
        {
            parameterStrSplitted = parameterStr.Split('=');
            parameters.Add(parameterStrSplitted[0], parameterStrSplitted[1]);
        }
    }
    else
    {
        uriString = uri.OriginalString;
    }

    object result = null;
    // 1st Format: [BundleSymbolicName]@[Class Full Name]
    if (uriString.Contains('@'))
    {
        var bundleSymbolicNameAndClass = uriString.Split('@');
        if (bundleSymbolicNameAndClass.Length != 2 || string.IsNullOrEmpty(bundleSymbolicNameAndClass[0]) || string.IsNullOrEmpty(bundleSymbolicNameAndClass[1]))
        {
            throw new Exception("The uri must be in format of '[BundleSymbolicName]@[Class Full Name]'");
        }
        var bundle = BundleRuntime.Instance.Framework.Bundles.GetBundleBySymbolicName(bundleSymbolicNameAndClass[0]);
        if (bundle == null)
        {
            throw new Exception(string.Format("The uri is not correct since the bunde '{0}' does not exist.", bundleSymbolicNameAndClass[0]));
        }
        var type = bundle.LoadClass(bundleSymbolicNameAndClass[1]);
        if (type == null)
        {
            throw new Exception(string.Format("The class '{0}' is not found in bunle '{1}'.", bundleSymbolicNameAndClass[1], bundleSymbolicNameAndClass[0]));
        }
        result = Activator.CreateInstance(type);
    }
    // 2nd Format: /[AssemblyName],Version=[Version];component/[XAML relative path]
    else if (string.IsNullOrEmpty(paraString))
    {
        result = Application.LoadComponent(uri);
    }
    else
    {
        result = Application.LoadComponent(new Uri(uriString, UriKind.RelativeOrAbsolute));
    }

    ApplyProperties(result, parameters);

    return result;
}
複製程式碼

這集成了預設的載入行為,同時支援:(1)以“[BundleSymbolicName]@[PageClassName]”方式支援內容載入;(2)支援WPF傳統資源載入方式;(3)支援引數化。

另外,為了實現三級選單,我定義了一個ContentPlaceHolder,它用於獲取第三級的選單,並建立內容,程式碼如下。

複製程式碼
namespace UIShell.WpfShellPlugin.Pages
{
    /// <summary>
    /// ContentPlaceHolder.xaml 的互動邏輯
    /// </summary>
    public partial class ContentPlaceHolder : UserControl
    {
        private string _linkId = string.Empty;
        private FirstFloor.ModernUI.Windows.Controls.ModernTab _tab;
        public string LinkId
        {
            get
            {
                return _linkId;
            }
            set
            {
                _linkId = value;
                TabLinkData tabLinkData = null;
                foreach (var linkGroupData in MainWindow.ShellExtensionPointHandler.LinkGroups)
                {
                    foreach (var link in linkGroupData.Links)
                    {
                        if (link.LinkId.ToString().Equals(_linkId, StringComparison.OrdinalIgnoreCase))
                        {
                            tabLinkData = link as TabLinkData;
                            break;
                        }
                    }
                }

                if (tabLinkData != null)
                {
                    _tab.SelectedSource = new Uri(tabLinkData.FormatSource(tabLinkData.DefaultContentSource), UriKind.RelativeOrAbsolute);
                    _tab.Layout = (TabLayout)Enum.Parse(typeof(TabLayout), tabLinkData.Layout);
                    foreach(var linkData in tabLinkData.Links)
                    {
                        _tab.Links.Add(new Link { DisplayName = linkData.DisplayName, Source = new Uri(linkData.FormatSource(linkData.Source), UriKind.RelativeOrAbsolute) });
                    }
                }
            }
        }
        public ContentPlaceHolder()
        {
            InitializeComponent();
            _tab = FindName("ModernTab") as FirstFloor.ModernUI.Windows.Controls.ModernTab;
        }
    }
}
複製程式碼

它利用傳遞的引數可以獲取對應的三級選單的擴充套件模型,然後建立對應的介面元素。

到此,我們已經成功實現了整個外掛化的介面框架了,文章有點長,能堅持看到這的基本屬於勇士了~~,接下來還想用一點點篇幅演示一下介面框架動態性。

4 動態性演示

OSGi.NET動態性支援允許我們在程式執行中來安裝、啟動、停止、解除安裝和更新外掛,請看下圖。當你執行下載的程式時,最開始會展示以下選單,其中“演示11、演示12”選單由DemoPlugin外掛註冊,“演示3”由DemoPlugin2外掛提供,此時,你執行一下遠端管理控制檯,輸入list指令後,可以發現這兩個外掛都是Active狀態。

image

下面我們輸入“stop 2”指令,將DemoPlugin外掛停止,如下圖所示,此時你可以發現DemoPlugin註冊的選單已經動態的從主介面中被移除掉了。

image

同樣,你還可以繼續嘗試Start、Stop、Install、Uninstall等指令來動態更改外掛狀態,從而影響應用程式的行為。

當然,你也可以通過“外掛管理”來實現對核心安裝的外掛的狀態變更,如下所示。

image

再進一步,你可以直接訪問外掛倉庫來安裝更多的外掛。你可以在原始碼中檢視到如何實現外掛管理和外掛倉庫訪問及下載安裝外掛的程式碼。

image

怎樣,很強大吧!!如果我們構建了這樣的通用框架,以後開發起來那簡單多了。當然,如果你還有興趣的話,你可以再嘗試瞭解基於外掛的一鍵部署和自動化升級,我在《分享讓你震驚的自動化升級和部署方案,讓我們一起來PK一下!》這篇文章介紹了。

好了~,非常感謝這麼耐心看完這篇文章。該附上原始碼了~。

原始碼下載 點選下載。

相關推薦

[.Net]分享非常漂亮WPF介面框架原始碼外掛實現原理

在上文《分享一個非常漂亮的WPF介面框架》中我簡單的介紹了一個介面框架,有朋友已經指出了,這個介面框架是基於ModernUI來實現的,在該文我將分享所有的原始碼,並詳細描述如何基於ModernUI來構造一個非常通用的、外掛化的WPF開發框架。下載原始碼的同志,希望點選一下推薦。 本文將按照以下四點來介紹:

分享非常漂亮WPF介面框架原始碼外掛實現原理

本文將按照以下四點來介紹: (1)ModernUI簡介; (2)構建通用介面框架的思路; (3)基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析; (4)其它更有趣的東西~~。 1 ModernUI簡介 ModernUI(http:

【.Net】C#反射獲取 所有欄位 私有欄位

using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace ReflectionDemo {

【.NetWPF介面設計—擼大師

WPF介面設計,模仿了金山衛士,360,魯大師的介面! <!--無邊框窗體-->     <Stylex:Key="NoResize_window"TargetType="{x:Type Window}">     <Set

[.Net][WPF] ListView 中 View 的模板替換(新增/刪除列)

===================================================== 今天在網上閒逛,突然發現這邊文章竟然被人毫不尊重原作者權益而胡亂轉載,讓人氣憤。 本著知識共享的目的,歡迎大家轉載,但是轉載請保留本文的原始連結,謝謝!  本文原作者:YuanHui =====

wpf筆記(讀一線8天入門wpf)

type ret adding combobox 延伸 new listbox pen value wpf筆記(讀一線碼農8天入門wpf) ****************基礎****************●引入命名空間 xmlns:local="clr-namespa

分享WOW.js – 讓頁面滾動更有趣

簡介 有的頁面在向下滾動的時候,有些元素會產生細小的動畫效果。雖然動畫比較小,但卻能吸引你的注意。比如剛剛釋出的 iPhone 6 的頁面(檢視)。如果你希望你的頁面也更加有趣,那麼你可以試試 WOW.js。 WOW.js 依賴 animate.css,所以它支援 a

【.Net】C#中的partial class(部分類)

C# 2.0 可以將類、結構或介面的定義拆分到兩個或多個原始檔中,在類宣告前新增partial關鍵字即可。 例如:下面的PartialTest類 class PartialTest { string Str_FieldTest; int Int_FieldTes

【.Net】C#.net檔案批量上傳解決方案附下載(swfupload)2015-8-28更新

因為最近專案需要多檔案同時上傳所以自己在網上找了下方法。swfupload做到了,所以我把我的C#.net環境的多檔案同時上傳共享給大家!(本例項最大隻能上傳500M的資料,如需要上傳更大的下面會告訴大家如何設定) 功能完全支援ie和firefox瀏覽器! 一般的WEB方式檔案上傳只能一個一個的進行上傳

【.Net】WebBrowser與IE的關係,如何設定WebBrowser工作在IE9模式下?

一.問題的提出 偶然發現,Winform裡的WebBrowser和IE實際安裝的版本似乎並不同步,很有趣! 下面有張圖,裡面一個視窗是用IE9開啟某網站,另一個視窗是用Winform+WebBrowser開啟同樣的網站,有意思的事情出現了。   在IE9視窗中,這個網站左邊選單樹無法顯示,原因是IE9使用

【.Net】修改WebBrowser控制元件的核心解決方案

首先說一下原理 當下很大瀏覽器他們都是用了IE的core, 這個core只提供HTML/JS的執行和渲染,並沒有給出關於介面和一些特性上的事,所以開發自己瀏覽器如果基於IE core需要自己完成這些內容。 一張圖很好的說明了這個情況,IE瀏覽器的架構:http://msdn.microsoft.com/e

【.Net】C# 啟動EXE檔案帶啟動引數EXE

(一)、先製作一個帶啟動引數的EXE檔案。  步驟:             1、定義全域性私有變數:private string[] s = new string[1];  //這裡為了簡單起見,只做一個引數            2、  在窗體的建構

小汪-SQL 圖形介面設計約束,unqiue,check Default Foreign

怎麼說呢,這個東西並不是經常的使用,慢慢的記不住怎麼玩得啦,網上找的資料,複習複習哈! SQLServer中有五種約束,Primary Key約束、Foreign Key約束、Unique約束、Default約束和Check約束,今天使用SQL 1、Pri

【.Net】ABP學習目錄傳送門

https://aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template入門教程http://www.cnblogs.com/mienreal/p/4528470.htmlhttps://www.cnblogs.c

【.Net】DataGrid 資料繫結使用小結一

  做過ASP.NET開發的朋友都知道,ASP.NET提供了強大的列表資料控制元件,從ASP.NET1.1 時代的DataGrid,到ASP.NET2.0時代的GridView,再到ASP.NET3.5時代的ListView,功能越 來越強大,使用起來也非常的靈活.在Silverlight2中,同樣提供了一

[.Net].net呼叫外部exe檔案

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text

【.Net】【NPOI】NPOI對Excel的操作(Sheet轉DataTable、List)

通過NPOI對Excel進行操作,這裡主要是讀取的操作。封裝到ExcelHelper操作類中。 using System.Collections.Generic; using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.U

【.Net】在 Web 窗體上報表檢視器 Web 控制元件需要 System.Web.UI.ScriptManager

異常詳細資訊: Microsoft.Reporting.WebForms.ScriptManagerNotFoundException: 在 Web 窗體上報表檢視器 Web 控制元件需要 System.Web.UI.ScriptManager。 源錯誤: [沒有相

【.Net】初試T4模板

工具下載地址:【二選一 個人喜歡用 devart T4 Editor for Visual Studio 小巧 有些朋友 從網上下載外掛後 開啟專案 還是顯示純黑字型 沒有著色 和提示功能,但新建專案 和 新建模板 又正常解答【把你的老專案 升級上來

[.Net]MVC——入門+最簡單的小例項

MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controler)的縮寫,一種軟體設計典範,用於組織程式碼用一種業務邏輯和資料顯示分離的方法。 1.最上面直接面向用戶的"檢視層"(View)。它是提供