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){// 这里使用的实例是ViewModelvar 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.xdnf.cn/news/1547466.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

使用npm link 把一个本地项目变成依赖,引入到另一个项目中

突然有天,发现线上的项目有块功能缺失,我以为是我优化的时候不小心改坏了什么代码,导致的,先上图 第一反应,就以为天塌了,完全无从入手,然后我就找了之前的离职的同事,他又给我两个包,让我打成依赖扔进去,这两个包分别是scratch-blocks,scratch-vm, 然后我就使用了npm link np…

【HarmonyOS】组件长截屏方案

【HarmonyOS】普通组件与web组件长截屏方案&#xff1a;原则是利用Scroll内的组件可以使用componentSnapshot完整的截屏 【普通组件长截屏】 import { componentSnapshot, promptAction } from kit.ArkUI import { common } from kit.AbilityKit import { photoAccessHelper }…

001、视频添加字幕

1. 腾讯智影 (可用) https://zenvideo.qq.com/ 1.1 操作步骤 https://zenvideo.qq.com/ https://zenvideo.qq.com/my/material?typeexport 上传资源 自动字幕识别 修改字幕 下载字幕 上传字幕 https://zenvideo.qq.com/my/material?typeexport 2. 秒剪–手机版app &a…

华为云分布式缓存服务Redis®版9月企业版、灵活的购买方式全新上市

华为云分布式缓存服务&#xff08;Distributed Cache Service&#xff0c;简称DCS&#xff09;是华为云提供的一款兼容Redis的高速内存数据处理引擎&#xff0c;为您提供即开即用、安全可靠、弹性扩容、便捷管理的在线分布式缓存能力&#xff0c;满足用户高并发及数据快速访问的…

MacOS多桌面调度快捷键

单桌面调度快捷键 可能是我用着妙控鼠标用着不习惯&#xff0c;所以追求快捷键操作&#xff0c;看起来也比较酷。而且在Windows上&#xff0c;我基本不使用多桌面&#xff0c;但是看着同事用Mac的多桌面用的飞起&#xff0c;炫酷程度不亚于win7的Windows键Tab。在不使用多桌面的…

SSM+Vue家教平台系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质创作…

水平分库分表的方法策略

分库分表介绍 在当前业务量迅猛增加的情况下&#xff0c;数据库经常面临性能的极致挑战。尤其是在处理大规模的数据集&#xff0c;例如超过千万条数据记录的情况下&#xff0c;SQL查询的性能将显著下降。随着数据量的增加&#xff0c;查询所需要扫描的数据范围变得更广&#x…

AOT源码解析4.4 -decoder生成预测mask并计算loss

3、生成ref_imgs的预测mask和loss 这一步在训练阶段调用 3.1 数据处理 图1&#xff0c;如图1所示&#xff0c;将enc_embs的最后一个比例的特征图和有ref_imgs相关的特征图得到的LSTT特征图相拼接作为输入 curr_enc_embs self.curr_enc_embscurr_lstt_embs self.curr_lstt_o…

了解针对基座大语言模型(类似 ChatGPT 的架构,Decoder-only)的重头预训练和微调训练

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 随着自然语言处理&#xff08;NLP&#xff09;技术的飞速进步&#xff0c;基于 Transformer 架构的大语言模型在众多任务中取得了显著成就。特别是 Decoder-only 架构&#xff0c;如 GPT 系列模型&…

8.7基于数学形态学的边缘检测

基本概念 数学形态学&#xff08;Mathematical Morphology&#xff09;是一套用于图像处理的技术&#xff0c;它包括膨胀&#xff08;Dilation&#xff09;、腐蚀&#xff08;Erosion&#xff09;、开运算&#xff08;Opening&#xff09;和闭运算&#xff08;Closing&#xf…

使用电子模拟器 Wokwi 运行 ESP32 示例(Arduino IDE、VSCode、ESP32C3)

文章目录 Wokwi 简介安装客户端&#xff08;Mac/Linux&#xff09;创建 Token Arduino IDEVSCode 配置安装 wokwi 插件打开编译后目录 ESP32C3 示例Arduino IDE创建模拟器运行模拟器 Wokwi 简介 Wokwi 是一款在线电子模拟器。您可以使用它来模拟 Arduino、ESP32、STM32 以及许…

HTML·第3章 表格布局与表单交互

3.1 表格概述 3.1.1 表格的结构 表格是由行和列组成的二维表&#xff0c;而每行又由一个或多个单元格组成&#xff0c;用于放置数据或其他内容。表格中的单元格是行与列的交叉部分&#xff0c;是组成表格的最基本单元。单元格的内容是数据&#xff0c;也称数据单元格。数据单元…

线上环境排故思路与方法GC优化策略

前言 这是针对于我之前[博客]的一次整理&#xff0c;因为公司需要一些技术文档的定期整理与分享&#xff0c;我就整理了一下。(https://blog.csdn.net/TT_4419/article/details/141997617?spm1001.2014.3001.5501) 其实&#xff0c;nginx配置 服务故障转移与自动恢复也是可以…

人工智能开发实战照片智能搜索功能实现

内容提要 项目分析预备知识项目实战 一、项目分析 1、提出问题 随着人民生活水平的提高和手机照相功能的日趋完美&#xff0c;我们不经意中拍摄了很多值得回忆的时刻&#xff0c;一场说走就走的旅行途中也记录下许多令人心动的瞬间&#xff0c;不知不觉之中&#xff0c;我们…

【CSS in Depth 2 精译_040】6.3 CSS 定位技术之:相对定位(下)—— 用纯 CSS 绘制一个三角形

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09;第二章 相对单位&#xff08;已完结&#xff09;第三章 文档流与盒模型&#xff08;已完结&#xff09;第四章 Flexbox 布局&#xff08;已…

RabbitMQ应用

RabbitMQ 共提供了7种⼯作模式, 进⾏消息传递 一、七种模式的概述 1、Simple(简单模式) P&#xff1a;生产者&#xff0c;就是发送消息的程序 C&#xff1a;消费者&#xff0c;就是接收消息的程序 Queue&#xff1a;消息队列&#xff0c;类似⼀个邮箱, 可以缓存消息; ⽣产者…

【微服务即时通讯系统】——brpc远程过程调用、百度开源的RPC框架、brpc的介绍、brpc的安装、brpc使用和功能测试

文章目录 brpc1. brpc的介绍1.1 rpc的介绍1.2 rpc的原理1.3 grpc和brpc 2. brpc的安装3. brpc使用3.1 brpc接口介绍 4. brpc使用测试4.1 brpc同步和异步调用 brpc 1. brpc的介绍 1.1 rpc的介绍 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;是一…

使用Postman搞定各种接口token实战

现在许多项目都使用jwt来实现用户登录和数据权限&#xff0c;校验过用户的用户名和密码后&#xff0c;会向用户响应一段经过加密的token&#xff0c;在这段token中可能储存了数据权限等&#xff0c;在后期的访问中&#xff0c;需要携带这段token&#xff0c;后台解析这段token才…

Java Stream流编程入门

流式编程 stream流式编程分为 首先转化为stream中间函数的链接最后的终结函数 怎么转化为stream 单列集合 List<String> list new ArrayList<String>(); Collections.addAll(list,"1","2","3","4","5","…

【MySQL】MVCC及其实现原理

目录 1. 概念介绍 什么是MVCC 什么是当前读和快照读 MVCC的好处 2. MVCC实现原理 隐藏字段 Read View undo-log 数据可见性算法 3. RC和RR隔离级别下MVCC的差异 4. MVCC&#xff0b;Next-key-Lock 防止幻读 1. 概念介绍 什么是MVCC Multi-Version Concurrency Cont…