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.rhkb.cn/news/433165.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

局域网广域网,IP地址和端口号,TCP/IP 4层协议,协议的封装和分用

前言 在古老的年代&#xff0c;如果我们要实现两台机器进行数据传输&#xff0c; A员工就得去B员工的办公电脑传数据&#xff08;B休息&#xff0c;等A传完&#xff09;&#xff0c;这样就很浪费时间 所以能不能不去B的工位的同时&#xff0c;还能传数据。这时候网络通信就出来…

智能抠图怎么操作?4款不动手自动抠图的智能神器分享

对于资深的图片设计师们来说&#xff0c;抠图是他们必备的基础技能&#xff0c;没几下功夫就能在PS中操作完成。 然而对于小编这种修图小白来讲&#xff0c;拥有一款傻瓜式智能抠图免费软件&#xff0c;才是硬道理&#xff01; 小到简单的图形文字、大到飞扬细碎的毛发&#…

贴片式TF卡(SD NAND)参考设计

【MK 方德】贴片 TF 卡参考设计 一、电路设计 1、 参考电路&#xff1a; R1~R5 (10K-100 kΩ)是上拉电阻&#xff0c;当 SD NAND 处于高阻抗模式时&#xff0c;保护 CMD 和 DAT 线免受总线浮动。 即使主机使用 SD NAND SD 模式下的 1 位模式&#xff0c;主机也应通过上拉电阻…

传奇微端黑屏不更新地图?传奇微端架设教程——GOM引擎

登录器和网站配置好后&#xff0c;我们进入游戏后会发现是黑屏的&#xff0c;更新不了地图和NPC这些&#xff0c;因为还没有做微端&#xff0c;会黑屏也是正常的。有些老G做了微端但是还是黑屏&#xff0c;就可能是你的微端架设出现了问题&#xff0c;可以参考以下教程。 gom引…

MQTT--快速入门

目录 1、什么是MQTT2、MQTT协议特性3、MQTT协议原理3.1 发布/订阅、主题、会话3.2 MQTT协议中的方法3.3 MQTT协议数据包结构 4、MQTT工作流程总结PS: 1、什么是MQTT MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09; &#…

学习之什么是生成器

什么是生成器&#xff08;Generator&#xff09; 1、是一种数据类型能源源不断地生成数据 2、"惰性"特点:一次生成一个值&#xff0c;而不是生成一个序列 3、生成器一定是迭代器比迭代器更简洁使用生成器表达式创建生成器 from typing import Generator, Iterator,…

Excel数据检视——对角线连续数据连线

实例需求&#xff1a;数据表如下图所示&#xff0c;现需要根据规则&#xff0c;在符合要求的单元格上&#xff0c;添加连线。 连续单元格位于对角线方向单元格内容相同连续单元格数量不少于7个 示例代码如下。 Sub LT2RB()Dim objDic As Object, rngData As Range, bFlag As …

fastapp-微信开发GPT项目第一课

0. 开发说明 在学习开发本项目之前&#xff0c;必须保证有以下知识储备和环境工具。 技术栈说明python>3.9、pydantic>2.7.1python基础&#xff0c;http协议fastapi>0.111.0web协程异步框架&#xff0c;有web开发基础&#xff0c;异步编程&#xff0c;类型标注[pyth…

【HTTP】请求“报头”(Host、Content-Length/Content-Type、User-Agent(简称 UA))

Host 表示服务器主机的地址和端口号 URL 里面不是已经有 Host 了吗&#xff0c;为什么还要写一次&#xff1f; 这里的 Host 和 URL 中的 IP 地址、端口什么的&#xff0c;绝大部分情况下是一样的&#xff0c;少数情况下可能不同当前我们经过某个代理进行转发。过程中&#xf…

【Qt | QList 】QList<T> 容器详细介绍和例子代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a; 2024-09-26 …

国产化低功耗窄带物联网无线通讯方案_ZETA技术

01 物联网系统中为什么要使用ZETA LPWAN 模组 物联网系统中使用ZETA LPWAN模组的原因主要基于以下几个方面&#xff1a; 1、技术优势 低功耗广域网&#xff08;LPWAN&#xff09;特性&#xff1a;ZETA技术是一种基于UNB的低功耗广域网技术协议标准&#xff0c;具有覆盖范围广…

从 Kafka 到 WarpStream: 用 MinIO 简化数据流

虽然 Apache Kafka 长期以来一直是流数据的行业标准&#xff0c;但新的创新替代方案正在重塑生态系统。其中之一是 WarpStream&#xff0c;它最近在 Confluent 的所有权下进入了新的篇章。此次收购进一步增强了 WarpStream 提供高性能、云原生数据流的能力&#xff0c;巩固了其…

【IOS】申请开发者账号(公司)

官网&#xff1a;Apple Developer (简体中文) 申请开发者账号前提 如果是第一次申请建议注册一个新的apple id作为组织的开发者账号。&#xff08;确保apple id的个人信息是真实的&#xff0c;不能是网名或者是其他名。后续的申请步骤需要能和apple id的个人信息对上。&#…

Navicat数据库管理工具实现Excel、CSV文件导入到MySQL数据库

1.所需要的工具和环境 navicat等第三方数据库管理工具云服务器中安装了 1Panel面板搭建的mysql数据库 2.基于 1Panel启动mysql容器 2.1 环境要求 安装前请确保您的系统符合安装条件&#xff1a; 操作系统&#xff1a;支持主流 Linux 发行版本&#xff08;基于 Debian / Re…

“类型名称”在Go语言规范中的演变

Go语言规范&#xff08;The Go Programming Language Specification&#xff09;[1]是Go语言的核心文档&#xff0c;定义了该语言的语法、类型系统和运行时行为。Go语言规范的存在使得开发者在实现Go编译器时可以依赖一致的标准&#xff0c;它确保了语言的稳定性和一致性&#…

c++----继承(初阶)

大家好呀&#xff0c;今天我们也是多久没有更新博客了&#xff0c;今天来讲讲我们c加加中的一个比较重要的知识点继承。首先关于继承呢&#xff0c;大家从字面意思看&#xff0c;是不是像我们平常日常生活中很容易出现的&#xff0c;比如说电视剧里面什么富豪啊&#xff0c;去了…

万魔头戴式耳机好用吗?万魔、西圣、索尼头戴式耳机决赛圈测评

现在耳机市场已经有各种不同类型的耳机&#xff0c;对于有降噪需求的人来说&#xff0c;头戴式耳机是一个不错的选择。那么对于后台有人私信说想知道万魔头戴式耳机好用吗&#xff1f;为了解答这个疑问&#xff0c;今天我就为大家评测西圣H1、万魔SonoFlow和索尼WH-CH520这三款…

【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器

文章目录 C list 容器详解&#xff1a;从入门到精通前言第一章&#xff1a;C list 容器简介1.1 C STL 容器概述1.2 list 的特点 第二章&#xff1a;list 的构造方法2.1 常见构造函数2.1.1 示例&#xff1a;不同构造方法2.1.2 相关文档 第三章&#xff1a;list 迭代器的使用3.1 …

【Linux】Linux 的 权限

一、 Linux 权限的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以在 linux 系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在 linux 下做有限的事情。超级用户的命令提示符是“#”&#xff0c;普…

初学51单片机之I2C总线与E2PROM

首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了&#xff0c;他的LCD1602讲的也很不错&#xff0c;把数据建立tsp和数据保持thd&#xff0c;比喻成拍照时候的摆pose和按快门两个过程&#xff0c;感觉还是…