国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
分享非常漂亮的WPF界面框架源碼及插件化實(shí)現(xiàn)原理
隨筆-64 文章-0 評(píng)論-3197 

分享非常漂亮的WPF界面框架源碼及插件化實(shí)現(xiàn)原理

在上文《分享一個(gè)非常漂亮的WPF界面框架》中我簡單的介紹了一個(gè)界面框架,有朋友已經(jīng)指出了,這個(gè)界面框架是基于ModernUI來實(shí)現(xiàn)的,在該文我將分享所有的源碼,并詳細(xì)描述如何基于ModernUI來構(gòu)造一個(gè)非常通用的、插件化的WPF開發(fā)框架。下載源碼的同志,希望點(diǎn)擊一下推薦。

 

本文將按照以下四點(diǎn)來介紹:

(1)ModernUI簡介;

(2)構(gòu)建通用界面框架的思路;

(3)基于ModernUI和OSGi.NET的插件化界面框架實(shí)現(xiàn)原理及源碼分析;

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

 

1 ModernUI簡介

ModernUI(http://mui.codeplex.com/)是一個(gè)開源的WPF界面庫,利用該界面庫,我們可以創(chuàng)建很酷的應(yīng)用程序。下面是ModernUI官方示例,你可以從官方網(wǎng)站直接下載源碼運(yùn)行,如果是.NET 4.0的話,記得要聲明“NET4”預(yù)編譯變量,否則無法編譯通過。

要編寫這樣的WPF界面,我們需要在一個(gè)Window上聲明菜單和Tab頁面,下圖是定義菜單的聲明。

此外,每一個(gè)Tab風(fēng)格頁面,你也需要手動(dòng)的為菜單創(chuàng)建這樣的界面元素。

 

直接用這樣的方式來使用ModernUI,顯然不太適合團(tuán)隊(duì)協(xié)作性的并行開發(fā),因?yàn)樵谝粋€(gè)團(tuán)隊(duì)的協(xié)作中,不同的人需要完成不同的功能,實(shí)現(xiàn)不同頁面,每個(gè)人都需要來更改主界面。

 

我非常希望模塊化的開發(fā)方法,因?yàn)檫@可以盡可能的復(fù)用現(xiàn)有資產(chǎn),使程序員可以聚焦在自己關(guān)注的業(yè)務(wù)邏輯上,不需要關(guān)心UI的使用。下面,我將來描述基于ModernUI實(shí)現(xiàn)的一個(gè)通用界面框架,這個(gè)界面框架允許程序員在自己的業(yè)務(wù)模塊中配置需要顯示的界面元素。

 

2 通用界面框架實(shí)現(xiàn)思路

我希望能夠?qū)崿F(xiàn)這樣的通用界面框架:

(1)程序員可以直接實(shí)現(xiàn)需要展現(xiàn)業(yè)務(wù)邏輯的界面,不需要關(guān)注如何使用ModernUI;

(2)程序員可以通過簡單的配置就可以將自己實(shí)現(xiàn)的業(yè)務(wù)邏輯頁面顯示在主界面中;

(3)這個(gè)界面框架可以完全復(fù)用。

 

當(dāng)我看到ModernUI這個(gè)界面庫時(shí),我希望將應(yīng)用程序做成模塊化,每一個(gè)模塊能夠:

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

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

這樣做的好處是,開發(fā)插件的時(shí)候可以不需要關(guān)心界面框架插件;團(tuán)隊(duì)在協(xié)作開發(fā)應(yīng)用的時(shí)候,可以獨(dú)立開發(fā)并不需要修改主界面;團(tuán)隊(duì)成員的插件可以隨時(shí)集成到這個(gè)主界面;當(dāng)主界面無法滿足我們的布局時(shí)或者用戶需求無法滿足時(shí),可以直接替換主界面框架而不需要修改任何插件代碼。

 

