准备.Net转前端开发-WPF界面框架那些事,搭建基础框架

题外话

最近都没怎么写博客,主要是最近在看WPF方面的书《wpf-4-unleashed.pdf》,挑了比较重要的几个章节学习了下WPF基础技术。另外,也把这本书推荐给目前正在从事WPF开发的程序猿。 现在书看完了也该实践实践,写了个WPF项目,主要以界面框架为主。  最近的几篇博客也主要围绕这个WPF项目,介绍下WPF搭建界面框架以及怎样写自定义的Windows界面和控件。

这也许是写最后几篇关于.Net技术的博客。做.Net开发也快五年了,感觉自己搞得不温不火,另外工作中正好有一个机会转做前段开发。稳稳当当的做年几年.Net开发,也想给自己一个挑战的机会。所以这几篇博客完成后,更多的可能是涉及前段开发。

雷军通知常常爱说:不忘初心,不忘初心、,不忘初心。重要的事说三遍。走在程序猿的路上,别忘了自己的初心,是做一个牛逼的程序猿呢还是做一个非常牛逼的程序猿呢?come on-打着鸡血一般的奋斗吧!

设计思路

前段时间看了下ASP.NET请求过程的管道运行,其中包含有IHttpModule接口,通过接口的扩展,可以让各个模块独立化,例如登录认证、权限认证以及处理IHttpHandler。同理,我们可以让WPF系统启动过程中独立加载各个模块,包括系统资源模块、主题资源模块、登录模块、用户认证模块、权限管理模块、以及自定义模块等。先来看下系统仅有的两个配置文件,分别是Application.xml和Startup.xml。Application.xml结构如下图所示:

<?xml version="1.0" encoding="utf-8" ?>
<Application  xmlns="http://tempuri.org/ApplicationSchema.xsd"
    xmlns:mstns="http://tempuri.org/ApplicationSchema.xsd">
  <Attributes>
    <Attr mstns:Name="stsdb" mstns:Value="Data/db.stsdb4"/>
  </Attributes>
</Application>

上图中,我们很容易的看出Application.xml主要存放字典资源,例如数据库的配置。sttsdb是数据库的字典名,而"Data/db.stsdb4"是数据库的路径。节点Attr表示一个字典项。

除了Application.xml文件,Startup.xml配置文件是整个系统的核心部分,因为我们系统启动的整个流程都体现在配置内容中。先看看节点结构:

<?xml version="1.0" encoding="utf-8" ?>
<Startup xmlns="http://tempuri.org/StartupSchema.xsd"
xmlns:xsi="http://tempuri.org/StartupSchema.xsd">
  <Startup.Modules>
    <ResourceModules>
       <Add xsi:Type="HeaviSoft.Documentor.Theme.Implements.ThemeModule, HeaviSoft.Documentor.Theme"/>
    </ResourceModules>
    <LoginModules>
       <Add xsi:Name="LoginModule" xsi:Type="HeaviSoft.Documentor.Presentation.Login.Implements.LoginModule,HeaviSoft.Documentor.Presentation.Login"/>
    </LoginModules>
    <AuthenticationModules>
      <Add xsi:Name="AuthenticationModule" xsi:Type="HeaviSoft.Documentor.Application.Security.AuthenticationModule, HeaviSoft.Documentor.Application.Security"  />
    </AuthenticationModules>
    <AuthorizationModules>
    </AuthorizationModules>
    <ExecutionModules>
      <Add xsi:Type="HeaviSoft.Documentor.Presentation.Docking.Implements.ExecutionModule, HeaviSoft.Documentor.Presentation.Docking"/>
    </ExecutionModules>
  </Startup.Modules>
</Startup>

系统的所有资源存放在节点Startup.Modules节点下。节点说明:

节点 说明
ResourceModules 存放所有的资源模块,例如Theme资源以及语言资源,可添加多个。
LoginModules 存放登录模块资源,一般我们只用添加一个Login模块。但也支持添加多个。
AuthenticationModules 验证系统用户的身份是否合法,可添加多个。
AuthorizationModules 系统的授权管理,可添加多个。
ExecutionModules 通用执行模块,前面是个模块执行完后,可添加自己的功能模块。例如我们可以把主界面的显示流程添加到这个模块。

