WPF治具软件模板分享

运行环境:VS2022 .NET 8.0

完整项目:Gitee仓库

项目重命名方法参考:网页

概要:针对治具单机软件制作了一个设计模板,此项目可对一些治具的PC简易软件项目提供一个软件模板,可以通过修改项目名快速开发,本项目使用了CommunityToolkit.Mvvm来搭建MVVM框架,使用了Microsoft.Extensions.DependencyInjection来实现DI依赖注入实现IOC,并使用了WPFUI作为UI框架 这个项目可以通过重命名修改成自己所需的软件项目,实现减少重复创建框架的目的 此项目还有很多不足,望大家多多谅解!


程序功能介绍

此程序适用于治具的PC简易软件的快速开发,软件各个功能实现如下:

MVVM框架:CommunityToolkit.Mvvm库

DI依赖注入:Microsoft.Extensions.DependencyInjection

UI框架:WPFUI库

程序日志:NLog库

程序配置:System.Text.Json库

多语言支持:resx资源文件+WPFLocalizeExtension库

软件文件架构如下图所示

功能实现

导航功能

  • App.xaml.cs
var container = new ServiceCollection();

//...

//注册导航服务
container.AddSingleton<Common.Services.NavigationService>(); //... Services = container.BuildServiceProvider();
  • MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions> <Grid>
<ui:TitleBar Title="JIG_SoftTemplate_V1.0.0" Background="#e5e5e5">
<ui:TitleBar.Icon>
<ui:ImageIcon Source="/Resources/Image/Company_logo.png"/>
</ui:TitleBar.Icon>
</ui:TitleBar>
</Grid> <Grid Grid.Row="1" x:Name="ShowGrid">
<Frame x:Name="rootFrame"/>
<ui:SnackbarPresenter x:Name="rootSnackbarPresenter" Margin="0,0,0,-15"/>
<ContentPresenter x:Name="rootContentDialog"/>
</Grid>
</Grid>
  • MainWindow.xaml.cs
public partial class MainWindow
{
public MainWindow(MainWindowViewModel viewModel, Common.Services.NavigationService navigationService)
{
InitializeComponent(); DataContext = viewModel; navigationService.SetMainFrame(rootFrame); App.Current.Services.GetRequiredService<ISnackbarService>().SetSnackbarPresenter(rootSnackbarPresenter);
App.Current.Services.GetRequiredService<IContentDialogService>().SetDialogHost(rootContentDialog); }
}
  • NavigationService.cs
public class NavigationService
{
private Frame? mainFrame; public void SetMainFrame(Frame frame) => mainFrame = frame; private Type? FindView<VM>()
{
return Assembly
.GetAssembly(typeof(VM))
?.GetTypes()
.FirstOrDefault(t => t.Name == typeof(VM).Name.Replace("ViewModel", ""));
} public void Navigate<VM>()
where VM : ViewModelBase
{
Navigate<VM>(null);
} public void Navigate<VM>(Dictionary<string, object?>? extraData)
where VM : ViewModelBase
{
var viewType = FindView<VM>();
if (viewType is null)
return; var page = App.Current.Services.GetService(viewType) as Page;
mainFrame?.Navigate(page, extraData);
}
}

在MainWindow.xaml里放Frame控件,在MainWindow.xaml.cs里,调用NavigationService的SetMainFrame方法设定了Frame组件实例,然后在VM中调用Navigate方法即可跳转页面,示例:navigationService.Navigate<HomePageViewModel>();

需注意,View和ViewModel需要名称对的上,如NavigationService.cs中所示,比如HomePage.xaml的ViewModel就是HomePageViewModel,名称错误的话,会在FindView方法中报错

程序配置

  • App.xaml.cs
var container = new ServiceCollection();

//...

//注册Json配置文件服务
container.AddSingleton<Common.Services.JsonConfigService>(); //... Services = container.BuildServiceProvider();
  • AppConfigModel.cs
public class AppConfigModel
{
public CommonConfig? Common { get; set; } public JIGCommConfig? JIGComm { get; set; }
} public class CommonConfig
{
public string? DataStoragePath { get; set; }
public string? SelectedLang { get; set; }
} public class JIGCommConfig
{ }
  • JsonConfigService.cs