最終的效果如下,以下界面的幾個(gè)菜單及點(diǎn)擊菜單顯示的內(nèi)容由DemoPlugin插件、DemoPlugin2插件來提供。當(dāng)插件框架加載更多插件時(shí),界面上會(huì)出現(xiàn)更多的菜單;反之,當(dāng)插件被卸載或者被停止時(shí),則相應(yīng)的菜單將消失掉。

      

下面我來介紹如何實(shí)現(xiàn)。

 

3 基于ModernUI和OSGi.NET的插件化界面框架實(shí)現(xiàn)原理及源碼分析

 

3.1 OSGi.NET插件框架原理簡介

OSGi.NET框架是一個(gè)完全通用的.NET插件框架,它支持WPF、WinForm、ASP.NET、ASP.NET MVC 3.0/4.0、控制臺(tái)等任意.NET應(yīng)用程序,也就是說,你可以基于該插件框架來快速構(gòu)架插件化的應(yīng)用程序。OSGi.NET插件框架提供了插件化支持、插件擴(kuò)展和面向服務(wù)支持三大功能。

 

OSGi.NET插件框架啟動(dòng)時(shí),從插件目錄中搜索插件,安裝并啟動(dòng)這些插件,將這些插件組裝在插件框架中;一個(gè)插件可以暴露擴(kuò)展點(diǎn),允許其它插件在不更改其代碼情況下,擴(kuò)展該插件的功能;插件間可以通過服務(wù)來進(jìn)行通訊。

 

在一個(gè)插件應(yīng)用程序中,它首先要獲取一個(gè)入口點(diǎn),這個(gè)入口點(diǎn)由一個(gè)插件來提供,然后進(jìn)入這個(gè)插件的入口并運(yùn)行起來。一個(gè)提供入口的插件通常是一個(gè)主界面插件,比如上面介紹的這個(gè)WPF界面框架。也就是說,插件應(yīng)用程序啟動(dòng)起來后,會(huì)先運(yùn)行這個(gè)界面框架的主界面。而主界面一般都提供了關(guān)于界面元素的擴(kuò)展,允許其它插件將菜單、導(dǎo)航和內(nèi)容頁面注冊到主界面,因此,當(dāng)主界面運(yùn)行時(shí),它會(huì)將其它插件注冊的界面元素顯示出來。當(dāng)用戶點(diǎn)擊界面元素時(shí),插件框架就會(huì)加載這個(gè)插件的頁面,某個(gè)插件的頁面在呈現(xiàn)時(shí),則有可能會(huì)從數(shù)據(jù)庫中提取數(shù)據(jù)展示,這時(shí)候,該插件則可能會(huì)調(diào)用數(shù)據(jù)訪問服務(wù)提供的通用數(shù)據(jù)訪問接口。OSGi.NET提供的三大功能,剛好能夠非常的吻合這樣的系統(tǒng)的啟動(dòng)形式。當(dāng)然,OSGi.NET除了提供插件三大支撐功能之外,它還支持插件動(dòng)態(tài)性與隔離性。動(dòng)態(tài)性,意味著我們可以在運(yùn)行時(shí)來動(dòng)態(tài)安裝、啟動(dòng)、停止、卸載和更新插件,而隔離性則意味著每一個(gè)插件都擁有自己獨(dú)立的目錄,有自己獨(dú)立的類型加載器和類型空間。

 

基于OSGi.NET插件框架,我們很容易實(shí)現(xiàn)插件的動(dòng)態(tài)安裝、遠(yuǎn)程管理、自動(dòng)化部署、自動(dòng)升級(jí)和應(yīng)用商店。下面,我來描述如何使用OSGi.NET來構(gòu)建一個(gè)WPF插件應(yīng)用。

 

3.2 基于OSGi.NET來實(shí)現(xiàn)WPF插件應(yīng)用