谈谈框架

比较常见的系统框架一般都包含两个部分:基础框架和业务框架。基础框架一般不会太涉及业务,主要为业务框架提供平台,提供上下文环境。本次设计的基础框架主要包括了上下文环境、安全策略、日志以及自定义组件等等。而业务框架我们主要就是考虑业务模块的功能实现。在实现业务功能的同时,我们得考虑分层设计,保证业务的高扩展性以及系统的可持续性。

基础框架

基础框架包含了七个模块,框架结构如下图所示:

接下来我们就分别介绍上图中比较重要的几个模块:Core、utility、Resource。

基础框架-Core

Core包含了Core模块和Configuration模块,两个模块都有一个共同的功能,都为系统的启动过程服务。Configuration模块定义了系统配置文件的模板。Configuration的整个工程结构如下所示:

图中的Schema文件夹下定义了两个配置规则文件,分别对应Application.xml和Startup.xml。这两个文件的具体内容在前面的“设计思路”部分已经给出。

配置已经有了,我们分析Startup.xml中的节点内容,例如:<Add xsi:Type="HeaviSoft.FrameworkBase.Client.Implements.ThemeModule, HeaviSoft.FrameworkBase.Client"/>。这一句代码的实际意义是什么?属性Type的值代表一个类的名称以及所在命名空间。在系统启动的过程中,可通过反射机制动态创建节点类型的实体对象。这些通过动态创建的类都有一个共同的特点,都需要实现某个接口来扩展一个模块的功能。而Core工程下为我们定义了这些模块接口,如下图所示:

主题和语言接口包括:IResourceModule、IThemeResourceModule、ILanguageResourceModule。IResourceModule是父接口,而IThemeResourceModule和ILanguageResourceModule是IResourceModule的扩展接口。IResourceModule定义如下:

/// <summary>
    /// 资源加载模块
    /// </summary>
    public interface IResourceModule
    {
        /// <summary>
        /// 资源加载中
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="name">资源名称</param>
        /// <returns></returns>
        bool Loading(ExtendedApplicationBase app, string name);
        /// <summary>
        /// 资源加载完成
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="name">资源名称</param>
        /// <returns></returns>
        bool Loaded(ExtendedApplicationBase app, string name);
        /// <summary>
        /// 资源卸载中
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="name">资源名称</param>
        /// <returns></returns>
        bool UnLoading(ExtendedApplicationBase app, string name);
        /// <summary>
        /// 资源卸载完成
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="name">资源名称</param>
        /// <returns></returns>
        bool UnLoaded(ExtendedApplicationBase app, string name);
    }

我们可以在Loading中执行加载资源,并且接收两个参数:app和name,app的类型为ExtendedApplicationBase,ExtendedApplicationBase就是我们的应用基类,它几乎存储了系统的所有上下文信息,稍后再详解。在IResourceModule接口的方法中,我们可以获取系统的任何上下文信息。

IThemeResourceModule是IResourceModule的一个子接口,用于加载主题资源;和IThemeResourceModule相似,ILanguageResourceModule用户加载管理语言资源。如果我们要切换主题或资源,可先掉调用IResourceModule接口的UnLoading方法先卸载主题和语言资源。然后再调用新资源的Loading方法。

登录接口:ILoginModule。系统的登录操作可通过实现ILoginModule接口来执行,代码如下:

/// <summary>
    /// 登录模块
    /// </summary>
    public interface ILoginModule
    {
        /// <summary>
        /// 登录接口
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <returns>返回登陆操作状态</returns>
        bool Login(ExtendedApplicationBase app);
        /// <summary>
        /// 登录成功
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="message">成功消息</param>
        void LoginSuccessed(ExtendedApplicationBase app, object message);
        /// <summary>
        /// 登录失败
        /// </summary>
        /// <param name="app">应用对象</param>
        /// <param name="message">失败消息</param>
        void LoginFailed(ExtendedApplicationBase app, object message);
    }

