diff --git a/SunnyUI.Demo/Bin/SunnyUI.Common.dll b/SunnyUI.Demo/Bin/SunnyUI.Common.dll index 31e09c80..b9bb89d3 100644 Binary files a/SunnyUI.Demo/Bin/SunnyUI.Common.dll and b/SunnyUI.Demo/Bin/SunnyUI.Common.dll differ diff --git a/SunnyUI.Demo/Bin/SunnyUI.Demo.exe b/SunnyUI.Demo/Bin/SunnyUI.Demo.exe index 06b1342d..0b7f6365 100644 Binary files a/SunnyUI.Demo/Bin/SunnyUI.Demo.exe and b/SunnyUI.Demo/Bin/SunnyUI.Demo.exe differ diff --git a/SunnyUI.Demo/Bin/SunnyUI.dll b/SunnyUI.Demo/Bin/SunnyUI.dll index 4f9a677d..c05c1ec4 100644 Binary files a/SunnyUI.Demo/Bin/SunnyUI.dll and b/SunnyUI.Demo/Bin/SunnyUI.dll differ diff --git a/SunnyUI.Demo/Properties/AssemblyInfo.cs b/SunnyUI.Demo/Properties/AssemblyInfo.cs index 22e2e518..5819b28c 100644 --- a/SunnyUI.Demo/Properties/AssemblyInfo.cs +++ b/SunnyUI.Demo/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.2.5.0")] -[assembly: AssemblyFileVersion("3.2.5.0")] +[assembly: AssemblyVersion("3.2.6.0")] +[assembly: AssemblyFileVersion("3.2.6.0")] diff --git a/SunnyUI.Demo/SunnyUI.Demo.csproj b/SunnyUI.Demo/SunnyUI.Demo.csproj index d3efcde2..203d5845 100644 --- a/SunnyUI.Demo/SunnyUI.Demo.csproj +++ b/SunnyUI.Demo/SunnyUI.Demo.csproj @@ -41,11 +41,11 @@ app.manifest - - ..\packages\SunnyUI.3.2.5\lib\net472\SunnyUI.dll + + ..\packages\SunnyUI.3.2.6.1\lib\net472\SunnyUI.dll - - ..\packages\SunnyUI.Common.3.2.5\lib\net40\SunnyUI.Common.dll + + ..\packages\SunnyUI.Common.3.2.6\lib\net40\SunnyUI.Common.dll diff --git a/SunnyUI.Demo/packages.config b/SunnyUI.Demo/packages.config index 8369dc0d..52921eb1 100644 --- a/SunnyUI.Demo/packages.config +++ b/SunnyUI.Demo/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/SunnyUI/Common/UIniConfig.cs b/SunnyUI/Common/UIniConfig.cs new file mode 100644 index 00000000..c6139092 --- /dev/null +++ b/SunnyUI/Common/UIniConfig.cs @@ -0,0 +1,175 @@ +/****************************************************************************** + * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。 + * CopyRight (C) 2012-2022 ShenYongHua(沈永华). + * QQ群:56829229 QQ:17612584 EMail:SunnyUI@QQ.Com + * + * Blog: https://www.cnblogs.com/yhuse + * Gitee: https://gitee.com/yhuse/SunnyUI + * GitHub: https://github.com/yhuse/SunnyUI + * + * SunnyUI.dll can be used for free under the GPL-3.0 license. + * If you use this code, please keep this note. + * 如果您使用此代码,请保留此说明。 + ****************************************************************************** + * 文件名称: UIniConfig.cs + * 文件说明: INI 配置文件类 + * 当前版本: V3.1 + * 创建日期: 2020-01-01 + * + * 2020-01-01: V2.2.0 增加文件说明 +******************************************************************************/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; + +namespace Sunny.UI +{ + /// + /// INI 配置文件类 + /// + /// 类型 + public class IniConfig : BaseConfig where TConfig : IniConfig, new() + { + #region 加载 + + /// 加载指定配置文件 + /// 文件名 + /// 结果 + public override bool Load(string filename) + { + if (filename.IsNullOrWhiteSpace()) + { + filename = DirEx.CurrentDir() + ConfigFile; + } + + if (filename.IsNullOrWhiteSpace()) + { + throw new ApplicationException($"未指定{typeof(TConfig).Name}的配置文件路径!"); + } + + if (!File.Exists(filename)) + { + return false; + } + + try + { + ConcurrentDictionary idents = ConfigHelper.InitIdents(current); + foreach (var ident in idents.Values) + { + if (ident.Section.IsNullOrEmpty()) + { + ident.Section = "Setup"; + } + } + + IniFile ini = new IniFile(filename); + foreach (var ident in idents.Values) + { + if (ident.IsList) + { + ident.Values.Clear(); + NameValueCollection list = ini.GetSectionValues(ident.Section + "-" + ident.Key); + foreach (var pair in list) + { + ident.Values.Add(ini.Read(ident.Section + "-" + ident.Key, pair.ToString(), "")); + } + } + else + { + ident.Value = ini.Read(ident.Section, ident.Key, ""); + } + } + + ConfigHelper.LoadConfigValue(current, idents); + return true; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return false; + } + } + + /// 保存到配置文件中去 + /// 文件名 + public override void Save(string filename) + { + if (filename.IsNullOrWhiteSpace()) + { + filename = DirEx.CurrentDir() + ConfigFile; + } + + if (filename.IsNullOrWhiteSpace()) + { + throw new ApplicationException($"未指定{typeof(TConfig).Name}的配置文件路径!"); + } + + ConcurrentDictionary idents = ConfigHelper.InitIdents(current); + foreach (var ident in idents.Values) + { + if (ident.Section.IsNullOrEmpty()) + { + ident.Section = "Setup"; + } + } + + ConfigHelper.SaveConfigValue(Current, idents); + List strs = new List { ";", "" }; + Dictionary> listidents = new Dictionary>(); + foreach (var ident in idents.Values) + { + string section = ident.IsList ? ident.Section + "-" + ident.Key : ident.Section; + + if (!listidents.ContainsKey(section)) + { + listidents.Add(section, new List()); + } + + listidents[section].Add(ident); + } + + foreach (var values in listidents) + { + strs.Add("[" + values.Key + "]"); + + SortedList slist = new SortedList(); + foreach (var ident in values.Value) + { + slist.Add(ident.Index, ident); + } + + foreach (var ident in slist.Values) + { + if (!ident.Description.IsNullOrEmpty()) + { + strs.Add(";"); + } + + if (ident.IsList) + { + for (int i = 0; i < ident.Values.Count; i++) + { + strs.Add("Value" + i + "=" + ident.Values[i]); + } + } + else + { + strs.Add(ident.Key + "=" + ident.Value); + } + } + + strs.Add(""); + } + + listidents.Clear(); + DirEx.CreateDir(Path.GetDirectoryName(filename)); + File.WriteAllLines(filename, strs.ToArray(), IniBase.IniEncoding); + } + + #endregion 加载 + } +} \ No newline at end of file diff --git a/SunnyUI/Common/UIniFile.cs b/SunnyUI/Common/UIniFile.cs new file mode 100644 index 00000000..a4eaf5c9 --- /dev/null +++ b/SunnyUI/Common/UIniFile.cs @@ -0,0 +1,801 @@ +/****************************************************************************** + * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。 + * CopyRight (C) 2012-2022 ShenYongHua(沈永华). + * QQ群:56829229 QQ:17612584 EMail:SunnyUI@QQ.Com + * + * Blog: https://www.cnblogs.com/yhuse + * Gitee: https://gitee.com/yhuse/SunnyUI + * GitHub: https://github.com/yhuse/SunnyUI + * + * SunnyUI.dll can be used for free under the GPL-3.0 license. + * If you use this code, please keep this note. + * 如果您使用此代码,请保留此说明。 + ****************************************************************************** + * 文件名称: UIniFile.cs + * 文件说明: INI 文件读取类 + * 当前版本: V3.1 + * 创建日期: 2020-01-01 + * + * 2020-01-01: V2.2.0 增加文件说明 +******************************************************************************/ + +using Sunny.UI.Win32; +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; + +namespace Sunny.UI +{ + /// + /// INI 文件读取基类 + /// + public abstract class IniBase : IDisposable + { + /// + /// 文件名 + /// + [Description("文件名")] + public string FileName { get; set; } //INI文件名 + + + /// + /// Ini文件编码格式 + /// + public static readonly Encoding IniEncoding = Encoding.Default; + + /// + /// 类的构造函数,文件名必须是完全路径,不能是相对路径 + /// + /// 文件名 + protected IniBase(string fileName) + { + //必须是完全路径,不能是相对路径 + FileName = fileName; + // 判断文件是否存在 + if (!File.Exists(fileName)) + { + //文件不存在,建立文件 + using (StreamWriter sw = new StreamWriter(fileName, false, IniEncoding)) + { + sw.Write(";"); + sw.WriteLine(""); + } + } + } + + /// + /// 确保资源的释放 + /// + ~IniBase() + { + ReleaseUnmanagedResources(); + } + + private void ReleaseUnmanagedResources() + { + UpdateFile(); + } + + /// + /// 析构函数 + /// + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + /// + /// 写字符串 + /// + /// The section. + /// The key. + /// The value. + /// 结果 + public bool Write(string section, string key, string value) + { + if (value == null) + { + value = ""; + } + + return Kernel.WritePrivateProfileString(IniEncoding.GetBytes(section), IniEncoding.GetBytes(key), IniEncoding.GetBytes(value), FileName); + } + + /// + /// 读取字符串 + /// + /// section + /// key + /// Normal + /// 结果 + public string ReadString(string section, string key, string Default) + { + return Read(section, key, Default); + } + + /// + /// 读取字符串 + /// + /// section + /// key + /// Normal + /// 结果 + public string Read(string section, string key, string Default) + { + byte[] buffer = new byte[2048]; + if (Default == null) + { + Default = ""; + } + + int bufLen = Kernel.GetPrivateProfileString(IniEncoding.GetBytes(section), IniEncoding.GetBytes(key), IniEncoding.GetBytes(Default), buffer, buffer.Length, FileName); + //必须设定0(系统默认的代码页)的编码方式,否则无法支持中文 + return IniEncoding.GetString(buffer, 0, bufLen).Trim(); + } + + /// + /// 获取指定的Section名称中的所有Key + /// + /// section + /// 结果 + public string[] GetKeys(string section) + { + StringCollection keyList = new StringCollection(); + GetKeys(section, keyList); + return keyList.Cast().ToArray(); + } + + /// + /// 从Ini文件中,读取所有的Sections的名称 + /// + public string[] Sections + { + get + { + StringCollection keyList = new StringCollection(); + GetSections(keyList); + return keyList.Cast().ToArray(); + } + } + + /// + /// 从Ini文件中,将指定的Section名称中的所有Key添加到列表中 + /// + /// section + /// keys + private void GetKeys(string section, StringCollection keys) + { + byte[] buffer = new byte[65535]; + int bufLen = Kernel.GetPrivateProfileString(IniEncoding.GetBytes(section), null, null, buffer, 65535, FileName); + //对Section进行解析 + GetStringsFromBuffer(buffer, bufLen, keys); + } + + private void GetStringsFromBuffer(byte[] buffer, int bufLen, StringCollection strings) + { + strings.Clear(); + if (bufLen == 0) + { + return; + } + + int start = 0; + for (int i = 0; i < bufLen; i++) + { + if ((buffer[i] == 0) && ((i - start) > 0)) + { + string s = IniEncoding.GetString(buffer, start, i - start); + strings.Add(s); + start = i + 1; + } + } + } + + /// + /// 从Ini文件中,读取所有的Sections的名称 + /// + /// sectionList + private void GetSections(StringCollection sectionList) + { + //Note:必须得用Bytes来实现,StringBuilder只能取到第一个Section + byte[] buffer = new byte[65535]; + int bufLen = Kernel.GetPrivateProfileString(null, null, null, buffer, buffer.GetUpperBound(0), FileName); + GetStringsFromBuffer(buffer, bufLen, sectionList); + } + + /// + /// 读取指定的Section的所有Value到列表中 + /// + /// section + public NameValueCollection GetSectionValues(string section) + { + NameValueCollection values = new NameValueCollection(); + StringCollection keyList = new StringCollection(); + GetKeys(section, keyList); + values.Clear(); + foreach (string key in keyList) + { + values.Add(key, Read(section, key, "")); + } + + return values; + } + + /// + /// 清除某个Section + /// + /// section + public void EraseSection(string section) + { + if (!Kernel.WritePrivateProfileString(IniEncoding.GetBytes(section), null, null, FileName)) + { + throw (new ApplicationException("无法清除Ini文件中的Section")); + } + } + + /// + /// 删除某个Section下的键 + /// + /// section + /// key + public void DeleteKey(string section, string key) + { + Kernel.WritePrivateProfileString(IniEncoding.GetBytes(section), IniEncoding.GetBytes(key), null, FileName); + } + + /// + /// Note:对于Win9X,来说需要实现UpdateFile方法将缓冲中的数据写入文件 + /// 在Win NT, 2000和XP上,都是直接写文件,没有缓冲,所以,无须实现UpdateFile + /// 执行完对Ini文件的修改之后,应该调用本方法更新缓冲区。 + /// + public void UpdateFile() + { + Kernel.WritePrivateProfileString(null, null, null, FileName); + } + + /// + /// 检查某个Section下的某个键值是否存在 + /// + /// section + /// key + /// 结果 + public bool KeyExists(string section, string key) + { + StringCollection keys = new StringCollection(); + GetKeys(section, keys); + return keys.IndexOf(key) > -1; + } + } + + /// + /// IniFile的类 + /// + public class IniFile : IniBase + { + /// + /// 写结构 + /// + /// section + /// key + /// value + /// T + public void WriteStruct(string section, string key, T value) where T : struct + { + Write(section, key, value.StructToBytes()); + } + + /// + /// 读结构 + /// + /// T + /// section + /// key + /// Normal + /// 结果 + public T ReadStruct(string section, string key, T Default) where T : struct + { + //得到结构体的大小 + int size = Default.Size(); + byte[] bytes = Read(section, key, "").ToHexBytes(); + return size > bytes.Length ? Default : bytes.ToStruct(); + } + + /// + /// 写Byte数组 + /// + /// section + /// key + /// value + public void Write(string section, string key, byte[] value) + { + Write(section, key, value.ToHexString()); + } + + /// + /// 读Byte数组 + /// + /// section + /// key + /// Normal + /// 结果 + public byte[] ReadBytes(string section, string key, byte[] Default) + { + return Read(section, key, Default.ToHexString()).ToHexBytes(); + } + + /// + /// 写Char + /// + /// section + /// key + /// value + public void Write(string section, string key, char value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读Char + /// + /// section + /// key + /// Normal + /// 结果 + public char ReadChar(string section, string key, char Default = ' ') + { + return Read(section, key, Default.ToString()).ToChar(Default); + } + + /// + /// 写Decimal + /// + /// section + /// key + /// value + public void Write(string section, string key, decimal value) + { + Write(section, key, value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// 读Decimal + /// + /// section + /// key + /// Normal + /// 结果 + public decimal ReadDecimal(string section, string key, decimal Default = 0) + { + return Read(section, key, Default.ToString(CultureInfo.InvariantCulture)).ToDecimal(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, short value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public short ReadShort(string section, string key, short Default = 0) + { + return Read(section, key, Default.ToString()).ToShort(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, ushort value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public ushort ReadUShort(string section, string key, ushort Default = 0) + { + return Read(section, key, Default.ToString()).ToUShort(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, int value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public int ReadInt(string section, string key, int Default = 0) + { + return Read(section, key, Default.ToString()).ToInt(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, uint value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public uint ReadUInt(string section, string key, uint Default = 0) + { + return Read(section, key, Default.ToString()).ToUInt(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, ulong value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public ulong ReadULong(string section, string key, ulong Default = 0) + { + return Read(section, key, Default.ToString()).ToULong(Default); + } + + /// + /// 写整数 + /// + /// section + /// key + /// value + public void Write(string section, string key, long value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读整数 + /// + /// section + /// key + /// Normal + /// 结果 + public long ReadLong(string section, string key, long Default = 0) + { + return Read(section, key, Default.ToString()).ToLong(Default); + } + + /// + /// 写布尔 + /// + /// section + /// key + /// value + public void Write(string section, string key, bool value) + { + Write(section, key, value ? bool.TrueString : bool.FalseString); + } + + /// + /// 读布尔 + /// + /// section + /// key + /// Normal + /// 结果 + public bool ReadBool(string section, string key, bool Default = false) + { + string str = Read(section, key, Default.ToString()); + if (string.Equals(str, bool.TrueString, StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + + if (string.Equals(str, bool.FalseString, StringComparison.CurrentCultureIgnoreCase)) + { + return false; + } + + return Default; + } + + /// + /// 写Double + /// + /// section + /// key + /// value + public void Write(string section, string key, double value) + { + Write(section, key, value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// 读Double + /// + /// section + /// key + /// Normal + /// 结果 + public double ReadDouble(string section, string key, double Default = 0) + { + return Read(section, key, Default.ToString(CultureInfo.InvariantCulture)).ToDouble(Default); + } + + /// + /// 写Float + /// + /// section + /// key + /// value + public void Write(string section, string key, float value) + { + Write(section, key, value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// 读Float + /// + /// section + /// key + /// Normal + /// 结果 + public float ReadFloat(string section, string key, float Default = 0) + { + return Read(section, key, Default.ToString(CultureInfo.InvariantCulture)).ToFloat(Default); + } + + /// + /// 写Byte + /// + /// section + /// key + /// value + public void Write(string section, string key, byte value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读Byte + /// + /// section + /// key + /// Normal + /// 结果 + public byte ReadByte(string section, string key, byte Default = 0) + { + return Read(section, key, Default.ToString()).ToByte(Default); + } + + /// + /// 写SByte + /// + /// section + /// key + /// value + public void Write(string section, string key, sbyte value) + { + Write(section, key, value.ToString()); + } + + /// + /// 读Byte + /// + /// section + /// key + /// Normal + /// 结果 + public sbyte ReadSByte(string section, string key, sbyte Default = 0) + { + return Read(section, key, Default.ToString()).ToSByte(Default); + } + + /// + /// 写DateTime + /// + /// section + /// key + /// value + public void Write(string section, string key, DateTime value) + { + Write(section, key, value.ToString(DateTimeEx.DateTimeFormat)); + } + + /// + /// 读DateTime + /// + /// section + /// key + /// Normal + /// 结果 + public DateTime ReadDateTime(string section, string key, DateTime Default) + { + string str = Read(section, key, Default.ToString(CultureInfo.InvariantCulture)); + try + { + return str.ToDateTime(DateTimeEx.DateTimeFormat); + } + catch (Exception) + { + return Default; + } + } + + /// + /// 写Point + /// + /// section + /// key + /// value + public void Write(string section, string key, Point value) + { + Write(section, key, ConvertEx.ObjectToString(value, typeof(Point))); + } + + /// + /// 读Point + /// + /// section + /// key + /// Normal + /// 结果 + public Point ReadPoint(string section, string key, Point Default) + { + string str = Read(section, key, ""); + return (Point)ConvertEx.StringToObject(str, typeof(Point), Default); + } + + /// + /// 写PointF + /// + /// section + /// key + /// value + public void Write(string section, string key, PointF value) + { + Write(section, key, ConvertEx.ObjectToString(value, typeof(PointF))); + } + + /// + /// 读PointF + /// + /// section + /// key + /// Normal + /// 结果 + public PointF ReadPointF(string section, string key, PointF Default) + { + string str = Read(section, key, ""); + return (PointF)ConvertEx.StringToObject(str, typeof(PointF), Default); + } + + /// + /// 写Size + /// + /// section + /// key + /// value + public void Write(string section, string key, Size value) + { + Write(section, key, ConvertEx.ObjectToString(value, typeof(Size))); + } + + /// + /// 读Size + /// + /// section + /// key + /// Normal + /// 结果 + public Size ReadSize(string section, string key, Size Default) + { + string str = Read(section, key, ""); + return (Size)ConvertEx.StringToObject(str, typeof(Size), Default); + } + + /// + /// 写SizeF + /// + /// section + /// key + /// value + public void Write(string section, string key, SizeF value) + { + Write(section, key, ConvertEx.ObjectToString(value, typeof(SizeF))); + } + + /// + /// 读SizeF + /// + /// section + /// key + /// Normal + /// 结果 + public SizeF ReadSizeF(string section, string key, SizeF Default) + { + string str = Read(section, key, ""); + return (SizeF)ConvertEx.StringToObject(str, typeof(SizeF), Default); + } + + /// + /// 写Color + /// + /// section + /// key + /// value + public void Write(string section, string key, Color value) + { + Write(section, key, ConvertEx.ObjectToString(value, typeof(Color))); + } + + /// + /// 读Color + /// + /// section + /// key + /// Normal + /// 结果 + public Color ReadColor(string section, string key, Color Default) + { + string str = Read(section, key, ""); + return (Color)ConvertEx.StringToObject(str, typeof(Color), Default); + } + + /// + /// 构造函数 + /// + /// 文件名 + public IniFile(string fileName) : base(fileName) + { + } + } +} \ No newline at end of file diff --git a/SunnyUI/Controls/UIGlobal.cs b/SunnyUI/Controls/UIGlobal.cs index 5ddcea98..aaeb0af7 100644 --- a/SunnyUI/Controls/UIGlobal.cs +++ b/SunnyUI/Controls/UIGlobal.cs @@ -31,7 +31,7 @@ namespace Sunny.UI /// /// 版本 /// - public const string Version = "SunnyUI.Net V3.2.5"; + public const string Version = "SunnyUI.Net V3.2.6"; public const int EditorMinHeight = 20; public const int EditorMaxHeight = 60; diff --git a/SunnyUI/SunnyUI.csproj b/SunnyUI/SunnyUI.csproj index 4e0525fc..70b90172 100644 --- a/SunnyUI/SunnyUI.csproj +++ b/SunnyUI/SunnyUI.csproj @@ -9,7 +9,7 @@ SunnyUI.Net 是基于.Net Framework 4.0~4.8、.Net 6 框架的 C# WinForm 开源控件库、工具类库、扩展类库、多页面开发框架。 CopyRight © SunnyUI.Net 2012-2022 GPL-3.0-only - 3.2.5 + 3.2.6.1 ShenYonghua SunnyUI.Net SunnyUI @@ -75,7 +75,7 @@ - +