简介

本文实现一个简单的配置类,原理比较简单,适用于一些小型项目。主要实现以下功能:

  • 保存配置到json文件
  • 从文件或实例加载配置类的属性值
  • 数据绑定到界面控件

一般情况下,项目都会提供配置的设置界面,很少手动更改配置文件,所以选择以json文件保存配置数据。

配置基类

为了方便管理,项目中的配置一般是按用途划分到不同的配置类中,保存时也是保存到多个配置文件。所以,我们需要实现一个配置基类,然后再派生出不同用途的配置类。

配置基类需要引用Json.NET,继承数据绑定基类BindableBase,实现从其它实例加载数据的功能(参考C# 两个具有相同属性的类赋值),基类代码如下:

using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection; namespace TestConfigDll
{
/// <summary>
/// 配置基类,实现配置类的加载、保存功能
/// </summary>
/// <typeparam name="T"></typeparam>
public class ConfigBase<T> : BindableBase where T : class
{
/// <summary>
/// 文件保存路径
/// </summary>
private string filePath = ""; /// <summary>
/// 设置文件保存路径
/// </summary>
/// <param name="filePath"></param>
public virtual void SetPath(string filePath)
{
this.filePath = filePath;
} /// <summary>
/// 保存到本地文件
/// </summary>
public virtual void Save()
{
string dirStr = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dirStr))
{
Directory.CreateDirectory(dirStr);
}
string jsonStr = JsonConvert.SerializeObject(this);
File.WriteAllText(filePath, jsonStr);
} /// <summary>
/// 从本地文件加载
/// </summary>
public virtual void Load()
{
if (File.Exists(filePath))
{
var config = JsonConvert.DeserializeObject<T>(File.ReadAllText(filePath));
foreach (PropertyInfo pro in typeof(T).GetProperties())
{
pro.SetValue(this, pro.GetValue(config));
}
}
} /// <summary>
/// 从其它实例加载
/// </summary>
/// <param name="config"></param>
public virtual void Load(T config)
{
foreach (PropertyInfo pro in typeof(T).GetProperties())
{
pro.SetValue(this, pro.GetValue(config));
}
} /// <summary>
/// 从其它实例加载,仅加载指定的属性
/// </summary>
/// <param name="config"></param>
/// <param name="proName"></param>
public virtual void Load(T config, IEnumerable<string> proName = null)
{
foreach (PropertyInfo pro in typeof(T).GetProperties())
{
if (proName == null || proName.Contains(pro.Name))
{
pro.SetValue(this, pro.GetValue(config));
}
}
}
}
}

数据绑定基类BindableBase的实现参考WPF之数据绑定基类,代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices; namespace TestConfigDll
{
public class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// 属性值更改时发生
/// </summary>
public event PropertyChangedEventHandler PropertyChanged; /// <summary>
/// 检查属性是否已与设置值相等,设置属性并仅在必要时通知侦听器。
/// </summary>
/// <typeparam name="T">属性的类型</typeparam>
/// <param name="storage">对同时具有getter和setter的属性的引用</param>
/// <param name="value">属性的所需值</param>
/// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
/// <returns>如果值已更改,则为True;如果现有值与所需值匹配,则为false。</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value)) return false; storage = value;
RaisePropertyChanged(propertyName); return true;
} /// <summary>
/// 检查属性是否已与设置值相等,设置属性并仅在必要时通知侦听器。
/// </summary>
/// <typeparam name="T">属性的类型</typeparam>
/// <param name="storage">对同时具有getter和setter的属性的引用</param>
/// <param name="value">属性的所需值</param>
/// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
/// <param name="onChanged">属性值更改后调用的操作。</param>
/// <returns>如果值已更改,则为True;如果现有值与所需值匹配,则为false。</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value)) return false; storage = value;
onChanged?.Invoke();
RaisePropertyChanged(propertyName); return true;
} /// <summary>
/// 引发此对象的PropertyChanged事件。
/// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
} /// <summary>
/// 引发此对象的PropertyChanged事件。
/// </summary>
/// <param name="args">PropertyChangedEventArgs参数</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChanged?.Invoke(this, args);
} }
}

派生配置类

配置类的属性定义不能使用缩写形式,不用单例模式时可删除“配置单例”的代码,配置类代码如下:

class TestConfig :ConfigBase<TestConfig>
{
#region 配置单例
/// <summary>
/// 唯一实例
/// </summary>
public static TestConfig Instance { get; private set; } = new TestConfig(); /// <summary>
/// 静态构造函数
/// </summary>
static TestConfig()
{
Instance.SetPath("Config\\" + nameof(TestConfig) + ".json");
Instance.Load();
}
#endregion #region 属性定义
private string configStr = "";
public string ConfigStr
{
get
{
return this.configStr;
}
set
{
SetProperty(ref this.configStr, value);
}
} private int configInt =0;
public int ConfigInt
{
get
{
return this.configInt;
}
set
{
if (value > 100)
{
SetProperty(ref this.configInt, value);
}
}
} private bool configBool = false;
public bool ConfigBool
{
get
{
return this.configBool;
}
set
{
SetProperty(ref this.configBool, value);
}
}
#endregion
}

数据绑定

一般数据绑定会定义一个Model类、ViewModel类,本文为了演示方便使用配置类同时承担两者的角色。

Winform中的数据绑定

先设计一个简单的界面,如下所示:



配置数据的加载、保存不用对每个控件进行操作,后台代码如下:

public partial class Form1 : Form
{
private TestConfig testConfig = new TestConfig();
private List<string> proName = new List<string>(); public Form1()
{
InitializeComponent(); string textProName = nameof(textBox1.Text);
textBox1.DataBindings.Add(textProName, testConfig, nameof(testConfig.ConfigStr));
textBox2.DataBindings.Add(textProName, testConfig, nameof(testConfig.ConfigInt));
string checkedProName= nameof(checkBox1.Checked);
checkBox1.DataBindings.Add(checkedProName, testConfig, nameof(testConfig.ConfigBool)); proName.Add(textBox1.DataBindings[0].BindingMemberInfo.BindingField);
proName.Add(textBox2.DataBindings[0].BindingMemberInfo.BindingField);
proName.Add(checkBox1.DataBindings[0].BindingMemberInfo.BindingField); } private void button1_Click(object sender, EventArgs e)
{
testConfig.Load(TestConfig.Instance, proName);
} private void button2_Click(object sender, EventArgs e)
{
TestConfig.Instance.Load(testConfig, proName);
TestConfig.Instance.Save();
}
}

如上所示,testConfig作为中转,可以根据需求加载、保存配置类的部分或全部属性。如果对Winform下的数据绑感兴趣,可以参考Winform 普通控件的双向绑定

WPF下的数据绑定

先设计一个简单的界面,如下所示:

<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="208" Width="306.667">
<Grid>
<Label Content="ConfigStr:" HorizontalAlignment="Left" Margin="18,16,0,0" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="113,20,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="156" Text="{Binding Path=ConfigStr ,Mode=TwoWay}"/>
<Label Content="ConfigInt:" HorizontalAlignment="Left" Margin="18,44,0,0" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="113,48,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="156" Text="{Binding Path=ConfigInt ,Mode=TwoWay}"/>
<Label Content="ConfigBool:" HorizontalAlignment="Left" Margin="18,74,0,0" VerticalAlignment="Top"/>
<CheckBox Content="" HorizontalAlignment="Left" Margin="118,84,0,0" VerticalAlignment="Top" IsChecked="{Binding Path=ConfigBool ,Mode=TwoWay}"/>
<Button Content="加载" HorizontalAlignment="Left" Margin="24,129,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<Button Content="保存" HorizontalAlignment="Left" Margin="194,129,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/> </Grid>
</Window>

相对于Winform,WPF控件绑定的操作由XAML实现,后台代码如下:

/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private TestConfig testConfig = new TestConfig(); public MainWindow()
{
InitializeComponent();
this.DataContext = testConfig;
} private void Button_Click(object sender, RoutedEventArgs e)
{
testConfig.Load(TestConfig.Instance);
} private void Button_Click_1(object sender, RoutedEventArgs e)
{
TestConfig.Instance.Load(testConfig);
TestConfig.Instance.Save();
}
}

上面的代码比较粗糙,没有记录已经绑定的属性,实际使用时可以进一步优化。

附件