登录过程在Login方法中实现,登录验证后,如果登录成功则会调用LoginSuccessed方法,如果登录失败,则调用LoginFailed。

身份认证:IAuthenticationModule接口。ILoginModule接口执行后,上下文信息中缓存了加密后的登录信息,IAuthenticationModule主要对上下文中的登录信息做验证。验证有没有通过我们可以通过app中的Context.User.Identity.IsAuthenticated来判断。

授权接口:IAuthorizationModule。IAuthenticationModule执行后,我们能够知道用户身份是否合法,如果身份有效,IAuthorizationModule可到服务器上获取用户的权限信息,并保存在上下文中。如果用户要使用某个业务模块,可从上下文中调用接口IPrincipal的IsInRole方法验证授权。IPrincipal的定义如下:

public interface IPrincipal
    {
        IIdentity Identity { get; }
        bool IsInRole(string role);
    }

模块的接口就介绍到这里,在启动系统时,怎样把这些接口串联的执行。这就不得不提到系统基础ExtendedApplicationBase。

基础框架-ExtendedApplicationBase

首先,我们需要考虑系统启动后,Startup.xml中模块集合存放在哪里。ExtendedApplicationBase就是比较合适的一个容器,它定义了像ThemeResourceModules、LanguageResourceMudules等模块集合。容器有了,那么执行的步骤怎样串联起来?ExtendedApplicationBase实现了一个抽象方法BuildSteps(),用于构建系统的启动步骤。而整个系统的执行需要实现抽象方法ExecuteSteps()。还是先看看代码再分析:

/// <summary>
    /// 应用对象类
    /// </summary>
    public abstract class ExtendedApplicationBase : Application
    {
        public readonly string PROPERTY_ACCOUNT = "ExtendedApplicationBase_Account";
        public readonly string PROPERTY_PASSWORD = "ExtendedApplicationBase_Password";

        /// <summary>
        /// 当前应用实例
        /// </summary>
        public static ExtendedApplicationBase Current { get; set; }

        public ExtendedApplicationBase() : base()
        {
            Data = new Dictionary<object, object>();
            ThemeResourceModules = new List<IThemeResourceModule>();
            LanguageResourceMudules = new List<ILanguageResourceModule>();
            LoginModules = new List<ILoginModule>();
            AuthenticationModules = new List<IAuthenticationModule>();
            AuthorizationModules = new List<IAuthorizationModule>();
            ExecutionModules = new List<IExecutionModule>();

            Context = new AppContext();
        }

        /// <summary>
        /// 应用上下文
        /// </summary>
        public AppContext Context { get; private set; }

        public Dictionary<object, object> Data { get; private set; }

        protected List<IThemeResourceModule> ThemeResourceModules { get; private set; }

        protected List<ILanguageResourceModule> LanguageResourceMudules { get; private set; }

        protected List<ILoginModule> LoginModules { get; private set; }

        protected List<IAuthenticationModule> AuthenticationModules { get; private set; }

        protected List<IAuthorizationModule> AuthorizationModules { get; private set; }

        protected List<IExecutionModule> ExecutionModules { get; private set; }

        /// <summary>
        /// 构建步骤
        /// </summary>
        public abstract void BuildSteps();

        /// <summary>
        /// 执行步骤
        /// </summary>
        /// <returns>执行状态</returns>
        public virtual bool ExecuteSteps()
        {
            if (!ExecuteThemeResourceModulesCore())
                return false;
            if (!ExecuteLanguageResourceModulesCore())
                return false;
            if (!ExecuteLoginModulesCore())
                return false;
            if (!ExecuteAuthorizationModulesCore())
                return false;
            if (!ExecuteExecutionModulesCore())
                return false;

            return true;
        }

        /// <summary>
        /// 关闭系统
        /// </summary>
        public virtual void ExitEx()
        {
            Environment.Exit(0);
        }

        /// <summary>
        /// 执行主题资源加载
        /// </summary>
        /// <returns></returns>
        protected abstract bool ExecuteThemeResourceModulesCore();

        /// <summary>
        /// 执行语言资源加载
        /// </summary>
        /// <returns></returns>
        protected abstract bool ExecuteLanguageResourceModulesCore();

        /// <summary>
        /// 执行登录流程
        /// </summary>
        /// <returns></returns>
        protected abstract bool ExecuteLoginModulesCore();
        /// <summary>
        /// 执行身份验证
        /// </summary>
        /// <returns></returns>
        public abstract bool ExecuteAutheticationModulesCore();
        /// <summary>
        /// 执行身份授权
        /// </summary>
        /// <returns></returns>
        protected abstract bool ExecuteAuthorizationModulesCore();
        /// <summary>
        /// 执行常规流程
        /// </summary>
        /// <returns></returns>
        protected abstract bool ExecuteExecutionModulesCore();

        /// <summary>
        /// 激活当前应用
        /// </summary>
        public void Activate()
        {
            this.MainWindow.Show();
            this.MainWindow.Activate();
        }

代码中,ExecuteSteps方法分别调用了ExecuteThemeResourceModulesCore、ExecuteLanguageResourceModulesCore、ExecuteLoginModulesCore、ExecuteAuthorizationModulesCore、ExecuteExecutionModulesCore。这几个方法都是抽象方法。具体的实现都是在子类中。我们还看到类中包括一个Data属性,类型为Dictionary。之前我们提到了Application.xml,这里边配置的所有Attr字典配置数据都会存储在Data中,取值直接通过ExtendedApplicationBase.Current.Data[Key]读取。

基础框架-ExtendedApplicationBase实现类

在系统中添加一个ExtendedApplicationBase的具体实现类ExtendedApplication。代码如下:

/// <summary>
    /// 系统应用
    /// </summary>
    internal class ExtendedApplication : ExtendedApplicationBase
    {
        public ExtendedApplication() : base()
        {
            ShutdownMode = ShutdownMode.OnExplicitShutdown;
        }

        #region 系统事件
        /// <summary>
        /// 流程启动
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStartup(StartupEventArgs e)
        {
            //注册事件
            RegistEvent();
            //开始构建步骤
            try
            {
                this.BuildSteps();
                //执行步骤
                if (!this.ExecuteSteps())
                {
                    this.ExitEx();
                }
            }
            catch (Exception ex)
            {
                //写日志
                Logger.Error("Error occured during appication start-up.", ex);
                this.ExitEx();
            }
        }

        private void RegistEvent()
        {
            //捕获UI线程
            this.DispatcherUnhandledException += ExtendedApplication_DispatcherUnhandledException;
            //捕获其他线程异常
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
        }

        private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            //写日志
            Logger.Error("Unknown error.", e.Exception);
            //处理异常
        }

        /// <summary>
        /// 未捕获的异常
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ExtendedApplication_DispatcherUnhandledException(object sender,
            System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            HandleUnKnownExcpetion(e.Exception);
        }

        private void HandleUnKnownExcpetion(Exception e)
        {
            //写日志
            Logger.Error("Unknown error.", e);
            //启动过程中发生了异常。
            if (e is StartupException)
            {
                //启动异常提示
            }
            else if (e is FatalException)
            {
                //中断异常提示
            }
            else
            {
                //其他异常
            }
        }

        #endregion

        #region 父类抽象方法实现
        public override void BuildSteps()
        {
            #region Application Attributes
            var applicationRoot = ConfigurationHelper.GetApplicationConfigRoot();
            if (!applicationRoot.IsNull())
            {
                var eleLayouts = new string[] { ConfigurationHelper.Config_Node_Attriutes, ConfigurationHelper.Config_Node_Attr };
                var attributes = applicationRoot.GetElements(eleLayouts);
                foreach (var element in attributes)
                {
                    var name = element.GetAttributeValue("Name");
                    var value = element.GetAttributeValue("Value");

                    if (!this.Data.ContainsKey(name))
                    {
                        this.Data.Add(name, value);
                    }
                }
            }
            #endregion

            #region Startup
            var startupRoot = ConfigurationHelper.GetStartupConfigRoot();
            if (!startupRoot.IsNull())
            {
                //加载ResourceModules
                var startups = startupRoot.GetElements(new string[] { ConfigurationHelper.Config_Node_Modules, ConfigurationHelper.Config_Node_ResourceModules, ConfigurationHelper.Config_Node_Operation });
                var resourcesTypes = startups.ToList().Select(el => el.GetAttributeValue("Type"));
                var resourceModules = new List<IResourceModule>();
                foreach (var type in resourcesTypes)
                {
                    resourceModules.Add(CreateInstanceByType<IResourceModule>(type));
                }
                this.ThemeResourceModules.AddRange(resourceModules.Where(res => res is IThemeResourceModule).Cast<IThemeResourceModule>());
                this.LanguageResourceMudules.AddRange(resourceModules.Where(res => res is ILanguageResourceModule).Cast<ILanguageResourceModule>());
                //加载LoginModules
                var loginTypes = startupRoot.GetElements(new string[] { ConfigurationHelper.Config_Node_Modules, ConfigurationHelper.Config_Node_LoginModules, ConfigurationHelper.Config_Node_Operation }).ToList().Select(el => el.GetAttributeValue("Type"));
                foreach (var type in loginTypes)
                {
                    this.LoginModules.Add(CreateInstanceByType<ILoginModule>(type));
                }
                //加载AuthenticationModules
                var authenticationTypes = startupRoot.GetElements(new string[] { ConfigurationHelper.Config_Node_Modules, ConfigurationHelper.Config_Node_AuthenticationModules, ConfigurationHelper.Config_Node_Operation }).ToList().Select(el => el.GetAttributeValue("Type"));
                foreach (var type in authenticationTypes)
                {
                    this.AuthenticationModules.Add(CreateInstanceByType<IAuthenticationModule>(type));
                }
                //加载AutorizationModules
                var authorizationTypes = startupRoot.GetElements(new string[] { ConfigurationHelper.Config_Node_Modules, ConfigurationHelper.Config_Node_AuthorizationModules, ConfigurationHelper.Config_Node_Operation }).ToList().Select(el => el.GetAttributeValue("Type"));
                foreach (var type in authorizationTypes)
                {
                    this.AuthorizationModules.Add(CreateInstanceByType<IAuthorizationModule>(type));
                }
                //加载执行流程
                var executionTypes = startupRoot.GetElements(new string[] { ConfigurationHelper.Config_Node_Modules, ConfigurationHelper.Config_Node_ExecutionModules, ConfigurationHelper.Config_Node_Operation }).ToList().Select(el => el.GetAttributeValue("Type"));
                foreach (var type in executionTypes)
                {
                    this.ExecutionModules.Add(CreateInstanceByType<IExecutionModule>(type));
                }
            }

            #endregion
        }

        protected override bool ExecuteThemeResourceModulesCore()
        {
            //加载主题资源
            foreach (var module in ThemeResourceModules)
            {
                if (!module.Loading(this, Context.CurrentTheme))
                {
                    throw new StartupException("Error occured when loading theme resource.");
                }
            }
            //主题资源加载完成
            foreach (var module in ThemeResourceModules)
            {
                if (!module.Loaded(this, Context.CurrentTheme))
                {
                    throw new StartupException("Error occured when loaded theme resource.");
                }
            }
            return true;
        }

        protected override bool ExecuteLanguageResourceModulesCore()
        {
            //加载语言资源
            foreach (var module in LanguageResourceMudules)
            {
                if (!module.Loading(this, Context.CurrentLanguage))
                {
                    throw new StartupException("Error occured when loading language resource.");
                }
            }
            //语言资源加载完成
            foreach (var module in LanguageResourceMudules)
            {
                if (!module.Loaded(this, Context.CurrentLanguage))
                {
                    throw new StartupException("Error occured when loaded language resource.");
                }
            }
            return true;
        }

        protected override bool ExecuteLoginModulesCore()
        {
            foreach (var login in LoginModules)
            {
                try
                {
                    if (!login.Login(this))
                    {
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            return true;
        }

        public override bool ExecuteAutheticationModulesCore()
        {
            var result = true;
            var message = "Authenticating user is successed.";
            //执行认证
            foreach (var auth in AuthenticationModules)
            {
                if (!auth.Authenticate(this))
                {
                    result = false;
                    message = "Invalid user, please check inputed user info!";
                    break;
                }
            }
            //用户认证失败,判断是否需要重新启动登录
            if (!result)
            {
                //是否需要重新登陆
                foreach (var login in LoginModules)
                {
                    login.LoginFailed(this, message);
                }
            }
            //用户认证成功
            else
            {
                foreach (var login in LoginModules)
                {
                    login.LoginSuccessed(this, message);
                }
            }

            return result;
        }

        protected override bool ExecuteAuthorizationModulesCore()
        {
            //授权之前检查用户是否验证通过
            if (!Context.User.Identity.IsAuthenticated)
            {
                Logger.Info("User must be autenticated before executing authorizationModule.");
                return false;
            }
            //用户授权操作
            foreach (var autor in AuthorizationModules)
            {
                if (!autor.Authorize(this))
                {
                    Logger.Info("User authorizate failed.");
                    return false;
                }
            }

            return true;
        }

        protected override bool ExecuteExecutionModulesCore()
        {
            //加载数据s
            foreach (var excute in ExecutionModules)
            {
                excute.Execute(this);
            }

            return true;
        }

        #endregion

        #region 私有方法
        private T CreateInstanceByType<T>(string typeInfo)
        {
            try
            {
                var array = typeInfo.Split(‘,‘);
                object instance = null;
                try
                {
                    instance = Assembly.LoadFrom(string.Format("{0}.dll", array[1].Trim())).CreateInstance(array[0].Trim());
                }
                catch (FileNotFoundException)
                {
                    instance = Assembly.LoadFrom(string.Format("{0}.exe", array[1].Trim())).CreateInstance(array[0].Trim());
                }

                return (T)instance;
            }
            catch (Exception ex)
            {
                throw new StartupException(string.Format("Error occured when executing CreateInstanceByType method, parameter:{0}", typeInfo), ex);
            }
        }

        #endregion

    }

首先说说重写的BuildSteps方法,先从Application.xml中读取配置字典,然后从Startup.xml读取模块配置。按顺序先后读取ResourceModules、LoginModules、AuthenticationModules、AutorizationModules、ExecutionModules。并且在每读取一个模块,调用CreateInstanceByType<IXXXModule>(moduleTypeName)实例化具体类型的对象。每创建一个实体对象,都会存放在模块集合中,例如创建的ThemeResourceModule对象就会存放在ExtendedApplicationBase的ThemeResourceModules集合中。在ExtendedApplicationBase类中我们也看到几个模块集合对象,如下图所示:

public Dictionary<object, object> Data { get; private set; }

        protected List<IThemeResourceModule> ThemeResourceModules { get; private set; }

        protected List<ILanguageResourceModule> LanguageResourceMudules { get; private set; }

        protected List<ILoginModule> LoginModules { get; private set; }

        protected List<IAuthenticationModule> AuthenticationModules { get; private set; }

        protected List<IAuthorizationModule> AuthorizationModules { get; private set; }

        protected List<IExecutionModule> ExecutionModules { get; private set; }

所有的模块对象都准备就绪,现在要考虑整个系统的执行步骤怎样实现。我们知道系统的启动都是以Application的OnStartup方法作为入口,那看看OnStartup代码:

/// <summary>
        /// 流程启动
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStartup(StartupEventArgs e)
        {
            //注册事件
            RegistEvent();
            //开始构建步骤
            try
            {
                this.BuildSteps();
                //执行步骤
                if (!this.ExecuteSteps())
                {
                    this.ExitEx();
                }
            }
            catch (Exception ex)
            {
                //写日志
                Logger.Error("Error occured during appication start-up.", ex);
                this.ExitEx();
            }
        }

代码先执行RegistEvent方法注册异常捕获事件,一般都是注册事件到DispatcherUnhandledException捕获UI线程的未知异常,但别忘了捕获工作线程,工作线程的异常使用TaskScheduler.UnobservedTaskException捕获。事件注册之后执行BuildSteps方法,前面刚刚说过了。步骤构建完了,就该调用ExecuteSteps方法执行具体模块。 ExecuteSteps的实现是在ExtendedApplicationBase中,代码如下:

/// <summary>
        /// 执行步骤
        /// </summary>
        /// <returns>执行状态</returns>
        public virtual bool ExecuteSteps()
        {
            if (!ExecuteThemeResourceModulesCore())
                return false;
            if (!ExecuteLanguageResourceModulesCore())
                return false;
            if (!ExecuteLoginModulesCore())
                return false;
            if (!ExecuteAuthorizationModulesCore())
                return false;
            if (!ExecuteExecutionModulesCore())
                return false;

            return true;
        }

这里列举一个ExecuteThemeResourceModulesCore的实现做简要说明,其他的模块执行流程相似。ExecuteThemeResourceModulesCore实现如下:

protected override bool ExecuteThemeResourceModulesCore()
        {
            //加载主题资源
            foreach (var module in ThemeResourceModules)
            {
                if (!module.Loading(this, Context.CurrentTheme))
                {
                    throw new StartupException("Error occured when loading theme resource.");
                }
            }
            //主题资源加载完成
            foreach (var module in ThemeResourceModules)
            {
                if (!module.Loaded(this, Context.CurrentTheme))
                {
                    throw new StartupException("Error occured when loaded theme resource.");
                }
            }
            return true;
        }

前面已经给出了IResouceModule接口的定义。ExecuteThemeResourceModulesCore先遍历ThemeResouceModules的对象,调用Loading方法。紧接着再次遍历ThemeResourceModules集合,调用对象的Loaded方法。其他的几个模块的执行原理相似。

本篇总结

本篇介绍的内容比较简单,首先介绍了系统的两个配置文件Application.xml和Startup.xml,根据这两个配置文件提出系统的设计思路。其次,给出了Core中的7个接口代码,每个接口代表了一个独立的模块,像资源模块、认证模块、授权模块等。然后,分析自定义的Application类:ExtendedApplicationBase,它包括了系统流程的构建方法BuildSteps、系统流程的执行方法ExecuteSteps。最后给出ExtendedApplicationBase的实现类ExtendedApplication,并且给出代码,根据代码分析了步骤如何构建,步骤如何执行,并且列举了资源模块的执行过程ExecuteThemeResourceModulesCore方法。

搭建一个WPF界面框架,肯定少不了Custom Window和Custom Control。因为开发一个给客户使用的系统,肯定会根据客户设计不同的UI。那么,如何开发这些具有独特风格的UI以及自定义控件,并且包含可替换的主题。下一篇再做详解。

如果本篇内容对大家有帮助,请点击页面右下角的关注。如果觉得不好,也欢迎拍砖。你们的评价就是博主的动力!下篇内容,敬请期待!

源代码

完整的代码存放在GitHub上,代码路径:https://github.com/heavis/Documentor_V01R01/

时间: 2024-12-26 11:42:13

准备.Net转前端开发-WPF界面框架那些事,搭建基础框架的相关文章

准备.Net转前端开发-WPF界面框架那些事,值得珍藏的8个问题

题外话 不出意外,本片内容应该是最后一篇关于.Net技术的博客,做.Net的伙伴们忽喷忽喷..Net挺好的,微软最近在跨平台方面搞的水深火热,更新也比较频繁,而且博客园的很多大牛也写的有跨平台相关技术的博客.做.Net开发块五年时间,个人没本事,没做出啥成绩.想象偶像梅球王,年龄都差不多,为啥差别就这么大.不甘平庸,想趁机会挑战下其他方面的技术,正好有一个机会转前段开发. 对于目前正在从事或者工作中会用到WPF技术开发的伙伴,此片内容不得不收藏,本片介绍的八个问题都是在WPF开发工作中经常使用到

[JavaEE] SSH框架笔记_eclipse搭建SSH框架详解

SSH框架是最常用的框架之一,在搭建SSH框架的时候总有人遇到这样,那样的问题.下面我介绍一下SSH框架搭建的全过程. 第一步:准备工作. 下载好eclipse,Struts2,Spring,Hibernate. 1.eclipse:eclipse下载的时候建议下载JavaEE版的eclipse. 当然你也可以下载eclipse-SDK.(下载eclipse-SDK需要下载Web,Tomcat等plugins) 2.Struts2:http://struts.apache.org/downloa

软件系统开发之前要做的事—需求调研框架

近期在做的一个软件需求调研,从以下一些方面进行了了解,当然这些只是初步的一个需求框架,还打不到设计开发的基本,只是让软件公司能够对软件需求有一个总体的了解而方便对软件有一个大概的估算. 公司情况 实现的根本目的 现有软件情况(现有应用.架构部署情况.使用技术:技术文档.是否需要进行数据对接,需对接方是否提供技术支持) 涉及人员 业务类型 业务流程 业务规则 关注重点 其他非功能性需求 数据规模 数据频率 应用环境

前端开发必须说的那些事之——同源策略(same origin policy)

同源策略指的是三个相同 协议相同 域名相同 端口相同 如https://www.baidu.com/hahah.html这个网址来说 https是使用的协议,www.baidu.com是域名,端口号默认是80(不指定端口默认是80端口); 所以: www.baidu.com/hehe.html 同源 www.baidu.com/heihei/houhou.html 同源 www.i71.com/haha.html  域名不同,不同源 www.baidu.com:888/haha.html   端

Spring框架学习之--搭建spring框架

此文介绍搭建一个最最简单的spring框架的步骤 一.创建一个maven项目 二.在pom.xml文件中添加依赖导入spring框架运行需要的相关jar包 注意:在引入jar包之后会出现org.junit里面的包无法使用,参考https://blog.csdn.net/he99774/article/details/78254262 <dependencies> <!-- 框架运行时的基本依赖 --> <dependency> <groupId>junit&

webpack 环境搭建基础框架

一.安装babel相关 1,安装依赖 cnpm i -D babel-core babel-loader babel-preset-env babel-preset-stage-2 babel-plugin-transform-runtime 2,新建.babelrc { presets: [ [ "env", { "targets": { "browsers": ["last 5 versions", "ie &g

web前端开发学习路线

首先分享一下我的经验,想做好一件事,必须要花费一些功夫,然后是多学.多思.多练.多交流.多总结,发现自己的问题,然后一定要克服,在状态不好的情况下,往往要及时调整.新手学习前端的话,一定要想想为什么要学习它,是出于一种什么心态,然后定位好自己,多向大牛请教,多教一些没有自己水平高的人,那样往往能让自己成长的快,切勿急躁.初学可以看一些入门视频教程,之后可以买一些书,做一些小项目,要学会投资,分析自己的现状及能力,实时调整,一定要有自己的想法,懂得创新.在这里一定要对自己做分析,然后找出一种适合的

web前端开发怎么样学习?看这份web前端学习路线

前端开发是创建Web页面或app等前端界面呈现给用户的过程.前端开发通过HTML,CSS及JavaScript以及衍生出来的各种技术.框架.解决方案,来实现互联网产品的用户界面交互.它从网页制作演变而来,名称上有很明显的时代特征.在互联网的演化进程中,网页制作是Web1.0时代的产物,早期网站主要内容都是静态,以图片和文字为主,用户使用网站的行为也以浏览为主.随着互联网技术的发展和HTML5.CSS3的应用,现代网页更加美观,交互效果显著,功能更加强大. 随着时代的发展,现在从事IT方向的人有很

什么专业转行web前端开发工程师,就业率最高?

近几年,随着技术和政策的变化,国内对技术人才的需求也是翻天覆地的变化. 对于很多毕业学生来说,前端开发工程师行业需求大.待遇好.不限门槛.政策优,成为了很多大学生的就业发展选择. 行业需求大 5G时代即将来临,各行各业对产品的用户体验需求空前大增.人们在享受互联网带来的便捷也给互联网产品提出了新的需求,这意味着前端开发人员也有了更多的机会和挑战.无论是大小公司,对前端开发工程师的需求都是在快速上涨,薪资待遇也随之上升很快. 待遇好 无论是在国内还是在国外,开发工程师都是高薪行业,如果能进入到BA