利用OSGi.NET來創(chuàng)建一個(gè)WPF插件應(yīng)用非常的簡單。只需要實(shí)現(xiàn):(1)創(chuàng)建一個(gè)插件主程序,定義插件目錄;(2)在主程序中利用BootStrapper實(shí)現(xiàn)OSGi.NET內(nèi)核升級(jí)檢測與自動(dòng)升級(jí);(3)啟動(dòng)插件框架;(4)利用PageFlowService獲取主界面,然后運(yùn)行主界面。下面我們看一下插件主程序。(注:如果你安裝了OSGi.NET框架,可以直接使用項(xiàng)目模板來創(chuàng)建WPF主程序項(xiàng)目。)

在這個(gè)主程序,我們在項(xiàng)目的屬性將輸出路徑改為bin,并在bin目錄下創(chuàng)建一個(gè)Plugins目錄,然后將OSGi.NET四個(gè)標(biāo)準(zhǔn)插件拷貝到Plugins目錄,它們分別用于:(1)插件遠(yuǎn)程管理,即RemotingManagement和WebServiceWrapperService,支持遠(yuǎn)程管理控制臺(tái)調(diào)試用;(2)插件管理服務(wù),即UIShell.BundleManagementService,支持對(duì)本地插件管理和插件倉庫訪問與下載;(3)頁面流服務(wù),即UIShell.PageFlowService,用于獲取主界面。

 

下面我們來看一下App.xaml.cs源碼,在這里實(shí)現(xiàn)了插件加載、啟動(dòng)和進(jìn)入主界面的功能。

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    }}

上述代碼非常簡單,我將介紹一下每一個(gè)函數(shù)的功能。

(1)構(gòu)造函數(shù):調(diào)用UpdateCore和StartBundleRuntime;

(2)UpdateCore:調(diào)用BootStrapper程序集的CoreFileUpdater來實(shí)現(xiàn)內(nèi)核文件升級(jí);

(3)StartBundleRuntime:創(chuàng)建一個(gè)BundleRuntime,即插件框架,BundleRuntime默認(rèn)構(gòu)造函數(shù)指定的插件目錄為Plugins;啟動(dòng)BundleRuntime,即啟動(dòng)插件框架;掛載Startup和Exit事件;

(4)在App_Startup事件處理函數(shù)中,從插件框架獲取PageFlowService服務(wù),利用該服務(wù)獲取主界面,然后創(chuàng)建該界面實(shí)例,并運(yùn)行;

(5)在App_Exit事件處理函數(shù)中,終止插件框架,釋放資源。

 

3.3 基于ModernUI實(shí)現(xiàn)通用界面插件框架

我在第2節(jié)描述了通用界面框架的思路。這個(gè)界面框架將基于OSGi.NET插件框架三大功能之一——插件擴(kuò)展來實(shí)現(xiàn)。我將按照以下順序來描述實(shí)現(xiàn)。

 

3.3.1 OSGi.NET插件擴(kuò)展原理

下圖是OSGi.NET插件擴(kuò)展原理,在這里,需要暴露擴(kuò)展點(diǎn)的插件暴露一個(gè)ExtensionPoint,提供擴(kuò)展的插件則聲明一個(gè)Extension(XML格式),如下所示。暴露擴(kuò)展點(diǎn)的插件通過OSGi.NET框架獲取所有Extension,然后對(duì)其進(jìn)行處理。

依據(jù)第2節(jié)描述,通用界面框架插件需要暴露擴(kuò)展點(diǎn)和處理擴(kuò)展。暴露擴(kuò)展點(diǎn)意味著它需要定義界面擴(kuò)展的格式。下面我來介紹擴(kuò)展格式的XML定義。

 

3.3.2 界面擴(kuò)展XML定義

根據(jù)界面框架要實(shí)現(xiàn)的功能,我們定義的擴(kuò)展格式,如下所示。擴(kuò)展點(diǎn)的名稱為UIShell.WpfShellPlugin.LinkGroups。通過LinkGroup來定義一級(jí)菜單,通過Link來定義葉子節(jié)點(diǎn)菜單,通過TabLink來定義三級(jí)菜單的Tab布局方式。