public class JsonConfigService
{
private const string ConfigFileName = "AppSettings.json"; private readonly LoggerService loggerService; public JsonConfigService(LoggerService loggerService)
{
this.loggerService = loggerService;
} public async Task<T> LoadConfigAsync<T>(T defaultValue = default) where T : new()
{
try
{
var filePath = GetConfigFilePath();
if (!File.Exists(filePath))
{
loggerService.Info("配置文件不存在,返回默认值");
await SaveConfigAsync(defaultValue ?? new T());
return defaultValue ?? new T();
} await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
return await JsonSerializer.DeserializeAsync<T>(fs) ?? new T();
}
catch (Exception ex)
{
loggerService.Error("加载配置文件失败", ex);
return defaultValue ?? new T();
}
} public async Task SaveConfigAsync<T>(T config)
{
try
{
var filePath = GetConfigFilePath();
var dirPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
} var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}; await using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
await JsonSerializer.SerializeAsync(fs, config, options);
loggerService.Info("配置文件保存成功");
}
catch (Exception ex)
{
loggerService.Error("保存配置文件失败", ex);
throw;
}
} private static string GetConfigFilePath()
{
// 使用程序根目录存储配置文件
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigFileName);
}
}

调用服务时,只需调用LoadConfigAsyncSaveConfigAsync异步方法即可,示例如下:

//读取配置文件
string DataStoragePath = ""; var loadedConfig = await jsonConfigService.LoadConfigAsync(new AppConfigModel());
if (loadedConfig != null)
{
if (loadedConfig?.Common != null)
{
//更新到属性
DataStoragePath = loadedConfig.Common.DataStoragePath;
}
} //保存配置到文件
private AppConfigModel appConfig;
//初始化appConfig
appConfig = new AppConfigModel();
appConfig.Common = new CommonConfig(); //更新界面上数据到配置appConfig中
appConfig.Common.DataStoragePath = DataStoragePath; //保存配置
await jsonConfigService.SaveConfigAsync(appConfig);

日志功能

  • App.xaml.cs
var container = new ServiceCollection();

//...

//注册日志服务
container.AddSingleton<Common.Services.LoggerService>(); //... Services = container.BuildServiceProvider();
  • NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"> <targets>
<!-- Info日志目标 -->
<target name="InfoFile"
xsi:type="File"
fileName="LogFile/LogInfo/${shortdate}-Info.log"
layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff} [INFO] ${message}${newline}"
encoding="UTF-8"
archiveEvery="Day"
maxArchiveFiles="100"
concurrentWrites="true"
keepFileOpen="false"/> <!-- Error日志目标 -->
<target name="ErrorFile"
xsi:type="File"
fileName="LogFile/LogError/${shortdate}-Error.log"
layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff} [ERROR] [ThreadID:${threadid}] ${message}${newline}[StackTrace:${exception:format=Message}]${newline}"
encoding="UTF-8"
archiveEvery="Day"
maxArchiveFiles="100"
concurrentWrites="true"
keepFileOpen="false"/>
</targets> <rules>
<!-- MyLog记录器规则 -->
<logger name="MyLog" minlevel="Info" maxlevel="Info" writeTo="InfoFile" final="true" />
<logger name="MyLog" minlevel="Error" maxlevel="Error" writeTo="ErrorFile" final="true" />
</rules>
</nlog>
  • LoggerService.cs
public class LoggerService
{
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("MyLog"); public void Info(string message)
{
Logger.Info(message);
} public void Error(string message, Exception? ex = null)
{
Logger.Error(ex, message);
}
}

调用服务时,只需调用InfoError方法即可,示例如下:

private void FunA()
{
try
{
loggerService.Info("程序成功");
}
catch (Exception ex)
{
loggerService.Error("程序出错", ex);
}
}

界面介绍

各个界面如下图所示

主界面

登录界面

设置界面

具体项目请自行到Gitee仓库查看吧