C#简单配置类及数据绑定的更多相关文章

  1. ssm简单配置

    MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架. MyBatis 摒除了大部分的JDBC代码.手工设置参数和结果集重获. MyBatis 只使用简单的XML 和注解来配置和映射 ...

  2. .net config文件 配置类的集合

    1,appconfig文件 <configSections> <section name="ToolConfig" type="DMTools.Tool ...

  3. mybatis 简单配置

    一.com/book/map包下有两个配置文件: 1.MyBatisConfig.xml <?xml version="1.0" encoding="UTF-8&q ...

  4. Struts 2简单配置分析

    要配置Struts 2,首先先要有Struts 2的Jar包,可以去Struts的官网下载(http://struts.apache.org/),这里有3个GA版本可以选择下载,我选择的是最新的2.2 ...

  5. Log4j简单配置

    Log4j是一组强大的日志组件,在项目中时常需要用它提供一些信息,这两天学习了一下它的简单配置. 第一步,我们需要导入log4j-1.2.14.jar到lib目录下 第二步,在src下建立log4j. ...

  6. spring注解开发中常用注解以及简单配置

    一.spring注解开发中常用注解以及简单配置 1.为什么要用注解开发:spring的核心是Ioc容器和Aop,对于传统的Ioc编程来说我们需要在spring的配置文件中邪大量的bean来向sprin ...

  7. Fluent Nhibernate code frist简单配置

    Fluent Nhibernate code frist简单配置   前言 在以前的项目开发过程中使用nhibernate做完orm映射工具需要编写大量的xml映射文件,项目过程中往往会因为一个字段等 ...

  8. Orleans简单配置

    Orleans简单配置 这是Orleans系列文章中的一篇.首篇文章在此 话说曾几何时,我第一次看到xml文件,心中闪过一念想:"这<>是什么鬼?"-用ini或者jso ...

  9. SpringMVC简单配置

    SpringMVC简单配置 一.eclipse安装Spring插件 打开help下的Install New Software 点击add,location中输入http://dist.springso ...

随机推荐

  1. PolarDB PostgreSQL Buffer Management 原理

    背景介绍 传统数据库的主备架构,主备有各自的存储,备节点回放WAL日志并读写自己的存储,主备节点在存储层没有耦合.PolarDB的实现是基于共享存储的一写多读架构,主备使用共享存储中的一份数据.读写节 ...

  2. P4783-[模板]矩阵求逆

    正题 题目链接:https://www.luogu.com.cn/problem/P4783 题目大意 给出一个矩阵,求它的逆矩阵. \(1\leq n\leq 400\) 解题思路 记给出矩阵\(P ...

  3. P5666-[CSP-S2019]树的重心【树状数组】

    正题 题目链接:https://www.luogu.com.cn/problem/P5666 题目大意 给出\(n\)个点的一棵树,对于每条边割掉后两棵树重心编号和. \(1\leq T\leq 5, ...

  4. Anaconda和canda简介及区别

    Anaconda简介: 1.是一个开源的Python发行版本,其包含了conda.Python等软件包,numpy,pandas(数据分析),scipy等科学计算包,而无需再单独下载配置. 可以在同一 ...

  5. mysql安装教程,mysql安装配置教程

    MySQL的安装教程 一.MYSQL的安装 首先登入官网下载mysql的安装包,官网地址:https://dev.mysql.com/downloads/mysql/ 一般下载这个就好,现在的最新版本 ...

  6. 数字图像处理(一)之灰度转换和卷积python实现

    使用Python实现数字图像处理中如下功能: 彩色图像转成灰度图像 实现图像的相关&卷积操作 实现图像的高斯核卷积 使用的库和python版本如下: imageio:2.9.0 用于读取磁盘中 ...

  7. 感恩笔记之SQL语句操纵数据集基本功能模板

    SQL查询_基本功能 一 SQL语句整体架构 SELECT --1 查询数据表 INTO --2 新建数据表 FROM --3 查询数据表 WHERE --4 筛选数据表 ORDER BY --5 排 ...

  8. WCF服务调用超时错误:套接字连接已中止。这可能是由于处理消息时出错或远程主机超过接收超时或者潜在的网络资源问题导致的。本地套接字超时是“00:05:30”(已解决)

    问题: 线上正式环境调用WCF服务正常,但是每次使用本地测试环境调用WCF服务时长就是出现:套接字连接已中止.这可能是由于处理消息时出错或远程主机超过接收超时或者潜在的网络资源问题导致的.本地套接字超 ...

  9. CSharpEntityFramework与CodeFirst实践

    CSharpEntityFramework与CodeFirst实践 前言 当我们进行开发的时候,常常会用到数据库来对数据进行持久化的操作,有的时候,我们并不想要在进行代码开发的过程中,还去关注数据库的 ...

  10. 打开属性页,分别在Debug和Release下将其配置类型改为:静态库(.lib);

    右键工程->属性 配置类型里面的下拉菜单选择静态库