<Extension Point="UIShell.WpfShellPlugin.LinkGroups">    <LinkGroup DisplayName="一級(jí)菜單" DefaultContentSource="默認(rèn)顯示頁面">    <Link DisplayName="二級(jí)菜單" Source="二級(jí)菜單頁面" />    <TabLink DisplayName="三級(jí)菜單Tab布局" DefaultContentSource="默認(rèn)頁面" Layout="List/Tab">      <Link DisplayName="三級(jí)菜單" Source="三級(jí)菜單頁面" />    </TabLink>  </LinkGroup></Extension>

界面框架插件需要做的就是獲取這樣的XML定義,并且自動(dòng)在界面上將元素創(chuàng)建出來并自動(dòng)加載插件提供的頁面。下面我來介紹界面框架如何實(shí)現(xiàn)。

 

3.3.3 界面框架的實(shí)現(xiàn)

界面框架基于ModernUI來實(shí)現(xiàn),它需要完成:(1)為Extension創(chuàng)建擴(kuò)展模型;(2)獲取所有擴(kuò)展模型對(duì)象,并在主界面創(chuàng)建界面元素;(3)監(jiān)聽擴(kuò)展變更事件,動(dòng)態(tài)變更界面元素。

 

首先,我們來看看擴(kuò)展模型的構(gòu)建。在這里,定義了LinkGroupData、TabLinkData、LinkData分別對(duì)應(yīng)于擴(kuò)展的XML的元素。

這里的ShellExtensionPointHandler對(duì)象則用于同OSGi.NET框架擴(kuò)展擴(kuò)展信息,并將其轉(zhuǎn)換成擴(kuò)展對(duì)象模型,然后存儲(chǔ)在LinkGroups屬性中。LinkGroups為ObservableCollection,當(dāng)添加或者刪除LinkGroup時(shí)會(huì)拋出Add/Remov事件。下面來看一下這個(gè)類的代碼。

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);            }        }    }}

這個(gè)類有以下幾個(gè)方法:

(1)InitExtensions:即從OSGi.NET框架獲取已經(jīng)注冊的擴(kuò)展信息,將其轉(zhuǎn)換成LinkGroupData實(shí)例,并保存;

(2)Context_ExtensionChanged事件處理函數(shù):即當(dāng)Extension被添加或者刪除時(shí)的處理函數(shù),這在插件安裝和卸載時(shí)發(fā)生,我們需要將新建的Extension轉(zhuǎn)換成LinkGroupData實(shí)例保存起來,需要已刪除的Extension對(duì)應(yīng)的LinkGroupData實(shí)例移除掉。

 

那接下來我們來看一下主界面如何根據(jù)擴(kuò)扎模型來創(chuàng)建或者刪除界面元素。首先,你可以看到,這個(gè)主界面是空的沒有預(yù)先定義任何的界面元素。

那你一定猜到了,這個(gè)界面肯定是通過代碼來動(dòng)態(tài)創(chuàng)建界面元素,我們來看看代碼先。

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);            }            // 設(shè)置第一個(gè)頁面            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("UIShell.WpfShellPlugin@UIShell.WpfShellPlugin.Pages.ContentPlaceHolder?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);            }        }    }}

上面的代碼也很簡單,邏輯很清晰,我來說明一下各個(gè)方法的用處:

(1)InitializeLinkGroupsForExtensions:獲取擴(kuò)展模型對(duì)象,并將對(duì)象轉(zhuǎn)換成界面元素LinkGroup,然后監(jiān)聽擴(kuò)展模型變更事件;

(2)LinkGroups_CollectionChanged:擴(kuò)展模型變更事件,當(dāng)有擴(kuò)展對(duì)象添加時(shí),需要添加新的界面元素;反之,則需要移除界面元素;

(3)CreateLinkGroupForData:為擴(kuò)展模型創(chuàng)建界面元素LinkGroup;

(4)RemoveLinkGroupForData:當(dāng)擴(kuò)展模型被刪除時(shí),需要將對(duì)應(yīng)的界面元素刪除掉。

為了支持插件化,還需要為ModernUI做一個(gè)變更,下面我將來介紹。

 

