当前位置: 首页 > article >正文

WPF项目中使用Caliburn.Micro框架实现日志和主题切换

目录

一、添加Caliburn.Micro框架

 二、配置Serilog日志

三、实现主题切换

        Caliburn.Micro是MVVM模式的轻量级WPF框架,简化了WPF中的不少用法。这个框架中所有的页面控制都是通过ViewModel去实现的。

        以下内容是自己在进行项目实战的同时进行记录的,对于文件的创建以及分类是考虑了实际扩展性等问题,不太适合初学者观看。 

一、添加Caliburn.Micro框架

创建一个WPF项目,删除项目的MainWindow.xaml,并为其添加Caliburn.Micro包

修改App.xaml文件内容,删掉StartupUri="MainWindow.xmal"语句,这个框架不需要通过这个语句去查找启动窗口

在项目中创建好View,Model和ViewModel文件夹

创建一个AppBootstrapper类继承BootstrapperBase,它是Caliburn.Micro框架的一个启动类(大部分代码都是固定的可以直接使用,除了Ioc注入以及OnStartup()函数中的内容可能需要更改)

using Caliburn.Micro;
using ProjectM.ViewModels;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Reflection;
using System.Windows.Threading;

namespace ProjectM.Components
{
    public class AppBootstrapper : BootstrapperBase
    {
        private CompositionContainer _container;
        private IWindowManager _windowManager;
        private IEventAggregator _eventAggregator;

        public AppBootstrapper()
        {
            Initialize();
        }

        protected override void Configure()
        {
            
            var aggregateCatalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
            // 管理和解析依赖关系
            var batch = new CompositionBatch();
            // 批量处理依赖注入
            _container = new CompositionContainer(aggregateCatalog);
            // 注册依赖注入
            batch.AddExportedValue(_container);

            //注入IoC,我们在其他文件中可以直接通过Ioc.Get<T>()来获取依赖注入的实例
            _windowManager = new WindowManager();// 注册窗体管理器
            batch.AddExportedValue(_windowManager);
            _eventAggregator = new EventAggregator();// 注册事件聚合器
            batch.AddExportedValue(_eventAggregator);

            _container.Compose(batch);
        }

        protected override void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            base.OnUnhandledException(sender, e);
        }

        protected override object GetInstance(Type service, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;

            var exports = _container.GetExportedValues<object>(contract);

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

            throw new Exception($"找不到实例 {contract}。");
        }

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

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            var assemblies = new List<Assembly>()
            {
                Assembly.GetEntryAssembly(),
                Assembly.GetExecutingAssembly(),
            };

            return assemblies.Where(x => x != null)
                .Distinct();
        }

        protected override void BuildUp(object instance)
        {
            _container.SatisfyImportsOnce((ComposablePart)instance);
        }

        protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
        {
            // 这里使用的实例是ViewModel
            var viewModel = new ShellViewModel();
            _windowManager.ShowWindow(viewModel);
        }
    }
}

 在ViewModel中继承Caliburn.Micro框架中的Screen,最后启动项目,项目成功被启动(这里是通过我们前面的AppBootstrapper文件中的OnStartup函数中配置的内容实现的窗口启动)

public class ShellViewModel : Screen
{

}

 二、配置Serilog日志

安装Serilog相关的包(第二个是将日志写入文件,还有其他的选项,比如打印到控制台等按需下载即可)

 我们新建一个类用于单独存放静态共用字段

public static class Fields
{
    public const string AppName = "ProjectM";

    public const string AppDataPath = "E:\\ProjectM\\" + AppName;

    public const string LogFileName = "log.txt";
}

 再建一个类存放动态字段

public class Environments
{
    private static string _appDataPath;
    private static string _logFilePath;

    /// <summary>
    /// 应用程序数据路径
    /// </summary>
    public static string AppDataPath
    {
        get
        {
            if (string.IsNullOrEmpty(_appDataPath))
            {
                _appDataPath = Environment.ExpandEnvironmentVariables(Fields.AppDataPath);
            }
            if (!Directory.Exists(_appDataPath))
            {
                Directory.CreateDirectory(_appDataPath);
            }
            return _appDataPath;
        }
    }

    /// <summary>
    /// 日志文件路径
    /// </summary>
    public static string LogFilePath
    {
        get
        {
            if (string.IsNullOrEmpty(_logFilePath))
            {
                _logFilePath = Path.Combine(AppDataPath, Fields.LogFileName);
            }
            return _logFilePath;
        }
    }
}

创建一个ILogger接口并实现接口

public interface ILogger
{
    void Error(Exception exception, string messageTemplate);

    void Error(Exception exception, string messageTemplate, params object[] args);

    void Infomation(string message);

    void Infomation(string messageTemplate, params object[] args);

    void Warning(string message);

    void Warning(string messageTemplate, params object[] args);
}
public class Logger : Contracts.ILogger
{
    private static Serilog.Core.Logger _logger;
    private static Logger _instance;