WPF治具软件模板分享的更多相关文章

  1. 分享一个WPF 实现 Windows 软件快捷小工具

    分享一个WPF 实现 Windows 软件快捷小工具 Windows 软件快捷小工具 作者:WPFDevelopersOrg 原文链接:https://github.com/WPFDevelopers ...

  2. WPF中的数据模板(DataTemplate)(转)

    原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/30/694388.html WPF中的数据模板(DataTemplate)        ...

  3. WPF中的数据模板(DataTemplate)

    原文:WPF中的数据模板(DataTemplate) WPF中的数据模板(DataTemplate)                                                   ...

  4. WPF默认控件模板的获取和资源词典的使用

    一.获取默认的控件模板 WPF修改控件模板是修改外观最方便的方式,但是会出现不知道原来的控件的模板长什么样,或者想用来参考的,下面分享一下获取某控件默认控件模板的方式(已Button为例): 1.创建 ...

  5. Xamarin For Visual Studio 3.0.54.0 完整离线破解版(C# 开发Android、IOS工具 吾乐吧软件站分享)

    Xamarin For Visual Studio就是原本的Xamarin For Android 以及 Xamarin For iOS,最新版的已经把两个独立的插件合并为一个exe安装包了.为了区分 ...

  6. WPF 中获取DataGrid 模板列中控件的对像

    WPF 中获取DataGrid 模板列中控件的对像 #region 当前选定行的TextBox获得焦点 /// <summary> /// 当前选定行的TextBox获得焦点 /// &l ...

  7. 【WPF】创建基于模板的WPF控件(经典)

    原文:[WPF]创建基于模板的WPF控件(经典) WPF可以创建两种控件,它们的名字也很容易让人混淆:用户控件(User Control)和定制控件(Customer Control),之所以如此命名 ...

  8. WPF中的数据模板使用方式之一:ContentControl、ContentTemplate和TemplateSelector的使用

    在WPF中,数据模板是非常强大的工具,他是一块定义如何显示绑定的对象的XAML标记.有两种类型的控件支持数据模板:(1)内容控件通过ContentTemplate属性支持数据模板:(2)列表控件通过I ...

  9. WPF Style设置和模板化Template

    WPF样式设置和模板化是一套功能(样式,模板,触发器和演示图版),可以为产品设置统一外观.类似于html的css,可以快速的设置一系列属性值到控件. 案例:ButtonStyle 这里创建了一个目标类 ...

  10. WPF中的菜单模板

    原文:WPF中的菜单模板 资源字典代码如下: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xa ...

随机推荐

  1. 全球首位 AI 程序员 Devin 诞生了,对于程序员的影响到底多大?

    当我第一次看到Devin的演示视频时,我的手机差点掉在地上. 不是因为兴奋,而是因为一种前所未有的恐惧和震撼. 作为一个在程序员这条路上摸爬滚打了快10年的老司机,我见证了太多技术的更迭和变革.从最初 ...

  2. git 已经commit 没有 poll / upload 回退

    git reset id 这里的id 是提交前一个id 完成Commit命令的撤销,但是不对代码修改进行撤销,可以直接通过git commit 重新提交对本地代码的修改.不对代码进行撤销可以重新提交.

  3. DXF 最简单的一个文件生成一个直线 (1)

    记得把# 注释删除 0 SECTION 2 HEADER 9 $ACADVER 1 AC1009 9 $INSBASE 10 0.000000 20 0.000000 30 0.000000 9 $E ...

  4. # opengl 学习 之 07 lesson

    opengl 学习 之 07 lesson 简介 OBJ的使用 link http://www.opengl-tutorial.org/uncategorized/2017/06/07/website ...

  5. 关于 python 人脸检测库 dlib 的 初识 2

    简介 关于人脸检测算法python库的初步认识2 使用CNN的实现人脸检测 简单说明 The example loads a pretrained model and uses it to find ...

  6. 【L53】动态规划求解最大子序和问题

    Question 给定一个整数数组 nums , 找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. Anwser 当问题可以分解为彼此独立且离散子问题时,可以考虑使用动态规划来 ...

  7. Django实时通信实战:WebSocket与ASGI全解析(上)

    一.实时通信的核心:WebSocket 协议 WebSocket 介绍 在 Web 开发中,实时通信场景(如在线聊天.实时数据监控.协作工具等)越来越常见.传统的 HTTP 协议基于 "请求 ...

  8. Wordless: 一个周末打造的小爆游戏

    这个项目是什么 Wordless就是个类似 Wordle 的猜单词游戏,用 Next.js 搭建的.玩家有 6 次机会猜出单词,支持 3 到 8 个字母的单词.说实话,开始只是想做点跟 wordle ...

  9. here's to never

    But it's not like that now This time I'll never let you go I will be all that you want And get mysel ...

  10. Drissionpage VS Selenium

    DrissionPage vs Selenium 特性/功能点 Selenium DrissionPage 备注 底层驱动 WebDriver 协议 (如 ChromeDriver, geckodri ...