3.4 ModernUI插件化支撐所做的變更

為了支持插件化,我需要對(duì)ModernUI的ContentLoader進(jìn)行擴(kuò)展,使其支持直接從插件加載內(nèi)容頁面。詳細(xì)查看以下代碼。

/// <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;}

這集成了默認(rèn)的加載行為,同時(shí)支持:(1)以“[BundleSymbolicName]@[PageClassName]”方式支持內(nèi)容加載;(2)支持WPF傳統(tǒng)資源加載方式;(3)支持參數(shù)化。

 

另外,為了實(shí)現(xiàn)三級(jí)菜單,我定義了一個(gè)ContentPlaceHolder,它用于獲取第三級(jí)的菜單,并創(chuàng)建內(nèi)容,代碼如下。

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;        }    }}

它利用傳遞的參數(shù)可以獲取對(duì)應(yīng)的三級(jí)菜單的擴(kuò)展模型,然后創(chuàng)建對(duì)應(yīng)的界面元素。

 

到此,我們已經(jīng)成功實(shí)現(xiàn)了整個(gè)插件化的界面框架了,文章有點(diǎn)長,能堅(jiān)持看到這的基本屬于勇士了~~,接下來還想用一點(diǎn)點(diǎn)篇幅演示一下界面框架動(dòng)態(tài)性。

 

4 動(dòng)態(tài)性演示

OSGi.NET動(dòng)態(tài)性支持允許我們在程序運(yùn)行中來安裝、啟動(dòng)、停止、卸載和更新插件,請看下圖。當(dāng)你運(yùn)行下載的程序時(shí),最開始會(huì)展示以下菜單,其中“演示11、演示12”菜單由DemoPlugin插件注冊,“演示3”由DemoPlugin2插件提供,此時(shí),你運(yùn)行一下遠(yuǎn)程管理控制臺(tái),輸入list指令后,可以發(fā)現(xiàn)這兩個(gè)插件都是Active狀態(tài)。

下面我們輸入“stop 2”指令,將DemoPlugin插件停止,如下圖所示,此時(shí)你可以發(fā)現(xiàn)DemoPlugin注冊的菜單已經(jīng)動(dòng)態(tài)的從主界面中被移除掉了。

同樣,你還可以繼續(xù)嘗試Start、Stop、Install、Uninstall等指令來動(dòng)態(tài)更改插件狀態(tài),從而影響應(yīng)用程序的行為。

當(dāng)然,你也可以通過“插件管理”來實(shí)現(xiàn)對(duì)內(nèi)核安裝的插件的狀態(tài)變更,如下所示。

再進(jìn)一步,你可以直接訪問插件倉庫來安裝更多的插件。你可以在源碼中查看到如何實(shí)現(xiàn)插件管理和插件倉庫訪問及下載安裝插件的代碼。

怎樣,很強(qiáng)大吧??!如果我們構(gòu)建了這樣的通用框架,以后開發(fā)起來那簡單多了。當(dāng)然,如果你還有興趣的話,你可以再嘗試了解基于插件的一鍵部署和自動(dòng)化升級(jí),我在《分享讓你震驚的自動(dòng)化升級(jí)和部署方案,讓我們一起來PK一下!》這篇文章介紹了。

 

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

 

有任何技術(shù)問題,可以加入iOpenWorks插件倉庫平臺(tái)QQ交流群: 121369588。

本文基于Creative Commons Attribution 2.5 China Mainland License發(fā)布,歡迎轉(zhuǎn)載,演繹或用于商業(yè)目的,但是必須保留本文的署名道法自然(包含鏈接)。如您有任何疑問或者授權(quán)方面的協(xié)商,請給我留言。

 

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
WPF漂亮界面框架實(shí)現(xiàn)原理分析及源碼分享
RAP 與 RCP的差別 - 好好生活,努力工作,天天向上! - BlogJava
白話插件框架原理
最簡單強(qiáng)大的插件框架
前端三大框架(vue,angular,react)
OSGi Bundle Repository(OBR)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服