    // 确保日志的唯一性(单例模式)
    public static Logger Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Logger();
            }
            return _instance;
        }
    }

    public void Error(Exception exception, string messageTemplate)
    {
        InitializeLogger();
        _logger?.Error(exception, messageTemplate);
    }

    public void Error(Exception exception, string messageTemplate, params object[] args)
    {
        InitializeLogger();
        _logger?.Error(exception, messageTemplate, args);
    }

    public void Infomation(string message)
    {
        InitializeLogger();
        _logger?.Information(message);
    }

    public void Infomation(string messageTemplate, params object[] args)
    {
        InitializeLogger();
        _logger?.Information(messageTemplate, args);
    }

    public void Warning(string message)
    {
        InitializeLogger();
        _logger?.Warning(message);
    }

    public void Warning(Exception exception, string messageTemplate, params object[] args)
    {
        InitializeLogger();
        _logger?.Warning(exception, messageTemplate, args);
    }

    public void Warning(string messageTemplate, params object[] args)
    {
        InitializeLogger();
        _logger?.Warning(messageTemplate, args);
    }

    /// <summary>
    /// 初始化_logger
    /// </summary>
    private void InitializeLogger()
    {
        if (_logger == null)
        {
            var logFilePath = Environments.LogFilePath;

            // 日志文件按天分割,最大文件数为30
            _logger = new LoggerConfiguration()
                 .WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30)
                 .CreateLogger();
        }
    }
}

最后我们可以创建一个ViewModelBase作为基类,让它去继承Screen。我们所有的ViewModel都可以继承这个 基类 然后在文件中声明我们注入的对象,从而优化代码。(这里能使用IoC.Get直接去获取是因为我们在AppBootstrapper类注入了Ioc)

public class ViewModelBase : Screen
{
    public IWindowManager WindowManager => IoC.Get<IWindowManager>();

    public IEventAggregator EventAggregator => IoC.Get<IEventAggregator>();

    public ILogger Logger => IoC.Get<ILogger>();
}

三、实现主题切换

我们首先定义两个资源文件,分别是Light.xaml和Dark.xaml,设置不同主题下的背景颜色和字体颜色

 在App.xaml中引入我们定义好的资源文件(这里引入一个我们打开的默认显示主题就行)

 定义一个枚举类存放我们的主题

public enum AppTheme
{
    Light = 0,
    Dark = 1
}

 定义一个接口类,用于声明切换主题的方法

public interface IThemeManager
{
    void UpdateTheme(AppTheme theme);
}

实现这个方法(通过在资源字典中找到主题资源设置,删除原来的资源,将新的主题资源添加进去,从而实现了主题切换的效果)

public class ThemeManager : IThemeManager
{
    /// <summary>
    /// 切换思路:在资源字典中找到主题资源,替换掉原来的资源,这样就实现了切换主题的效果
    /// </summary>
    /// <param name="theme"></param>
    public void UpdateTheme(AppTheme theme)
    {
        for(int i = 0; i < Application.Current.Resources.MergedDictionaries.Count; i++)
        {
            var dictionary = Application.Current.Resources.MergedDictionaries[i];
            if (string.IsNullOrEmpty(dictionary.Source?.OriginalString))
            {
                continue;
            }
            if(dictionary.Source.OriginalString.StartsWith("/ProjectM.UI;component/Themes/"))
            {
                Application.Current.Resources.MergedDictionaries.Remove(dictionary);
                var resourceDictionary = new ResourceDictionary()
                {
                    Source = new Uri($"/ProjectM.UI;component/Themes/{theme}.xaml", UriKind.RelativeOrAbsolute)
                };
                Application.Current.Resources.MergedDictionaries.Insert(i,resourceDictionary);
                return;
            }
        }
    }
}

我们在AppBootstrapper.cs文件中注入我们的IThemeManager(注入后我们可以再任何地方直接使用,不用去new对象,达到解耦的目的)

 最后进行测试,在界面定义一个Button和文本内容

 在ViewModel中实现SwitchTheme方法

bool isLight = true;
public void SwitchTheme()
{
    if (isLight)
    {
        ThemeManager.UpdateTheme(AppTheme.Dark);
    }
    else
    {
        ThemeManager.UpdateTheme(AppTheme.Light);
    }
    isLight =!isLight;
}

最后运行项目,点击Button,主题成功进行切换


http://www.kler.cn/news/321906.html

相关文章:

  • 论文解析_客户分组对商业银行个人信用评分模型的提升作用研究,作者张亚京-中国人民银行征信中心博士后工作站
  • 数据仓库适用的业务场景
  • 【高分系列卫星简介】
  • 系统架构设计师 SOA与微服务架构篇
  • Spark-RDD持久化
  • IDEA2020运行项目时不从配置的maven仓库找jar包,从C盘默认路径下找jar包
  • 使用 React useEffect 实现条件判断的最佳实践
  • c语言200例 64
  • 示例说明:sql语法学习
  • 9.sklearn-K-means算法
  • 人员个体检测、PID行人检测、行人检测算法样本
  • c++----继承(初阶)
  • 开源 AI 智能名片 S2B2C 商城小程序与营销工具的快速迭代
  • 其实你不懂老板的心——解释器模式
  • VUE.js笔记
  • 直接在tomcat下面访问jsp
  • 在虚幻引擎中实现Camera Shake 相机抖动/震屏效果
  • TryHackMe 第4天 | Pre Security (三)
  • 系统架构设计师 - 案例特训专题 - 数据库设计篇
  • 智能制造的生产力基础设施
  • Java Redis多限流
  • 使用SOCKS5代理:单窗口单IP的妙用
  • java核心基础
  • 【1分钟学会】实用的Git工作流程
  • s5pv210开发板刷机,分区,SD卡,emmc,nand,fastboot刷机命令,刷uboot,kernel,system,led裸机例子(二)
  • 从耐用到防水:全面综合评估SD卡的性能指标
  • Go语言实现后台管理系统如何根据角色来动态显示栏目
  • PHP基础语法讲解
  • 【力扣 | SQL题 | 每日三题】力扣175, 176, 181
  • MQ高级(二):死信交换机--延迟消息及DelayExchange插件--超时订单案例实现