精华内容
下载资源
问答
  • 由於技術轉型, 目前大部分工作都是WPF為主, 但是趨於如今想在網絡上找一套能夠滿意的WPF權限管理框架太難, 因為WinForm那時候是有一套改寫過的權限框架, 所以數據庫設計這塊已經有了一個成熟的設計,至於WPF客戶端...

    前言

    由於技術轉型, 目前大部分工作都是WPF為主, 但是趨於如今想在網絡上找一套能夠滿意的WPF權限管理框架太難, 因為WinForm那時候是有一套改寫過的權限框架,

    所以數據庫設計這塊已經有了一個成熟的設計,至於WPF客戶端這塊,技術選型也基本確定:MVVM模式, Entity,界面層的UI就使用開源的MaterialDesign組件。

     

    DB Design

    根據目前的設計中, 整體框架有:模板管理、菜單管理、用戶管理、權限分類、組與組用戶、組權限、系統日誌、數據字典。如下是整個PowerDesign的設計結構:

    注: 下面添加的一部分編碼、附件則是用於擴展的結構。核心的則是由上面介紹的幾個模塊組成

     

    权限模块构成

     

     

     

    权限控制的原理

    以一个简单的功能举个例子, 假设用户管理窗口具备一下的功能,每个功能都对应相应的权限值:

    当注册的用户登录时,首次会给其设置组, 所以我们只需要给组分配指定的访问权限, 首先,假设给当前分配的组设置权限为(新增、编辑、删除), 对应的权限值则是: 1+2+4=7。 那么用户登陆后就取得对应的权限值 7 , 当打开用户管理时, 针对指定的功能进行权限值逻辑运算。如下:

     

      当进行运算失败, 则选择隐藏或者禁用该功能。

     

    ----模块设计理念

    權限模塊

    由菜單功能細分到每個功能按鈕的權限控制、所有的功能定義在權限分類表中。最後通過組的方式進行用戶的權限配置。

     

    日誌級別

    通過日誌字段表定義需要進行日誌記錄的Table,根據數據庫配置的日誌字段,細分到字段級別的日誌管理。

     

    字典管理

    一個字典表, 控制所有的字典類型數據, 便於集中管理

     

    編碼規則

    針對不同的業務, 生成指定規則的編碼流水號。

    转载于:https://www.cnblogs.com/zh7791/p/9549933.html

    展开全文
  • 首页介绍: 下图为项目运行首页图片, 大的结构分为三块: 1.Header首部模块(存放通知组件[全局通知、消息管理 ]、扩展模块[皮肤、系统设置、关于作者、退出系统]) 2.Left左侧菜单模块(存放分配的功能模块) ...

    首页介绍:

    下图为项目运行首页图片, 大的结构分为三块:

    1.Header首部模块(存放通知组件[全局通知、消息管理 ]、扩展模块[皮肤、系统设置、关于作者、退出系统])

    2.Left左侧菜单模块(存放分配的功能模块)

    3.Center容器模块(存储/操作相关功能的模块)

    注: 所有的模块都以自定义模块的形式进行装载与动态加载。下面介绍的详细内容都是基于源代码进行介绍, 

    开源项目地址 (进行下载)https://gitee.com/zhgg666/publicWpf

     

    1.左侧菜单(MainLeftMenu)

    项目位置(url)  :  /Common/UserControls/MainLeftMenu.xaml

    关联Class(url) : /Common/CoreLib/Module/ModuleManager.cs

    绑定元素:ModuleGroups

    核心功能: 关联分配的所有功能, 存储功能的信息[代码、权限值、命名控件等] 

     

    2.分页组件(UcDataPager)

     项目位置(url)  :  /Common/UserControls/Common/UcDataPager.xaml

    关联Class(url) : /Interface/Base/IDataPager.cs

     核心功能: 控制模块表单分页、显示、查询等功能

     

    3.容器组件(MainTabControl)

     项目位置(url)  :  /Common/UserControls/MainTabControl.xaml

     关联Class(url) : /Common/CoreLib/PageInfo.cs

     绑定元素:TabList

     核心功能: 存储所有功能模块展示界面、提供可操作、关闭等功能

     

    4.消息通知组件(MainNotice)

     项目位置(url)  :  /Common/UserControls/MainNotice.xaml

     关联Class(url) : /ViewModel/Step/NoticeModel.cs

     绑定元素:NoticeView

     核心功能: 关联分配消息通知, 对话等功能实现

     

    5.辅助窗口(MainPopupBox)

     项目位置(url)  :  /Common/UserControls/MainPopupBox.xaml

     关联Class(url) : /ViewModel/Step/PopBoxViewModel.cs

     绑定元素:PopBoxView

     核心功能: 关联皮肤设置、系统设置、关于作者、退出系统等功能实现

     

    6.模块功能组件(UserToolBar)

     项目位置(url)  :  /Common/UserControls/UserToolBar.xaml

     关联Class(url) : /Common/CoreLib/ToolBarDefault.cs

     绑定元素:ButtonDefaults

     核心功能: 根据权限动态生成对应操作按钮, 每个模块都有单独的功能模块组件

     

    转载于:https://www.cnblogs.com/zh7791/p/9878322.html

    展开全文
  • WPF 通用权限开发框架,压缩包中有代码以及相关说明文档;完整的实战项目案例,可供学习和用于实际项目
  • WPF通用极速开发框架最轻最快最简单smart skin outlook style score 升级版
  • wpf权限通用快速开发框架可以帮你减少30左右的开发量,并且界面美观、大气,框架里的功能大部分都可以根据项目需求自行修改的,下载地址会有介绍开发框架的使用说明。下载地址:https://pan.baidu.com/s/1kV8wRll
  • 漂亮的WPF界面框架

    热门讨论 2013-09-09 14:53:15
    使用WPF ModernUI,该界面框架是非常现代、漂亮的通用界面框架,通过简单的配置,您可以将自定义的功能注册到页面。它支持三级菜单、支持更换皮肤和字体调整。 使用的OSGi.NET插件 更多细节尽在...
  • Model-View-ViewModel是一种架构模式,主要在WPF、Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind)。交互设计师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定,...

    Model-View-ViewModel是一种架构模式,主要在WPF、Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind)。交互设计师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定,而视图模型则是由应用程序开发者开发和维护的。

    MVVM是更加通用的Presentation模式的一个具体实现。MVVM视图模型包含概念模型而不是数据模型,所有业务逻辑和其它操作都是在模型和视图模型里完成的。有很多框架可以做到这点,其中一些是:

    开源的

    PRISM:由微软提供,和MEF/Unity一起用于依赖注入,支持组合命令,可以扩展。MSDN上有详细的教程和演练。

    MVVM Light Toolkit:有visual Studio和Expression Blend的项目和项的模板。更多信息请看这里,另外可以参考VS和Expression Blend的使用教程。

    Caliburn Micro:支持视图模型先行(ViewModel-First)和视图先行(View-First)两种开发方式,通过co-routine支持异步编程。

    Simple MVVM Toolkit:提供VS项目和项的模板,依赖注入,支持深拷贝以及模型和视图模型之间的属性关联。

    Catel:包含项目和项的模板,用户控件和企业类库。支持动态视图模型注入,视图模型的延迟加载和验证。还支持WP7专用的视图模型服务。

    闭源的

    Intersoft ClientUI:付费的,只支持WPF和Silverlight,但是,除了MVVM框架,它还提供其它一些特性。

    Vidyano:免费但不开源。带有实体映射/虚拟持久化对象(数据容器),业务规则以及内置基于ACL的安全特性。

    若想了解MVVM,可以参考以下资料:

    使用MVVM的最大好处之一是分离关注点,以便用户体验设计师和应用程序开发者可以并行工作。另一方面,相关的担忧包括它对于UI操作比较简单的情况有点杀鸡用牛刀的感觉,数据绑定有点难以调试,以及大量使用数据绑定可能带来性能问题等等。

    Jonathan Allen在评论里提到几点错误使用MVVM的征兆:

    1. 你的模型和视图模型名字相同。

    视图模型不应该是对模型的包装。视图模型的职责是外部服务的请求中介,比如加载和保存数据。而数据本身,以及验证和大多数业务逻辑应该放在模型里。

    我经常强调这点。每当你创建一个视图模型包装一个模型,你就在你的API里引入一个巨大漏洞。具体地,任何直接引用这个模型的东西都可能以视图模型无法察觉的方式改变某个属性,因此UI也不会有相应的改变。同样地,模型里计算字段的任何更改也不会回传给视图模型。

    2. 你的视图和视图模型名字相同。

    理想的情况下,视图模型是不知道使用它们的视图的,尤其是WPF应用程序有多个窗口共享相同的视图模型。

    对于比较小型的应用程序来说,整个应用程序可能只需一个视图模型。对于比较大型的应用程序来说,主要功能可能需要一个视图模型,每个次要方面也需要一个,比如配置管理。

    3. 你没有代码隐藏。

    代码隐藏既非一个好的东西,亦非一个坏的东西。它只是一个用来放置和视图或控件相关的逻辑的地方。因此,当我看到一个视图没有任何代码隐藏,我就会马上检查是否存在以下问题:

    视图模型是否通过名字接触了特定的控件?

    视图模型是否通过命令参数访问控件?

    是否使用了EventToCommand或其它可以导致泄露的行为而不是简单的事件处理程序?

    MVVM Light的EventToCommand很有问题,因为它会使得控件从屏幕移除之后无法被垃圾回收。

    4. 视图模型监听属性更改通知

    如果一个模型的的生命周期比监听它的事件的视图模型长,那么可能导致内存泄露。不同于视图有个Unloaded事件,视图模型对于生命周期管理没有很好的方案。因此如果它们关联到存活期比它们更长的视图模型的事件,视图模型将会出现泄露。

    展开全文
  • wpf的快速简单的本地化框架。 允许动态加载和多语言包。 入门 安装 要使用此框架。 添加nuget包: Install-Package Rasyidf.Localization 然后在App.cs上注册服务 public partial class App : Application { ...
  • 漂亮的WPF界面框架(OSGi.NET插件)源码 源码描述: 该界面框架是非常现代、漂亮的通用界面框架,通过简单的配置,您可以将自定义的功能注册到页面。它支持三级菜单、支持更换皮肤和字体调整。 使用的OSGi.NET插件 ...
  • 有朋友已经指出了,这个界面框架是基于ModernUI来实现的,在该文我将分享所有的源码,并详细描述如何基于ModernUI来构造一个非常通用的、插件化的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("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);
                }
            }
        }
    }
    复制代码

    上面的代码也很简单,逻辑很清晰,我来说明一下各个方法的用处:

    (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一下!》这篇文章介绍了。

     

    好了~,非常感谢这么耐心看完这篇文章。该附上源码了~。

    源码下载 点击下载。


    本文转自道法自然博客园博客,原文链接:http://www.cnblogs.com/baihmpgy/archive/2013/05/09/3068370.html,如需转载请自行联系原作者

    展开全文
  • 最简单最通用最完美的WPF快速开发框架smart-Skin源码
  • 漂亮的WPF界面框架(OSGi.NET插件)源码

    热门讨论 2015-03-02 00:31:33
    该界面框架是在ModernUI(http://mui.codeplex.com/)基础上开发的非常现代、漂亮的通用界面框架,通过简单的配置,您可以将自定义的功能注册到页面。它支持三级菜单、支持更换皮肤和字体调整。 使用的OSGi.NET插件 ...
  • WPF MVVM框架(转)

    千次阅读 2013-06-09 17:42:19
    Model-View-ViewModel是一种架构模式,主要在WPF、Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind)。交互设计师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定...
  • 有朋友已经指出了,这个界面框架是基于ModernUI来实现的,在该文我将分享所有的源码,并详细描述如何基于ModernUI来构造一个非常通用的、插件化的WPF开发框架。下载源码的同志,希望点击一下推荐。   本文将按照...
  • smart skin wpf outlook style score version1.0 原生开发框架没有任何插件升级版 ...最轻最快最简单通用极速开发框架smart skin wpf outlook style score 升级版 version 1.0 WPF仿杀毒软件界面UI360加速球功能UI
  • 背景 最近要求项目组成员开发一个通用的分页组件,要求是这个组件简单易用,通用性,兼容现有框架MVVM模式,可是最后给我提交的成果勉强能够用,却欠少灵活性和框架兼容性。 设计的基本思想 传入数据源,总页数,...
  • 最轻最快最简单通用的极速开发框架集合smart skin UI for winfrom and wpf 简极速极致产品 outlook style office for winform 开发框架 toolbar style office for winfrom 开发框架 原生态 wpf for outlook ...
  • 最轻最快最简单通用极速开发框架smart skin wpf outlook style score 升级版 version1.0 节约90% 的UI开发时间 上下节后,更简洁,更大气,更专业!
  • 最轻最快最简单通用极速开发框架smart skin wpf outlook style score 升级版 version 1.0 最轻最快最简单通用极速开发框架smart skin winfrom toolbar style score 升级版 version 3.0 最轻最快最简单通用极速开发...
  • 性能最好最轻最快最简单通用极速开发框架smart skin wpf outlook style score 升级版 version3 上千行数据绑定秒速渲染
  • 之前就一直在想着写这么一系列博客,将前段时间(也算有点久了)自己编写的一套框架分享下,和园子里的诸位大牛交流交流,奈何文思枯竭,提键盘而无从敲起,看来只有coding时才不会有这种裤子都脱了,才发现对方也是...
  • WPF简单导航框架(Window与Page互相调用)

    万次阅读 多人点赞 2016-09-27 00:30:11
    使用Frame的通用导航框架,可以从Page页中调用MainWindow中的公共方法和公共变量。
  • DbHelperMySQL.cs MySQL 数据库通用访问类 DbHelperOleDb.cs access 数据库通用访问类 DbHelperSQL.cs SQLserver 数据库通用访问类 DbHelperSQLite.cs SQLite 数据库通用访问类 Log.cs Log 系统错误日志通用类 ...
  • DbHelperMySQL.cs MySQL 数据库通用访问类 DbHelperOleDb.cs access 数据库通用访问类 DbHelperSQL.cs SQLserver 数据库通用访问类 DbHelperSQLite.cs SQLite 数据库通用访问类 Log.cs Log 系统错误日志通用类 ...
  • 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在WPF下写的简易的MVVM框架(MVVM模式和在WPF中的实现),都是.NET平台的,移到通用类...
  • 首先要阐述一点,MVVM并不是一个设计框架,而是一种设计模式,是MVP的一种进化,而MVVM的...譬如Silverlight,譬如WPF,而我们通常所说的MVVM框架指的则是galasoft的MVVMlight框架,地址如下所示,该框架支持windows...

空空如也

空空如也

1 2 3 4 5
收藏数 99
精华内容 39
关键字:

wpf通用框架