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

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,主题成功进行切换

相关文章:

  • ubuntu20.04系统安装zookeeper简单教程
  • 【PostgreSQL】PostgreSQL数据库允许其他IP连接到数据库(Windows Linux)
  • MATLAB案例 | Copula的密度函数和分布函数图
  • vue echarts tooltip动态绑定模板,并且处理vue事件绑定
  • 将ai模型部署在服务器,会比本地离线更快吗
  • Proteus-7.8sp2安装
  • 论文阅读 | 一种基于潜在向量优化的可证明安全的图像隐写方法(TMM 2023)
  • Apache Cordova和PhoneGap
  • Redis支持数据类型,它们各自的应用场景是
  • 基于深度学习的文本情感原因提取研究综述——论文阅读
  • Spring Boot 2.x基础教程:实现文件上传
  • 理解:基础地理实体相关概述
  • 局域网中实现一对一视频聊天(附源码)
  • Shp2pb:Shapefile转Protocol Buffers的高效工具
  • 直线模组降噪攻略
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Laravel Mix运行时关于es2015报错解决方案
  • LeetCode18.四数之和 JavaScript
  • magento 货币换算
  • node入门
  • Protobuf3语言指南
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Rancher-k8s加速安装文档
  • 构建工具 - 收藏集 - 掘金
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 前端路由实现-history
  • 入门到放弃node系列之Hello Word篇
  • 什么软件可以剪辑音乐?
  • 推荐一个React的管理后台框架
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​queue --- 一个同步的队列类​
  • #Spring-boot高级
  • (备份) esp32 GPIO
  • (二)hibernate配置管理
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (学习日记)2024.01.19
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET Core引入性能分析引导优化
  • .NET Core中的去虚
  • .NET导入Excel数据
  • .NET的数据绑定
  • .NET技术成长路线架构图
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .Net通用分页类(存储过程分页版,可以选择页码的显示样式,且有中英选择)
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)