chr
2025-02-24 104cad5ee53eb05d3a89d508a029425726ae89ae
上传长时间代码;
29个文件已修改
182个文件已添加
8 文件已重命名
3个文件已删除
33835 ■■■■■ 已修改文件
HMI.AlermInfoManage/AlermInfoManage.xaml 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage.xaml.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Entity/AimSetting.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/HMI.AlermInfoManage.csproj 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/AssemblyInfo.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/HMI.Base.csproj 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/PluginControl.cs 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/PluginInfo.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/PluginLoader.cs 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Base/packages.config 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Annotation/InitAttribute.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IConfigRefresh.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IFolderOwner.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IPlugin.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IProgram.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/ISequnce.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IStep.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Api/IStepRunner.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/NotifyBase.cs 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Entity/Param.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Event/TabChange.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Exceptions/FormValidateException.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/HMI.Core.csproj 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Properties/AssemblyInfo.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Setting/SystemSetting.cs 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/BeanUtil.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/DllHelper.cs 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/PathHelper.cs 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/SqliteHelper.cs 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/UtilContainer.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/Util/XmlHelper.cs 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/app.config 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.Core/packages.config 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DataViewer.xaml 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/DataViewer.xaml.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/HMI.DataViewer.csproj 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.DataViewer/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Entity/DioData.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/HMI.IOMonitor.csproj 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/IOMonitor.xaml.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.IOMonitor/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/MenuData.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/RoleData.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/RoleMenuRel.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/UserData.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Entity/UserRoleRel.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/HMI.MenuManage.csproj 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/MenuManage.xaml.cs 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/MenuModal.xaml 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/MenuModal.xaml.cs 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/RoleModal.xaml 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/RoleModal.xaml.cs 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/UserModal.xaml 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Modal/UserModal.xaml.cs 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/MenuManageModel.cs 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/RoleManageModel.cs 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Model/UserManageModel.cs 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/AssemblyInfo.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/Resources.Designer.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/Settings.Designer.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml.cs 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/UserManage.xaml.cs 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Util/RoleHelper.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Util/RoleMenuHelper.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/Util/UserRoleRelHelper.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ControlModel.cs 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Entity/ParameterData.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Entity/ProductData.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/HMI.ParameterManage.csproj 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterManage.xaml 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterManage.xaml.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ParameterModal.xaml.cs 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ProductModal.xaml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/ProductModal.xaml.cs 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.ParameterManage/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/HMI.SettingControl.csproj 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SettingControl.xaml 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SettingControl.xaml.cs 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SystemConfig.xaml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/SystemConfig.xaml.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.SettingControl/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Converter/DataGridRowNumberConverter.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Converter/PathImageConverter.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/DpPlugin.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/FormGroupHelper.cs 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/StackPanelHelper.cs 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/DpUtil/TreeViewHelper.cs 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Entity/NotifyBase.cs 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Properties/Resources.Designer.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Properties/Settings.Designer.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Font/Font.xaml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Font/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Resources/Icon/xinzeng_1.png 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/BaseWindow.xaml 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/BaseWindow.xaml.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/Colors.xaml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/FormStyle.xaml 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/MsgBoxStyle.xaml 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/PanuonResource.xaml 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/WindowResources.xaml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/IValueValidator.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/MsgBoxHelper.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/ToastHelper.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/packages.config 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/App.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/App.xaml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/App.xaml.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/HMI.UnitTest.csproj 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/Properties/Resources.Designer.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/Properties/Settings.Designer.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/packages.config 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Libs/DatabaseHelper.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/SQLite.Interop.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/System.Data.SQLite.EF6.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/System.Data.SQLite.Linq.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/System.Data.SQLite.dll 补丁 | 查看 | 原始文档 | blame | 历史
Libs/System.Data.SQLite.dll.config 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Libs/System.Data.SQLite.xml 21379 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Core/VisionBuilder.Core.csproj 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Engine/VisionBuilder.Engine.csproj 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Engine/app.config 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/AssemblyInfo.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Resources.Designer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Run/VisionBuilder.Run.csproj 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/PluginOne.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/VisionBuilder.Test.csproj 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
VisionBuilder.Test/app.config 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.config 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.xaml.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/IconTabItem.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/InnerWindow.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MainTabControl.xaml 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MainTabControl.xaml.cs 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MenuTabControl.xaml 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MenuTabControl.xaml.cs 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Controls/MinInnerWindow.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Entity/PluginLoadMessage.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Entity/PluginSettingInfo.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Icon/xinzeng_1.png 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MainWindow.xaml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MainWindow.xaml.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/NuGetUpgradeLog.html 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/VisionBuilder.Core.csproj 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/packages.config 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Models/Program.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Models/Sequence.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Models/Step.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/PanuonWindow.xaml.cs 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Properties/AssemblyInfo.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Style/Colors.xaml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Style/CommonTree.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Style/MainTab.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/TestWindow.xaml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/TestWindow.xaml.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/BottomBar.xaml.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/StepSetting.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/Test/FunctionSetting.xaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/Test/SequnceList.xaml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/WorkControl.xaml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/UI/WorkControl.xaml.cs 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/IPlugin.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/MainToastHandler.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Utils/Resize.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/log4net.config 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/packages.config 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.AlermInfoManage/AlermInfoManage.xaml
New file
@@ -0,0 +1,96 @@
<hb:PluginControl x:Class="HMI.AlermInfoManage.AlermInfoManage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.AlermInfoManage"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             mc:Ignorable="d"
             Background="{StaticResource WorkspaceBackground}"
             Loaded="UserControl_Loaded"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Style/BaseWindow.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/FormStyle.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/MsgBoxStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Border Style="{StaticResource CardBoarder}">
        <StackPanel>
            <DockPanel Margin="0,0,0,10">
                <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="报警信息列表"/>
            </DockPanel>
            <DataGrid x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="#" CanUserResize="False">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Width="*" Binding="{Binding Source}" Header="来源" />
                    <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编码" />
                    <DataGridTextColumn Width="*" Binding="{Binding Info}" Header="信息" />
                    <DataGridTextColumn Width="*" Binding="{Binding Time}" Header="时间" />
                    <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Button x:Name="editBtn" Style="{StaticResource LinkButton}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                    <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                </StackPanel>
                                <DataTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Path=IsMouseOver,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                        <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding Path=IsSelected,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                        <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                    </DataTrigger>
                                </DataTemplate.Triggers>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </Border>
</hb:PluginControl>
HMI.AlermInfoManage/AlermInfoManage.xaml.cs
New file
@@ -0,0 +1,27 @@
using System.Windows;
using HMI.Base;
namespace HMI.AlermInfoManage
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class AlermInfoManage : PluginControl
    {
        public AlermInfoManage()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
        }
    }
}
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml
New file
@@ -0,0 +1,29 @@
<UserControl x:Class="HMI.AlermInfoManage.AlermInfoManage_Setting"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:HMI.AlermInfoManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
             Loaded="UserControl_Loaded"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <StackPanel>
            <GroupBox Header="PLC设置">
                <StackPanel>
                    <pu:FormGroup Header="PLC IP" GroupName="MyFormGroup">
                        <TextBox Text="{Binding PlcIp}"/>
                    </pu:FormGroup>
                    <pu:FormGroup Header="启用PLC" GroupName="MyFormGroup">
                        <CheckBox IsChecked="{Binding PlcEnabled,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                    </pu:FormGroup>
                </StackPanel>
            </GroupBox>
            <Button Click="Button_Click">保存</Button>
        </StackPanel>
    </Grid>
</UserControl>
HMI.AlermInfoManage/AlermInfoManage_Setting.xaml.cs
New file
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using HMI.AlermInfoManage.Entity;
using HMI.Core.Util;
namespace HMI.AlermInfoManage
{
    /// <summary>
    /// AlermInfoManage_Setting.xaml 的交互逻辑
    /// </summary>
    public partial class AlermInfoManage_Setting : UserControl
    {
        public AlermInfoManage_Setting()
        {
            InitializeComponent();
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            DataContext = AimSetting.Instance.GetModel();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            AimSettingModel model = (AimSettingModel)DataContext;
            AimSetting newSetting = AimSetting.FromModel(model);
            XmlHelper.SerializeToXml(PathHelper.ConfigFolder + "\\AimSetting.xml", model);
        }
    }
}
HMI.AlermInfoManage/Entity/AimSetting.cs
New file
@@ -0,0 +1,65 @@
using HMI.Core.Entity;
using HMI.Core.Util;
namespace HMI.AlermInfoManage.Entity
{
    public class AimSetting
    {
        public static AimSetting Instance { get; private set; }
        public string PlcIp { get; private set; }
        public bool PlcEnabled { get; private set; }
        private AimSetting() { }
        static AimSetting()
        {
            LoadFromXml();
        }
        public static void LoadFromXml()
        {
            try
            {
                string path = PathHelper.ConfigFolder + "\\AimSetting.xml";
                AimSettingModel model = XmlHelper.DeserializeFromXml<AimSettingModel>(path);
                Instance = FromModel(model);
            }
            catch
            {
                Instance = new AimSetting();
            }
        }
        public AimSettingModel GetModel()
        {
            return new AimSettingModel() { PlcIp = PlcIp, PlcEnabled = PlcEnabled };
        }
        public static AimSetting FromModel(AimSettingModel model)
        {
            return new AimSetting() { PlcIp = model.PlcIp, PlcEnabled = model.PlcEnabled };
        }
    }
    public class AimSettingModel : NotifyBase
    {
        private string plcIp;
        public string PlcIp
        {
            get => plcIp;
            set
            {
                Set(ref plcIp, value);
            }
        }
        private bool plcEnabled;
        public bool PlcEnabled
        {
            get { return plcEnabled; }
            set { Set(ref plcEnabled, value); }
        }
    }
}
HMI.AlermInfoManage/HMI.AlermInfoManage.csproj
New file
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{945C2577-4558-45E3-891A-096CB3047304}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.AlermInfoManage</RootNamespace>
    <AssemblyName>HMI.AlermInfoManage</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="AlermInfoManage.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="AlermInfoManage.xaml.cs">
      <DependentUpon>AlermInfoManage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="AlermInfoManage_Setting.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AlermInfoManage_Setting.xaml.cs">
      <DependentUpon>AlermInfoManage_Setting.xaml</DependentUpon>
    </Compile>
    <Compile Include="Entity\AimSetting.cs" />
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.AlermInfoManage/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,54 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using HMI.Core.Annotation;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.AlermInfoManage")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.AlermInfoManage")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: PluginClassName("HMI.AlermInfoManage.AlermInfoManage")]
HMI.AlermInfoManage/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.AlermInfoManage.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.AlermInfoManage.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.AlermInfoManage/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.AlermInfoManage/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.AlermInfoManage.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.AlermInfoManage/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.AlermInfoManage/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.Base/HMI.Base.csproj
New file
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{2A6F9720-A890-4036-9D73-41A0DAE28341}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.Base</RootNamespace>
    <AssemblyName>HMI.Base</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="PluginControl.cs" />
    <Compile Include="PluginInfo.cs" />
    <Compile Include="PluginLoader.cs" />
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.Base/PluginControl.cs
New file
@@ -0,0 +1,68 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using HMI.Core.Api;
using log4net;
namespace HMI.Base
{
    public class PluginControl : UserControl, IPlugin, INotifyPropertyChanged
    {
        #region ...
        public event PropertyChangedEventHandler PropertyChanged;
        protected void Set([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                Set(propertyName);
            }
        }
        #endregion
        protected ILog log;
        protected string BaseFolder;
        public virtual object Active()
        {
            return null;
        }
        public virtual object DeActive()
        {
            return null;
        }
        public virtual object Init()
        {
            return null;
        }
        public virtual void OnTabChange(object sender, object args)
        {
        }
        public virtual void SetBaseFolder(string folder)
        {
            BaseFolder = folder;
        }
        public virtual void SetLogger(ILog logger)
        {
            log = logger;
        }
        public virtual void InitFinish()
        {
        }
    }
}
HMI.Base/PluginInfo.cs
New file
@@ -0,0 +1,25 @@
using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
namespace HMI.Base
{
    public class PluginInfo
    {
        public PluginInfo()
        {
        }
        public bool IsFont { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public string ParentCode { get; set; }
        public string DllPath { get; set; }
        public string ClassName { get; set; }
        public ObservableCollection<PluginInfo> Children { get; set; } = new ObservableCollection<PluginInfo>();
        public BitmapImage Icon { get; set; }
        public object Content;
        public object SettingControl;
    }
}
HMI.Base/PluginLoader.cs
New file
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Reflection;
using System.Windows.Media.Imaging;
using HMI.Core.Api;
using HMI.Core.Util;
using log4net;
namespace HMI.Base
{
    public class PluginLoader
    {
        private static string PluginFolder = PathHelper.PluginFolder();
        public bool Completed { get; private set; } = false;
        public SqliteHelper sqliteHelper;
        public ObservableCollection<PluginInfo> Menus { get; private set; }
        public ObservableCollection<PluginInfo> Plugins { get; private set; }
        public static PluginLoader Instance { get; private set; }
        public event EventHandler<string> LoadMessageAppend;
        private PluginLoader()
        {
            sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
        }
        public static PluginLoader GetLoader()
        {
            if (Instance == null)
            {
                Instance = new PluginLoader();
            }
            return Instance;
        }
        public void LoadMenuAndPlugin()
        {
            if (Menus == null)
            {
                Plugins = new ObservableCollection<PluginInfo>();
                Menus = LoadTreeMenu();
            }
        }
        private void LoadChildrenMenu(PluginInfo menu)
        {
            LoadMessageAppend?.Invoke(this, $"正在加载{menu.Name}菜单...");
            try
            {
                if (!string.IsNullOrEmpty(menu.DllPath))
                {
                    string dllPath = Path.Combine(PluginFolder, menu.DllPath);
                    Assembly ass = Assembly.LoadFrom(dllPath);
                    Type ClassType = ass.GetType(menu.ClassName);
                    object Instance = Activator.CreateInstance(ClassType);
                    menu.Content = Instance;
                    if (Instance is IPlugin plugin)
                    {
                        plugin.SetLogger(LogManager.GetLogger(menu.Code));
                        string configFolder = Path.Combine(PathHelper.ConfigFolder, menu.Code);
                        if (!Directory.Exists(configFolder))
                        {
                            Directory.CreateDirectory(configFolder);
                        }
                        plugin.SetBaseFolder(configFolder);
                        plugin.InitFinish();
                    }
                    Type SettingType = ass.GetType($"{menu.ClassName}_Setting");
                    if (SettingType != null)
                    {
                        object SettingInstance = Activator.CreateInstance(SettingType);
                        menu.SettingControl = SettingInstance;
                    }
                }
            }
            catch (Exception ex)
            {
                LoadMessageAppend?.Invoke(this, $"{ex.Message} {ex.StackTrace}");
            }
        }
        public ObservableCollection<PluginInfo> LoadTreeMenu()
        {
            ObservableCollection<PluginInfo> data = new ObservableCollection<PluginInfo>();
            Dictionary<string, PluginInfo> cache = new Dictionary<string, PluginInfo>();
            try
            {
                LoadMessageAppend?.Invoke(this, "开始加载菜单...");
                DataTable dt = sqliteHelper.GetDataTable(@"SELECT * FROM menu ORDER BY level,id;");
                if (dt != null)
                {
                    LoadMessageAppend?.Invoke(this, $"共有{dt.Rows.Count}条菜单待加载...");
                    foreach (DataRow dr in dt.Rows)
                    {
                        string path = Path.Combine(PathHelper.IconFolder, dr["iconPath"].ToString());
                        var menu = new PluginInfo
                        {
                            Code = dr["code"].ToString(),
                            Name = dr["name"].ToString(),
                            ParentCode = dr["parentCode"].ToString(),
                            DllPath = dr["dllPath"].ToString(),
                            ClassName = dr["className"].ToString(),
                            Icon = new BitmapImage(new Uri(Path.Combine(PathHelper.IconFolder, dr["iconPath"].ToString())))
                        };
                        cache[menu.Code] = menu;
                        if (!cache.ContainsKey(menu.ParentCode))
                        {
                            data.Add(menu);
                        }
                        else
                        {
                            cache[menu.ParentCode].Children.Add(menu);
                        }
                        Plugins.Add(menu);
                        LoadChildrenMenu(menu);
                    }
                }
            }
            catch (Exception ex)
            {
                LoadMessageAppend?.Invoke(this, ex.Message);
            }
            return data;
        }
    }
}
HMI.Base/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.Base")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.Base")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.Base/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.Base.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.Base.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.Base/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.Base/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.Base.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.Base/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.Base/packages.config
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
</packages>
HMI.Core/Annotation/InitAttribute.cs
New file
@@ -0,0 +1,18 @@
using System;
using System.Runtime.InteropServices;
namespace HMI.Core.Annotation
{
    [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
    [ComVisible(true)]
    public class PluginClassNameAttribute : Attribute
    {
        public string ClassName { get; private set; }
        public PluginClassNameAttribute(string ClassName)
        {
            this.ClassName = ClassName;
        }
    }
}
HMI.Core/Api/IConfigRefresh.cs
New file
@@ -0,0 +1,7 @@
namespace HMI.Core.Api
{
    public interface IConfigRefresh
    {
        void OnConfigRefrsh(object sender, object setting);
    }
}
HMI.Core/Api/IFolderOwner.cs
New file
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.Core.Api
{
    public interface IFolderOwner
    {
        void SetBaseFolder(string folder);
    }
}
HMI.Core/Api/IPlugin.cs
New file
@@ -0,0 +1,34 @@
using log4net;
namespace HMI.Core.Api
{
    public interface IPlugin : IFolderOwner
    {
        /// <summary>
        /// 初始化
        /// </summary>
        /// <returns></returns>
        object Init();
        void InitFinish();
        /// <summary>
        /// 插件面板被激活时的操作
        /// </summary>
        /// <returns></returns>
        object Active();
        /// <summary>
        /// 失活时的操作
        /// </summary>
        /// <returns></returns>
        object DeActive();
        /// <summary>
        /// 面板切换事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void OnTabChange(object sender, object args);
        void SetLogger(ILog logger);
    }
}
HMI.Core/Api/IProgram.cs
File was renamed from VisionBuilder.Core/Api/IProgram.cs
@@ -1,4 +1,4 @@
namespace VisionBuilder.Core.Api
namespace HMI.Core.Api
{
    public interface IProgram
    {
HMI.Core/Api/ISequnce.cs
File was renamed from VisionBuilder.Core/Api/ISequnce.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace VisionBuilder.Core.Api
namespace HMI.Core.Api
{
    public interface ISequence
    {
HMI.Core/Api/IStep.cs
File was renamed from VisionBuilder.Core/Api/IStep.cs
@@ -1,4 +1,4 @@
namespace VisionBuilder.Core.Api
namespace HMI.Core.Api
{
    public interface IStep
    {
HMI.Core/Api/IStepRunner.cs
File was renamed from VisionBuilder.Core/Api/IStepRunner.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using VisionBuilder.Core.Entity;
using HMI.Core.Entity;
namespace VisionBuilder.Core.Api
namespace HMI.Core.Api
{
    public interface IStepRunner
    {
HMI.Core/Entity/NotifyBase.cs
New file
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace HMI.Core.Entity
{
    public class NotifyBase : INotifyPropertyChanged
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void Set([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                Set(propertyName);
            }
        }
        #endregion
    }
    public class RelayCommand<T> : ICommand
    {
        /// <summary>
        /// 命令能否执行
        /// </summary>
        readonly Func<bool> _canExecute;
        /// <summary>
        /// 命令执行的方法
        /// </summary>
        readonly Action<T> _execute;
        /// <summary>
        /// 命令的构造函数
        /// </summary>
        /// <param name="action">命令需执行的方法</param>
        /// <param name="canExecute">命令是否可以执行的方法</param>
        public RelayCommand(Action<T> action, Func<bool> canExecute)
        {
            _execute = action;
            _canExecute = canExecute;
        }
        public RelayCommand(Action<T> action)
        {
            _execute = action;
        }
        /// <summary>
        /// 判断命令是否可以执行
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(Object parameter)
        {
            if (_canExecute == null)
                return true;
            return _canExecute();
        }
        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(Object parameter)
        {
            _execute((T)parameter);
        }
        /// <summary>
        /// 事件追加、移除
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }
    }
}
HMI.Core/Entity/Param.cs
File was renamed from VisionBuilder.Core/Entity/Param.cs
@@ -1,4 +1,4 @@
namespace VisionBuilder.Core.Entity
namespace HMI.Core.Entity
{
    public class Param
    {
HMI.Core/Event/TabChange.cs
File was renamed from WPFProject/Events/TabChange.cs
@@ -5,7 +5,7 @@
using System.Threading.Tasks;
using System.Windows;
namespace WPFProject.Events
namespace HMI.Core.Api
{
    public class TabChangeEventArgs
    {
HMI.Core/Exceptions/FormValidateException.cs
New file
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.Core.Exceptions
{
    public class FormValidateException : Exception
    {
        public readonly List<FormGroupValidateResult> Result;
        public bool Success
        {
            get => Result == null || Result.Count == 0;
        }
        public FormValidateException()
        {
            Result = new List<FormGroupValidateResult>();
        }
    }
    public class FormGroupValidateResult
    {
        public readonly string Message;
        public readonly object Sender;
        public FormGroupValidateResult(string message, object sender)
        {
            Message = message;
            Sender = sender;
        }
    }
}
HMI.Core/HMI.Core.csproj
New file
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{3E3BF44A-0B69-45C5-9512-528CB48A7106}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>HMI.Core</RootNamespace>
    <AssemblyName>HMI.Core</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="PresentationCore" />
    <Reference Include="System" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data.SQLite">
      <HintPath>..\Libs\System.Data.SQLite.dll</HintPath>
    </Reference>
    <Reference Include="System.Data.SQLite.EF6">
      <HintPath>..\Libs\System.Data.SQLite.EF6.dll</HintPath>
    </Reference>
    <Reference Include="System.Data.SQLite.Linq">
      <HintPath>..\Libs\System.Data.SQLite.Linq.dll</HintPath>
    </Reference>
    <Reference Include="System.Numerics" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Api\IConfigRefresh.cs" />
    <Compile Include="Api\IFolderOwner.cs" />
    <Compile Include="Api\IPlugin.cs" />
    <Compile Include="Api\IProgram.cs" />
    <Compile Include="Api\ISequnce.cs" />
    <Compile Include="Api\IStep.cs" />
    <Compile Include="Api\IStepRunner.cs" />
    <Compile Include="Annotation\InitAttribute.cs" />
    <Compile Include="Entity\NotifyBase.cs" />
    <Compile Include="Entity\Param.cs" />
    <Compile Include="Event\TabChange.cs" />
    <Compile Include="Exceptions\FormValidateException.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Setting\SystemSetting.cs" />
    <Compile Include="Util\BeanUtil.cs" />
    <Compile Include="Util\DllHelper.cs" />
    <Compile Include="Util\PathHelper.cs" />
    <Compile Include="Util\SqliteHelper.cs" />
    <Compile Include="Util\UtilContainer.cs" />
    <Compile Include="Util\XmlHelper.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="app.config" />
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.Core/Properties/AssemblyInfo.cs
File was renamed from VisionBuilder.Core/Properties/AssemblyInfo.cs
@@ -5,11 +5,11 @@
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VisionBuilder.Core")]
[assembly: AssemblyTitle("HMI.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("VisionBuilder.Core")]
[assembly: AssemblyProduct("HMI.Core")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
HMI.Core/Setting/SystemSetting.cs
New file
@@ -0,0 +1,78 @@
using System;
using System.IO;
using HMI.Core.Entity;
using HMI.Core.Util;
namespace HMI.Core.Setting
{
    public class SystemSetting
    {
        public static SystemSetting Instance { get; private set; }
        public static event EventHandler<SystemSetting> SystemSettingChanged;
        static SystemSetting()
        {
            LoadFromXml();
        }
        private SystemSetting()
        {
        }
        public string LogoPath { get; private set; }
        public string WindowTitle { get; private set; }
        private static void LoadFromXml()
        {
            if (Instance != null) return;
            string XmlFilePath = PathHelper.ConfigFolder + "\\SystemConfig.xml";
            if (!File.Exists(XmlFilePath))
            {
                Instance = new SystemSetting();
            }
            else
            {
                SystemSettingModel model = XmlHelper.DeserializeFromXml<SystemSettingModel>(XmlFilePath);
                SystemSetting setting = new SystemSetting();
                BeanUtil.Copy(model, setting);
                Instance = setting;
            }
        }
        public static void ReLoad()
        {
            string XmlFilePath = PathHelper.ConfigFolder + "\\SystemConfig.xml";
            if (!File.Exists(XmlFilePath))
            {
                Instance = new SystemSetting();
            }
            else
            {
                SystemSettingModel model = XmlHelper.DeserializeFromXml<SystemSettingModel>(XmlFilePath);
                SystemSetting setting = new SystemSetting();
                BeanUtil.Copy(model, setting);
                Instance = setting;
            }
            SystemSettingChanged?.Invoke(null, Instance);
        }
    }
    public class SystemSettingModel : NotifyBase
    {
        private string logoPath;
        public string LogoPath
        {
            get => logoPath;
            set { Set(ref logoPath, value); }
        }
        private string windowTitle;
        public string WindowTitle
        {
            get => windowTitle;
            set { Set(ref windowTitle, value); }
        }
    }
}
HMI.Core/Util/BeanUtil.cs
New file
@@ -0,0 +1,24 @@
using System;
using System.Reflection;
using System.Xml;
namespace HMI.Core.Util
{
    public class BeanUtil
    {
        public static void Copy<S, T>(S source, T target)
        {
            Type sourceType = typeof(S);
            Type targetType = typeof(T);
            PropertyInfo[] fieldArray = targetType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (PropertyInfo field in fieldArray)
            {
                string fileName = field.Name;
                PropertyInfo sourceField = sourceType.GetProperty(fileName);
                if (sourceField == null) continue;
                field.SetValue(target, sourceField.GetValue(source));
            }
        }
    }
}
HMI.Core/Util/DllHelper.cs
New file
@@ -0,0 +1,197 @@
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CSharp;
namespace HMI.Core.Util
{
    /// <summary>
    /// DLL文件操作类
    /// </summary>
    public class DllHelper
    {
        /// <summary>
        /// 获取当前DLL所处的路径
        /// </summary>
        /// <returns>Dll路径</returns>
        public static string GetAssemblyPath()
        {
            string CodeBase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
            CodeBase = CodeBase.Substring(8, CodeBase.Length - 8);    // 8是 file:// 的长度
            string[] arrSection = CodeBase.Split(new char[] { '/' });
            string FolderPath = string.Empty;
            for (int i = 0; i < arrSection.Length - 1; i++)
            {
                FolderPath += arrSection[i] + "\\";
            }
            return FolderPath;
        }
        /// <summary>
        /// 导入DLL(要导入的DLL要放在程序的根目录中)
        /// </summary>
        /// <param name="DLLName">要导入的DLL名称</param>
        public static void ImportDLL(string DllPath)
        {
            try
            {
                //string DllPath = GetAssemblyPath() + DLLName;
                Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
                foreach (Assembly dll in assemblyArray)
                {
                    if (dll.Location == DllPath)
                    {
                        return;
                    }
                }
                Assembly.LoadFrom(DllPath);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 导入DLL
        /// </summary>
        /// <param name="DllPath">要导入的DLL文件路径</param>
        public static void ImportDLL2(string DllPath)
        {
            try
            {
                Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
                foreach (Assembly dll in assemblyArray)
                {
                    if (!dll.FullName.Contains("Microsoft.GeneratedCode"))
                    {
                        if (dll.Location == DllPath)
                        {
                            return;
                        }
                    }
                }
                Assembly.LoadFile(DllPath);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 生成无参数的构造函数的实例
        /// </summary>
        /// <param name="NameSpaceStr">命名空间</param>
        /// <param name="ClassStr">包含命名控件的类名</param>
        /// <returns>生成的对应类的实例</returns>
        public static object CreateInstance(string NameSpaceStr, string ClassStr)
        {
            try
            {
                Type DllType = Assembly.Load(NameSpaceStr).GetType(ClassStr); // 生成类型
                object Instance = Activator.CreateInstance(DllType); // 生成实例
                return Instance;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 动态编译和执行语句
        /// </summary>
        /// <param name="Expression">要编译执行的语句</param>
        /// <returns>执行的结果</returns>
        public static object GetValue(string Expression)
        {
            object Rtn = null;
            // 1.CSharpCodePrivoder
            CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
            // 2.ICodeComplier
            ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();
            // 3.CompilerParameters
            CompilerParameters objCompilerParameters = new CompilerParameters();
            objCompilerParameters.ReferencedAssemblies.Add("System.dll");
            objCompilerParameters.GenerateExecutable = false;
            objCompilerParameters.GenerateInMemory = true;
            // 4.CompilerResults
            CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode(Expression));
            if (cr.Errors.HasErrors)
            {
                throw new Exception("编译错误:" + cr.Errors[0].ErrorText);
            }
            else
            {
                // 通过反射,调用HelloWorld的实例
                Assembly objAssembly = cr.CompiledAssembly;
                object Obj = objAssembly.CreateInstance("CommonLibs.Eval");
                MethodInfo objMI = Obj.GetType().GetMethod("GetValue");
                Rtn = objMI.Invoke(Obj, null);
            }
            return Rtn;
        }
        /// <summary>
        /// 生成编译语句
        /// </summary>
        /// <param name="Expression">需要编译的语句</param>
        /// <returns>编译执行后的结果</returns>
        private static string GenerateCode(string Expression)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("using System;");
            sb.Append(Environment.NewLine);
            sb.Append("namespace CommonLibs");
            sb.Append(Environment.NewLine);
            sb.Append("{");
            sb.Append(Environment.NewLine);
            sb.Append("    public class Eval");
            sb.Append(Environment.NewLine);
            sb.Append("    {");
            sb.Append(Environment.NewLine);
            sb.Append("        public object GetValue()");
            sb.Append(Environment.NewLine);
            sb.Append("        {");
            sb.Append(Environment.NewLine);
            sb.Append("             return " + Expression + " ;");
            sb.Append(Environment.NewLine);
            sb.Append("        }");
            sb.Append(Environment.NewLine);
            sb.Append("    }");
            sb.Append(Environment.NewLine);
            sb.Append("}");
            string code = sb.ToString();
            return code;
        }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T DeepCopy<T>(T obj)
        {
            //如果是字符串或值类型则直接返回
            if (obj is string || obj.GetType().IsValueType) return obj;
            object retval = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            foreach (FieldInfo field in fields)
            {
                try { field.SetValue(retval, DeepCopy(field.GetValue(obj))); }
                catch { }
            }
            return (T)retval;
        }
    }
}
HMI.Core/Util/PathHelper.cs
New file
@@ -0,0 +1,121 @@
using System;
using System.IO;
namespace HMI.Core.Util
{
    public class PathHelper
    {
        public static string AppFolder { get; private set; } = RootPath();
        public static string IconFolder { get; private set; }
        public static string ConfigFolder { get; private set; }
        public static string DatabaseFolder { get; private set; }
        public static string DatabasePath { get; private set; }
        /// <summary>
        /// 应用程序的根目录
        /// </summary>
        /// <returns></returns>
        private static string RootPath()
        {
            string DllPath = DllHelper.GetAssemblyPath();
            string path = Path.GetFullPath(".");
            IconFolder = InitFolder(path + "\\Icon");
            ConfigFolder = InitFolder(path + "\\Config");
            DatabaseFolder = InitFolder(ConfigFolder + "\\Database");
            DatabasePath = DatabaseFolder + "\\database.db";
            return path;
        }
        public static string SettingFolder()
        {
            string Path = RootPath() + "\\Config\\Setting";
            return Path;
        }
        /// <summary>
        /// 系统配置信息存储文件夹路径
        /// </summary>
        /// <returns>系统配置信息文件夹</returns>
        public static string SystemSettingFolder()
        {
            string Path = RootPath() + "\\Config\\SystemSetting";
            return Path;
        }
        /// <summary>
        /// 系统配置文件
        /// </summary>
        /// <returns>系统配置文件路径</returns>
        public static string SystemSettingFile()
        {
            string Path = RootPath() + "\\Config\\SystemSetting\\system.setting";
            return Path;
        }
        // <summary>
        /// 菜单配置文件
        /// </summary>
        /// <returns>菜单配置文件</returns>
        public static string MenuFile()
        {
            string Path = RootPath() + "\\Config\\SystemSetting\\menu.setting";
            return Path;
        }
        /// <summary>
        /// 生产配置文件夹
        /// </summary>
        /// <returns>生产配置文件夹</returns>
        public static string ProductionFolder()
        {
            string Path = RootPath() + "\\Config\\ProductionSetting";
            return Path;
        }
        /// <summary>
        /// 测试项信息存储文件夹
        /// </summary>
        /// <returns>测试项存储文件夹</returns>
        public static string TestItemFolder()
        {
            string Path = ProductionFolder() + "\\TestItem";
            return Path;
        }
        /// <summary>
        /// 设置参数文件夹
        /// </summary>
        /// <returns>设置参数文件夹</returns>
        public static string ParameterFolder()
        {
            string Path = ProductionFolder() + "\\Parameter";
            return Path;
        }
        /// <summary>
        /// 插件存储目录
        /// </summary>
        /// <returns>插件存储目录</returns>
        public static string PluginFolder()
        {
            string Path = RootPath() + "\\Plugin";
            return Path;
        }
        private static string InitFolder(string path)
        {
            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            catch (Exception ex)
            {
            }
            return path;
        }
    }
}
HMI.Core/Util/SqliteHelper.cs
New file
@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Odbc;
using System.Data.SQLite;
namespace HMI.Core.Util
{
    public class SqliteHelper
    {
        private string DataBaseFile = string.Empty;
        private string ConnectString = string.Empty;
        private SQLiteConnection conn = null;
        private SQLiteCommand cmd = null;
        public SqliteHelper(string DBFile)
        {
            DataBaseFile = DBFile;
            ConnectString = "DataSource=" + DataBaseFile + ";Version=3;Pooling=False;Max Pool Size=100";
            conn = new SQLiteConnection(ConnectString);
            cmd = new SQLiteCommand(conn);
        }
        public static bool CreateDatabase(string DataBaseFile)
        {
            try
            {
                SQLiteConnection.CreateFile(DataBaseFile);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Create Database [${DataBaseFile}] Failed.", ex);
            }
        }
        public int ExecuteSql(string Sql)
        {
            try
            {
                Connect();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = Sql;
                return cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                string message = "ExecuteSql Error:" + ex.Message;
                throw new Exception(message);
            }
            finally
            {
                DisConnect();
            }
        }
        public void ExecuteBatchSql(IEnumerable<string> sqls)
        {
            SQLiteTransaction tr = null;
            try
            {
                Connect();
                tr = conn.BeginTransaction();
                cmd.CommandType = CommandType.Text;
                foreach (string sql in sqls)
                {
                    if (string.IsNullOrEmpty(sql) || string.IsNullOrWhiteSpace(sql)) continue;
                    cmd.CommandText = sql;
                    cmd.ExecuteNonQuery();
                }
                tr.Commit();
            }
            catch (Exception ex)
            {
                tr?.Rollback();
                throw ex;
            }
            finally
            {
                DisConnect();
            }
        }
        public DataTable GetDataTable(string Sql)
        {
            DataTable dataTable = new DataTable();
            SQLiteDataAdapter sQLiteDataAdapter = new SQLiteDataAdapter();
            try
            {
                Connect();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = Sql;
                sQLiteDataAdapter.SelectCommand = cmd;
                sQLiteDataAdapter.Fill(dataTable);
            }
            catch (Exception ex)
            {
                string message = "ExecuteSql Error:" + ex.Message;
                throw new Exception(message);
            }
            finally
            {
                DisConnect();
            }
            return dataTable;
        }
        public string[,] GetDataTableToArray(string Sql)
        {
            try
            {
                DataTable dataTable = GetDataTable(Sql);
                string[,] array = new string[dataTable.Rows.Count, dataTable.Columns.Count];
                int num = 0;
                int num2 = 0;
                foreach (DataRow row in dataTable.Rows)
                {
                    num2 = 0;
                    object[] itemArray = row.ItemArray;
                    foreach (object obj in itemArray)
                    {
                        array[num, num2] = obj.ToString();
                        num2++;
                    }
                    num++;
                }
                return array;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public void Close()
        {
            try
            {
                if (conn.State == ConnectionState.Open)
                {
                    conn.Close();
                }
                if (cmd != null)
                {
                    cmd.Dispose();
                }
                if (conn != null)
                {
                    conn.Dispose();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        public bool HasData(string sql)
        {
            DataTable dataTable = GetDataTable(sql);
            return dataTable.Rows.Count > 0;
        }
        public string[] GetColumnNameFromTable(string TableName)
        {
            try
            {
                string sql = $"PRAGMA table_info('{TableName}')";
                DataTable dataTable = GetDataTable(sql);
                string[] array = new string[dataTable.Rows.Count];
                int num = 0;
                foreach (DataRow row in dataTable.Rows)
                {
                    array[num] = row.ItemArray[1].ToString();
                    num++;
                }
                return array;
            }
            catch (Exception ex)
            {
                throw new Exception("获取字段名称失败:" + ex.Message);
            }
        }
        public string[] GetAllTableInFile()
        {
            try
            {
                string sql = "select name from sqlite_master where type = 'table' order by name";
                DataTable dataTable = GetDataTable(sql);
                string[] array = new string[dataTable.Rows.Count];
                int num = 0;
                foreach (DataRow row in dataTable.Rows)
                {
                    array[num] = row.ItemArray[0].ToString();
                    num++;
                }
                return array;
            }
            catch (Exception ex)
            {
                throw new Exception("获取字段名称失败:" + ex.Message);
            }
        }
        private void Connect()
        {
            if (conn.State == ConnectionState.Closed)
            {
                conn.ConnectionString = ConnectString;
                cmd.Connection = conn;
                conn.Open();
            }
        }
        private void DisConnect()
        {
            if (conn.State == ConnectionState.Open)
            {
                conn.Close();
            }
        }
    }
}
HMI.Core/Util/UtilContainer.cs
New file
@@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace HMI.Core.Util
{
    public class UtilContainer
    {
        private static Dictionary<string, object> Map = new Dictionary<string, object>();
        public static bool RegisterUtil(string name, object value)
        {
            if (Map.ContainsKey(name)) return false;
            Map[name] = value;
            return true;
        }
        public T GetUtil<T>(string name)
        {
            if (!Map.ContainsKey(name)) return default(T);
            return (T)Map[name];
        }
    }
}
HMI.Core/Util/XmlHelper.cs
New file
@@ -0,0 +1,74 @@
using System;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using static System.Net.WebRequestMethods;
namespace HMI.Core.Util
{
    public static class XmlHelper
    {
        /// <summary>
        /// serialize object to xml file.
        /// </summary>
        /// <param name="path">the path to save the xml file</param>
        /// <param name="obj">the object you want to serialize</param>
        public static void SerializeToXml(string path, object obj)
        {
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            string content = string.Empty;
            //serialize
            using (StringWriter writer = new StringWriter())
            {
                serializer.Serialize(writer, obj);
                content = writer.ToString();
            }
            //save to file
            using (StreamWriter stream_writer = new StreamWriter(path))
            {
                stream_writer.Write(content);
            }
        }
        /// <summary>
        /// xml 文件反序列化为对象
        /// </summary>
        /// <typeparam name="T">序列化类型</typeparam>
        /// <param name="path">the path of the xml file</param>
        /// <returns>对象</returns>
        public static T DeserializeFromXml<T>(string path)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (StreamReader reader = new StreamReader(path))
            {
                return (T)serializer.Deserialize(reader);
            }
        }
        public static bool ReadFromXml<T>(T obj, string path) {
            XmlDocument xd = new XmlDocument();
            xd.Load(path);
            Type type = typeof(T);
            XmlNode root = xd.SelectSingleNode(type.Name);
            if (root == null) {
                return false;
            }
            //获取 RefHero 中所有的实例私有字段
            FieldInfo[] fieldArray = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            foreach (FieldInfo filed in fieldArray)
            {
                string fileName = filed.Name;
                XmlNode node = root.SelectSingleNode(fileName);
                if (node == null)
                {
                    continue;
                }
                filed.SetValue(obj, node.InnerText);
            }
            return true;
        }
    }
}
HMI.Core/app.config
New file
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup></configuration>
HMI.Core/packages.config
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
</packages>
HMI.DataViewer/DataViewer.xaml
New file
@@ -0,0 +1,127 @@
<hb:PluginControl x:Class="HMI.DataViewer.DataViewer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.DataViewer"
             mc:Ignorable="d"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="7*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单列表"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Binding="{Binding Code}" Header="菜单编号" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding Level}" Header="等级" />
                        <DataGridTextColumn Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding ParentCode}" Header="父菜单编号" />
                        <DataGridTextColumn Binding="{Binding ParentName}" Header="父菜单名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding DllPath}" Header="Dll路径" />
                        <DataGridTextColumn Width="*" Binding="{Binding ClassName}" Header="类名" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource LinkButton}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsMouseOver,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsSelected,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                   VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单结构"/>
                </DockPanel>
                <TreeView x:Name="treeViewer" Style="{StaticResource DefaultTreeView}" ItemsSource="{Binding TreeDataSource}">
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                <Image Width="{StaticResource ContentFontSize}" Height="{StaticResource ContentFontSize}" Margin="5"
                                       Source="{Binding IconPath,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <TextBlock VerticalAlignment="Center" FontSize="{StaticResource ContentFontSize}" Text="{Binding Name}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.DataViewer/DataViewer.xaml.cs
New file
@@ -0,0 +1,22 @@
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
namespace HMI.DataViewer
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class DataViewer : PluginControl
    {
        public DataViewer()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
        }
    }
}
HMI.DataViewer/HMI.DataViewer.csproj
New file
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{A580DE4A-34E7-4EFE-AD2C-0871396000E9}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.DataViewer</RootNamespace>
    <AssemblyName>HMI.DataViewer</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="DataViewer.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="DataViewer.xaml.cs">
      <DependentUpon>DataViewer.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.DataViewer/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.DataViewer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.DataViewer")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.DataViewer/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.DataViewer.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.DataViewer.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.DataViewer/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.DataViewer/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.DataViewer.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.DataViewer/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.DataViewer/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.IOMonitor/Entity/DioData.cs
New file
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.IOMonitor.Entity
{
    public class DioData
    {
    }
}
HMI.IOMonitor/HMI.IOMonitor.csproj
New file
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{F7663C79-7047-42F3-B129-73A2575EE7C6}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.IOMonitor</RootNamespace>
    <AssemblyName>HMI.IOMonitor</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="IOMonitor.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="Entity\DioData.cs" />
    <Compile Include="IOMonitor.xaml.cs">
      <DependentUpon>IOMonitor.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetPath)" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetFileName)"
copy /Y "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetName).pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
HMI.IOMonitor/IOMonitor.xaml
New file
@@ -0,0 +1,126 @@
<hb:PluginControl x:Class="HMI.IOMonitor.IOMonitor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.IOMonitor"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             mc:Ignorable="d"
             Padding="10"
             Loaded="UserControl_Loaded"
             Background="{StaticResource WorkspaceBackground}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition Width="5*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单列表"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="diTable" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号" />
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DI值" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource RowLink}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                   VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单结构"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="doTable" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="编号"/>
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="DO值" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" ui:StackPanelHelper.ChildSpace="10">
                                        <Button x:Name="editBtn" Style="{StaticResource RowLink}"
            Click="Button_Click"
            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
            DataContext="{Binding }"
            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.IOMonitor/IOMonitor.xaml.cs
New file
@@ -0,0 +1,29 @@
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using log4net;
namespace HMI.IOMonitor
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class IOMonitor : PluginControl
    {
        public IOMonitor()
        {
            InitializeComponent();
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
        }
    }
}
HMI.IOMonitor/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.IOMonitor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.IOMonitor")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.IOMonitor/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.IOMonitor.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.IOMonitor.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.IOMonitor/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.IOMonitor/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.IOMonitor.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.IOMonitor/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.IOMonitor/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.MenuManage/Entity/MenuData.cs
New file
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HMI.Core.Entity;
namespace HMI.MenuManage.Entity
{
    public class MenuData : NotifyBase
    {
        public string Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public string IconPath { get; set; }
        public string Level { get; set; }
        public string Type { get; set; }
        public string ParentCode { get; set; }
        public string ParentName { get; set; }
        public string DllPath { get; set; }
        public string ClassName { get; set; }
        private ObservableCollection<MenuData> _Children = new ObservableCollection<MenuData>();
        public ObservableCollection<MenuData> Children
        {
            get => _Children;
            set
            {
                Set(ref _Children, value);
            }
        }
        private bool _Selected;
        public bool Selected
        {
            get => _Selected;
            set
            {
                Set(ref _Selected, value);
            }
        }
    }
}
HMI.MenuManage/Entity/RoleData.cs
New file
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HMI.Core.Entity;
namespace HMI.MenuManage.Entity
{
    public class RoleData : NotifyBase
    {
        public string Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        private bool _Selected;
        public bool Selected
        {
            get => _Selected;
            set
            {
                if (_Selected != value)
                {
                    Set(ref _Selected, value);
                }
            }
        }
    }
}
HMI.MenuManage/Entity/RoleMenuRel.cs
New file
@@ -0,0 +1,64 @@
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using HMI.Core.Entity;
namespace HMI.MenuManage.Entity
{
    public class RoleMenuRel : NotifyBase
    {
        private event EventHandler<bool> SelectedChanged;
        public string Id { get; set; }
        public string RoleCode { get; set; }
        public string RoleName { get; set; }
        public string MenuCode { get; set; }
        public string ParentCode { get; set; }
        public string MenuName { get; set; }
        public string MenuIcon { get; set; }
        private bool _Selected;
        public bool Selected
        {
            get => _Selected;
            set
            {
                if (_Selected != value)
                {
                    Set(ref _Selected, value);
                }
            }
        }
        public ICommand CheckCommand
        {
            get => new RelayCommand<bool>(ExcuteCheckCommand);
        }
        private void ExcuteCheckCommand(bool flag)
        {
            if (flag && Parent != null) Parent.Selected = flag;
            foreach (var item in Children)
            {
                item.Selected = flag;
            }
        }
        public RoleMenuRel Parent;
        private ObservableCollection<RoleMenuRel> _Children = new ObservableCollection<RoleMenuRel>();
        public ObservableCollection<RoleMenuRel> Children
        {
            get { return _Children; }
            set { Set(ref _Children, value); }
        }
        public void AddChild(RoleMenuRel child)
        {
            child.Parent = this;
            Children.Add(child);
        }
    }
}
HMI.MenuManage/Entity/UserData.cs
New file
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HMI.Core.Entity;
namespace HMI.MenuManage.Entity
{
    public class UserData : NotifyBase
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string RoleCode { get; set; }
        private bool _Selected;
        public bool Selected
        {
            get => _Selected;
            set
            {
                if (_Selected != value)
                {
                    Set(ref _Selected, value);
                }
            }
        }
    }
}
HMI.MenuManage/Entity/UserRoleRel.cs
New file
@@ -0,0 +1,26 @@
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using HMI.Core.Entity;
namespace HMI.MenuManage.Entity
{
    public class UserRoleRel : NotifyBase
    {
        public string Id { get; set; }
        public string Username { get; set; }
        public string RealName { get; set; }
        public string RoleCode { get; set; }
        public string RoleName { get; set; }
        private bool _Selected;
        public bool Selected
        {
            get => _Selected;
            set
            {
                Set(ref _Selected, value);
            }
        }
    }
}
HMI.MenuManage/HMI.MenuManage.csproj
New file
@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{DE4205C6-5923-46FD-A4B9-65169BF61C75}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.MenuManage</RootNamespace>
    <AssemblyName>HMI.MenuManage</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="DatabaseHelper">
      <HintPath>..\Libs\DatabaseHelper.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Data.SQLite">
      <HintPath>..\Libs\System.Data.SQLite.dll</HintPath>
    </Reference>
    <Reference Include="System.Data.SQLite.EF6">
      <HintPath>..\Libs\System.Data.SQLite.EF6.dll</HintPath>
    </Reference>
    <Reference Include="System.Data.SQLite.Linq">
      <HintPath>..\Libs\System.Data.SQLite.Linq.dll</HintPath>
    </Reference>
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Entity\MenuData.cs" />
    <Compile Include="Entity\RoleData.cs" />
    <Compile Include="Entity\UserRoleRel.cs" />
    <Compile Include="Entity\RoleMenuRel.cs" />
    <Compile Include="Entity\UserData.cs" />
    <Compile Include="Modal\RoleModal.xaml.cs">
      <DependentUpon>RoleModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Model\RoleManageModel.cs" />
    <Compile Include="RoleManage.xaml.cs">
      <DependentUpon>RoleManage.xaml</DependentUpon>
    </Compile>
    <Compile Include="Modal\UserModal.xaml.cs">
      <DependentUpon>UserModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Model\UserManageModel.cs" />
    <Compile Include="UserManage.xaml.cs">
      <DependentUpon>UserManage.xaml</DependentUpon>
    </Compile>
    <Compile Include="Util\UserRoleRelHelper.cs" />
    <Compile Include="Util\RoleHelper.cs" />
    <Compile Include="Util\RoleMenuHelper.cs" />
    <Page Include="Modal\RoleModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="RoleManage.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="MenuManage.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="MenuManage.xaml.cs">
      <DependentUpon>MenuManage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="Modal\UserModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="Modal\MenuModal.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UserManage.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Modal\MenuModal.xaml.cs">
      <DependentUpon>MenuModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="Model\MenuManageModel.cs" />
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.MenuManage/MenuManage.xaml
New file
@@ -0,0 +1,131 @@
<hb:PluginControl x:Class="HMI.MenuManage.MenuManage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.MenuManage"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             mc:Ignorable="d"
             Padding="10"
             Loaded="DataGrid_Loaded"
             Background="{StaticResource WorkspaceBackground}"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="7*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe934;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:IconHelper.FontFamily="{StaticResource iconfont}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:IconHelper.FontFamily="{StaticResource iconfont}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单列表"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="table" Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Binding="{Binding Code}" Header="菜单编号" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding Level}" Header="等级" />
                        <DataGridTextColumn Binding="{Binding Type}" Header="类型" />
                        <DataGridTextColumn Binding="{Binding Name}" Header="菜单名称" />
                        <DataGridTextColumn Binding="{Binding ParentCode}" Header="父菜单编号" />
                        <DataGridTextColumn Binding="{Binding ParentName}" Header="父菜单名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding DllPath}" Header="Dll路径" />
                        <DataGridTextColumn Width="*" Binding="{Binding ClassName}" Header="类名" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource LinkButton}"
                            Margin="0,0,10,0"
                            Click="Button_Click"
                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                            DataContext="{Binding }"
                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsMouseOver,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsSelected,
                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                   VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单结构"/>
                </DockPanel>
                <TreeView x:Name="treeViewer" Style="{StaticResource DefaultTreeView}" ItemsSource="{Binding TreeDataSource}"
                          SelectedItemChanged="treeViewer_SelectedItemChanged">
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                <Image Width="{StaticResource ContentFontSize}" Height="{StaticResource ContentFontSize}" Margin="5"
                                       Source="{Binding IconPath,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <TextBlock VerticalAlignment="Center" FontSize="{StaticResource ContentFontSize}" Text="{Binding Name}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.MenuManage/MenuManage.xaml.cs
New file
@@ -0,0 +1,149 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Model;
using HMI.UI.Util;
using log4net;
namespace HMI.MenuManage
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class MenuManage : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        #region 属性
        private MenuManageModel Model;
        private ILog log;
        #endregion
        public MenuManage()
        {
            Model = new MenuManageModel();
            this.DataContext = Model;
            InitializeComponent();
        }
        #region 属性
        #endregion
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            var btn = ((Button)sender);
            if (btn.Name == "addBtn")
            {
                try
                {
                    MenuModal window = new MenuModal
                    {
                        Title = "新增菜单",
                        sqliteHelper = Model.sqliteHelper,
                        Owner = Window.GetWindow(this)
                    };
                    if (true == window.Open(new MenuData()))
                    {
                        MenuData data = window.Data;
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Add Menu Failed.", ex);
                }
            }
            else if (btn.Name == "refreshBtn")
            {
                try
                {
                    Model.LoadData();
                }
                catch (Exception ex)
                {
                    log.Error("Refresh Menu List Failed.", ex);
                }
            }
            else if (btn.Name == "deleteBtn") { }
            else if (btn.Name == "editBtn")
            {
                try
                {
                    var data = (MenuData)btn.DataContext;
                    MenuModal window = new MenuModal
                    {
                        Title = "编辑菜单",
                        sqliteHelper = Model.sqliteHelper,
                        Owner = Window.GetWindow(this)
                    };
                    if (true == window.Open(data))
                    {
                        var newData = window.Data;
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Edit Menu Failed.", ex);
                }
            }
            else if (btn.Name == "delBtn")
            {
                try
                {
                    var data = (MenuData)((Button)sender).DataContext;
                    var res = MsgBoxHelper.WarnMessage(Window.GetWindow(this), $"确定删除[{data.Name}]吗?该操作不可逆!");
                    if (res == MessageBoxResult.OK)
                    {
                        Model.DeleteMenuById(data.Id);
                        Model.LoadData();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Delete Menu Failed.", ex);
                }
            }
        }
        private void DataGrid_Loaded(object sender, RoutedEventArgs e)
        {
            Model.LoadData();
            Loaded -= DataGrid_Loaded;
        }
        private void treeViewer_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            table.SelectedItem = e.NewValue;
        }
        public void SetLogger(ILog logger)
        {
            this.log = logger;
        }
    }
}
HMI.MenuManage/Modal/MenuModal.xaml
New file
@@ -0,0 +1,98 @@
<pu:WindowX x:Class="HMI.MenuManage.MenuModal"
            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:HMI.MenuManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="10">
        <StackPanel>
            <pu:FormGroup x:Name="FgCode" Style="{StaticResource RequiredInputFormGroupStyle}"
                  Header="菜单编号" GroupName="MyFormGroup">
                <TextBox x:Name="TbCode" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgName" Style="{StaticResource RequiredInputFormGroupStyle}"
                              Header="菜单名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgIcon" Style="{StaticResource RequiredInputFormGroupStyle}" Header="图标" GroupName="MyFormGroup">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="30" />
                    </Grid.ColumnDefinitions>
                    <TextBox x:Name="TbIcon" IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" />
                    <Button Grid.Column="1" BorderThickness="0,1,1,1" Style="{StaticResource SearchButton}"
                            Click="IconSelect_Click"/>
                </Grid>
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgLevel" Style="{StaticResource RequiredInputFormGroupStyle}" Header="级别" GroupName="MyFormGroup">
                <ComboBox x:Name="TbLevel" Style="{StaticResource FormComboxStyle}">
                    <ComboBoxItem>1</ComboBoxItem>
                    <ComboBoxItem>2</ComboBoxItem>
                </ComboBox>
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgType" Style="{StaticResource RequiredInputFormGroupStyle}" Header="类型" GroupName="MyFormGroup">
                <ComboBox x:Name="TbType" Style="{StaticResource FormComboxStyle}">
                    <ComboBoxItem>WPF</ComboBoxItem>
                    <ComboBoxItem>LV</ComboBoxItem>
                </ComboBox>
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgParentCode" Style="{StaticResource RequiredInputFormGroupStyle}" Header="上级菜单" GroupName="MyFormGroup">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="30" />
                    </Grid.ColumnDefinitions>
                    <TextBox x:Name="TbParentCode" Style="{StaticResource EditorTextBoxStyle}" />
                    <Button Grid.Column="1" Style="{StaticResource SearchButton}"/>
                </Grid>
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgDllPath" Style="{StaticResource RequiredInputFormGroupStyle}" Header="Dll文件" GroupName="MyFormGroup">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="30" />
                    </Grid.ColumnDefinitions>
                    <TextBox x:Name="TbDllPath" IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" />
                    <Button Grid.Column="1" Click="DllSelect_Click" Style="{StaticResource SearchButton}"/>
                </Grid>
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgClassName" Style="{StaticResource RequiredInputFormGroupStyle}" Header="类名" GroupName="MyFormGroup">
                <TextBox x:Name="TbClassName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.MenuManage/Modal/MenuModal.xaml.cs
New file
@@ -0,0 +1,298 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;
using Panuon.WPF.UI;
using HMI.Core.Annotation;
using HMI.Core.Entity;
using HMI.Core.Exceptions;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
namespace HMI.MenuManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MenuModal : WindowX
    {
        public MenuModal()
        {
            InitializeComponent();
        }
        public MenuData Data { get; set; }
        public SqliteHelper sqliteHelper { get; set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    Panuon.WPF.UI.Toast.Show(this, "Error!", ToastPosition.Top);
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateForm()
        {
            try
            {
                FormValidateException ex = new FormValidateException();
                string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
                string sql = $"SELECT 1 FROM menu WHERE code='{TbCode.Text}'{segment};";
                if (string.IsNullOrEmpty(TbCode.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入菜单编号", FgCode));
                }
                else if (sqliteHelper.HasData(sql))
                {
                    ex.Result.Add(new FormGroupValidateResult("菜单编号不能重复", FgCode));
                }
                else
                {
                    FgCode.ValidateResult = ValidateResult.None;
                    FgCode.Message = null;
                }
                if (string.IsNullOrEmpty(TbName.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入菜单名称", FgName));
                }
                else
                {
                    FgName.ValidateResult = ValidateResult.None;
                    FgName.Message = null;
                }
                if(string.IsNullOrEmpty(TbIcon.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请选择菜单图标", FgIcon));
                }
                else
                {
                    FgIcon.ValidateResult = ValidateResult.None;
                    FgIcon.Message = null;
                }
                if (string.IsNullOrEmpty(TbLevel.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请选择菜单级别", FgLevel));
                }
                else
                {
                    FgLevel.ValidateResult = ValidateResult.None;
                    FgLevel.Message = null;
                }
                if (TbLevel.Text == "2")
                {
                    if (string.IsNullOrEmpty(TbType.Text))
                    {
                        ex.Result.Add(new FormGroupValidateResult("请选择菜单类型", FgType));
                    }
                    else
                    {
                        FgType.ValidateResult = ValidateResult.None;
                        FgType.Message = null;
                    }
                    if (string.IsNullOrEmpty(TbParentCode.Text))
                    {
                        ex.Result.Add(new FormGroupValidateResult("请选择上级菜单", FgParentCode));
                    }
                    else
                    {
                        FgParentCode.ValidateResult = ValidateResult.None;
                        FgParentCode.Message = null;
                    }
                    if (string.IsNullOrEmpty(TbDllPath.Text))
                    {
                        ex.Result.Add(new FormGroupValidateResult("请选择Dll路径", FgDllPath));
                    }
                    else
                    {
                        FgDllPath.ValidateResult = ValidateResult.None;
                        FgDllPath.Message = null;
                    }
                    if (string.IsNullOrEmpty(TbClassName.Text))
                    {
                        ex.Result.Add(new FormGroupValidateResult("请输入类名", FgClassName));
                    }
                    else
                    {
                        FgClassName.ValidateResult = ValidateResult.None;
                        FgClassName.Message = null;
                    }
                }
                else {
                }
                if (!ex.Success) {
                    throw ex;
                }
            }
            catch (FormValidateException ex)
            {
                foreach (var data in ex.Result)
                {
                    var fg = (FormGroup)data.Sender;
                    fg.ValidateResult = ValidateResult.Error;
                    fg.Message = data.Message;
                }
                return false;
            }
            Data.Code = TbCode.Text;
            Data.Name = TbName.Text;
            Data.IconPath = TbIcon.Text;
            Data.Level = TbLevel.Text;
            Data.Type = TbType.Text;
            Data.ParentCode = TbParentCode.Text;
            Data.DllPath = TbDllPath.Text;
            Data.ClassName = TbClassName.Text;
            return true;
        }
        public bool? Open(MenuData data)
        {
            Data = data;
            TbCode.Text = Data.Code;
            TbName.Text = Data.Name;
            TbIcon.Text = Data.IconPath;
            TbLevel.SelectedValue = Data.Level;
            TbType.SelectedValue = Data.Type;
            TbParentCode.Text = Data.ParentCode;
            TbDllPath.Text = Data.DllPath;
            TbClassName.Text = Data.ClassName;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (string.IsNullOrEmpty(Data.Id))
            {
                InsertMenu(Data);
            }
            else
            {
                UpdateMenu(Data);
            }
        }
        public void InsertMenu(MenuData data)
        {
            string sql = $@"INSERT INTO menu (code,name,iconPath,level,type,parentCode,dllPath,className)
                VALUES
                ('{data.Code}','{data.Name}','{data.IconPath}','{data.Level}','{data.Type}','{data.ParentCode}',
                '{data.DllPath}','{data.ClassName}');";
            sqliteHelper.ExecuteSql(sql);
        }
        public void UpdateMenu(MenuData data)
        {
            string sql = $@"UPDATE menu SET code='{data.Code}',name='{data.Name}',
iconPath='{data.IconPath}',level='{data.Level}',type='{data.Type}',parentCode='{data.ParentCode}',
dllPath='{data.DllPath}',className='{data.ClassName}' WHERE id='{data.Id}';";
            sqliteHelper.ExecuteSql(sql);
        }
        private void IconSelect_Click(object sender, RoutedEventArgs e)
        {
            string BasePath = PathHelper.IconFolder;
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "(*.*)|*.*";
            openFileDialog.InitialDirectory = BasePath;
            openFileDialog.Title = "选择图片";
            openFileDialog.Multiselect = false;
            if (openFileDialog.ShowDialog() == true)
            {
                string FileName = openFileDialog.FileName;
                if (!FileName.StartsWith(BasePath))
                {
                    Panuon.WPF.UI.Toast.Show(this, "请将图标移动至程序Icon目录下!", ToastPosition.Top);
                }
                else
                {
                    TbIcon.Text = FileName.Replace(BasePath, ".");
                }
            }
        }
        private void DllSelect_Click(object sender, RoutedEventArgs e)
        {
            string pluginPath = PathHelper.PluginFolder();
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "(*.dll)|*.dll";
            openFileDialog.InitialDirectory = pluginPath;
            openFileDialog.Title = "选择文件";
            openFileDialog.Multiselect = false;
            if (openFileDialog.ShowDialog() == true)
            {
                string FileName = openFileDialog.FileName;
                if (!FileName.StartsWith(pluginPath))
                {
                    Panuon.WPF.UI.Toast.Show(this, "请将插件移动至程序Plugin目录下!", ToastPosition.Top);
                }
                else
                {
                    TbDllPath.Text = FileName.Replace(pluginPath, ".");
                    try
                    {
                        object[] infos = Assembly.LoadFrom(FileName).GetCustomAttributes(typeof(PluginClassNameAttribute), false);
                        if (infos != null && infos.Length > 0)
                        {
                            PluginClassNameAttribute attr = (PluginClassNameAttribute)infos[0];
                            TbClassName.Text = attr.ClassName;
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
        }
    }
}
HMI.MenuManage/Modal/RoleModal.xaml
New file
@@ -0,0 +1,69 @@
<pu:WindowX x:Class="HMI.MenuManage.RoleModal"
            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:HMI.MenuManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TextBox,ComboBox,Button" />
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/FormStyle.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="10">
        <StackPanel>
            <pu:FormGroup x:Name="FgCode" Style="{StaticResource RequiredInputFormGroupStyle}"
              Header="角色编号" GroupName="MyFormGroup">
                <TextBox x:Name="TbCode" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgName" Style="{StaticResource RequiredInputFormGroupStyle}"
                              Header="角色名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgMenu" Orientation="Vertical" Style="{StaticResource RequiredInputFormGroupStyle}"
                              Header="权限" GroupName="MyFormGroup">
                <TreeView x:Name="menuTree" Grid.Column="1" Style="{StaticResource DefaultTreeView}"
                          ItemsSource="{Binding TreeDataSource}">
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected}"
Command="{Binding CheckCommand}"
CommandParameter="{Binding Path=IsChecked,RelativeSource={RelativeSource Mode=Self}}"/>
                                <Image Width="{StaticResource ContentFontSize}"
                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <TextBlock VerticalAlignment="Center" Text="{Binding MenuName}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.MenuManage/Modal/RoleModal.xaml.cs
New file
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;
using Panuon.WPF.UI;
using HMI.Core.Annotation;
using HMI.Core.Entity;
using HMI.Core.Exceptions;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Util;
namespace HMI.MenuManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class RoleModal : WindowX
    {
        public RoleModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        public RoleData Data { get; set; }
        private ObservableCollection<RoleMenuRel> _TreeDataSource;
        public ObservableCollection<RoleMenuRel> TreeDataSource
        {
            get { return _TreeDataSource; }
            set { Set(ref _TreeDataSource, value); }
        }
        public SqliteHelper sqliteHelper { get; set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    Panuon.WPF.UI.Toast.Show(this, "Error!", ToastPosition.Top);
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateForm()
        {
            try
            {
                FormValidateException ex = new FormValidateException();
                string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
                string sql = $"SELECT 1 FROM user WHERE username='{TbCode.Text}'{segment};";
                if (string.IsNullOrEmpty(TbCode.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入角色编号", FgCode));
                }
                else if (sqliteHelper.HasData(sql))
                {
                    ex.Result.Add(new FormGroupValidateResult("角色编号不能重复", FgCode));
                }
                else
                {
                    FgCode.ValidateResult = ValidateResult.None;
                    FgCode.Message = null;
                }
                if (string.IsNullOrEmpty(TbName.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入角色名称", FgName));
                }
                else
                {
                    FgName.ValidateResult = ValidateResult.None;
                    FgName.Message = null;
                }
                if (!ex.Success)
                {
                    throw ex;
                }
            }
            catch (FormValidateException ex)
            {
                foreach (var data in ex.Result)
                {
                    var fg = (FormGroup)data.Sender;
                    fg.ValidateResult = ValidateResult.Error;
                    fg.Message = data.Message;
                }
                return false;
            }
            Data.Code = TbCode.Text;
            Data.Name = TbName.Text;
            return true;
        }
        public bool? Open(RoleData data)
        {
            Data = data;
            TbCode.Text = Data.Code;
            TbName.Text = Data.Name;
            TreeDataSource = RoleMenuHelper.SelectRoleMenuTreeData(sqliteHelper, Data.Code);
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (string.IsNullOrEmpty(Data.Id))
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(Data);
            }
        }
        public void InsertData(RoleData data)
        {
            string sql = $@"INSERT INTO role (code,name)
                VALUES
                ('{data.Code}','{data.Name}');";
            RoleMenuHelper.SaveRelData(sqliteHelper, TreeDataSource, data.Code, sql, null);
        }
        public void UpdateData(RoleData data)
        {
            string sql = $@"UPDATE role SET code='{data.Code}',name='{data.Name}' WHERE id='{data.Id}';";
            RoleMenuHelper.SaveRelData(sqliteHelper, TreeDataSource, data.Code, sql, null);
        }
    }
}
HMI.MenuManage/Modal/UserModal.xaml
New file
@@ -0,0 +1,81 @@
<pu:WindowX x:Class="HMI.MenuManage.UserModal"
            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:HMI.MenuManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="10">
        <StackPanel>
            <pu:FormGroup x:Name="FgName" Style="{StaticResource RequiredInputFormGroupStyle}"
                              Header="用户名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgUsername" Style="{StaticResource RequiredInputFormGroupStyle}"
                          Header="账号" GroupName="MyFormGroup">
                <TextBox x:Name="TbUsername" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgPassword" Style="{StaticResource RequiredInputFormGroupStyle}"
                  Header="密码" GroupName="MyFormGroup">
                <TextBox x:Name="TbPassword" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgRoleCode" Orientation="Vertical"
                          Style="{StaticResource RequiredInputFormGroupStyle}" Header="角色" GroupName="MyFormGroup">
                <DataGrid x:Name="table" Style="{StaticResource DefaultDataGrid}" SelectionMode="Single"
           ItemsSource="{Binding Roles}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="授予" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                               HorizontalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="#"
             Binding="{Binding Converter={StaticResource rowNumber},
                     RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                             CanUserResize="False"/>
                        <DataGridTextColumn Width="*" Binding="{Binding RoleCode}" Header="角色编号" />
                        <DataGridTextColumn Width="*" Binding="{Binding RoleName}" Header="角色名称" />
                    </DataGrid.Columns>
                </DataGrid>
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.MenuManage/Modal/UserModal.xaml.cs
New file
@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;
using Panuon.WPF.UI;
using HMI.Core.Annotation;
using HMI.Core.Entity;
using HMI.Core.Exceptions;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Util;
namespace HMI.MenuManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class UserModal : WindowX
    {
        public UserModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        public UserData Data { get; set; }
        private ObservableCollection<UserRoleRel> _Roles;
        public ObservableCollection<UserRoleRel> Roles
        {
            get => _Roles;
            set { Set(ref _Roles, value); }
        }
        public SqliteHelper sqliteHelper { get; set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    Panuon.WPF.UI.Toast.Show(this, "Error!", ToastPosition.Top);
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateForm()
        {
            try
            {
                FormValidateException ex = new FormValidateException();
                string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
                string sql = $"SELECT 1 FROM user WHERE username='{TbUsername.Text}'{segment};";
                if (string.IsNullOrEmpty(TbUsername.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入账号", FgUsername));
                }
                else if (sqliteHelper.HasData(sql))
                {
                    ex.Result.Add(new FormGroupValidateResult("账号不能重复", FgUsername));
                }
                else
                {
                    FgUsername.ValidateResult = ValidateResult.None;
                    FgUsername.Message = null;
                }
                if (string.IsNullOrEmpty(TbName.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入用户名称", FgName));
                }
                else
                {
                    FgName.ValidateResult = ValidateResult.None;
                    FgName.Message = null;
                }
                if (!ex.Success)
                {
                    throw ex;
                }
            }
            catch (FormValidateException ex)
            {
                foreach (var data in ex.Result)
                {
                    var fg = (FormGroup)data.Sender;
                    fg.ValidateResult = ValidateResult.Error;
                    fg.Message = data.Message;
                }
                return false;
            }
            Data.Username = TbUsername.Text;
            Data.Name = TbName.Text;
            Data.Password = TbPassword.Text;
            return true;
        }
        public bool? Open(UserData data)
        {
            Data = data;
            TbUsername.Text = Data.Username;
            TbName.Text = Data.Name;
            TbPassword.Text = Data.Password;
            Roles = UserRoleRelHelper.SelectUserRoleData(sqliteHelper, Data.Username);
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (string.IsNullOrEmpty(Data.Id))
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(Data);
            }
        }
        public void InsertData(UserData data)
        {
            string sql = $@"INSERT INTO user (username,password,name)
                VALUES
                ('{data.Username}','{data.Password}','{data.Name}');";
            UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, data, sql, null);
        }
        public void UpdateData(UserData data)
        {
            string sql = $@"UPDATE user SET username='{data.Username}',password='{data.Password}',name='{data.Name}'
WHERE id='{data.Id}';";
            UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, data, sql, null);
        }
    }
}
HMI.MenuManage/Model/MenuManageModel.cs
New file
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using HMI.Core.Entity;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
namespace HMI.MenuManage.Model
{
    public class MenuManageModel : NotifyBase
    {
        public SqliteHelper sqliteHelper;
        private ObservableCollection<MenuData> _DataSource;
        public ObservableCollection<MenuData> DataSource
        {
            get => _DataSource;
            set
            {
                Set(ref _DataSource, value);
                Set(nameof(DataSourceCount));
            }
        }
        public int DataSourceCount { get => DataSource?.Count ?? 0; }
        private ObservableCollection<MenuData> _TreeDataSource;
        public ObservableCollection<MenuData> TreeDataSource
        {
            get => _TreeDataSource;
            set { Set(ref _TreeDataSource, value); }
        }
        public MenuManageModel()
        {
            if (!File.Exists(PathHelper.DatabasePath))
            {
                SqliteHelper.CreateDatabase(PathHelper.DatabasePath);
            }
            sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS menu (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    code TEXT NOT NULL,
                    name TEXT NOT NULL,
                    iconPath TEXT,
                    level TEXT,
                    type TEXT,
                    parentCode TEXT,
                    dllPath TEXT,
                    className TEXT,
                    UNIQUE(code)
                    );");
        }
        public void LoadData()
        {
            ObservableCollection<MenuData> data = new ObservableCollection<MenuData>();
            ObservableCollection<MenuData> treeData = new ObservableCollection<MenuData>();
            DataTable dt = sqliteHelper.GetDataTable("SELECT m1.*,m2.name AS parentName FROM menu AS m1 LEFT JOIN menu AS m2 ON m1.parentCode = m2.code Order By m1.id;");
            Dictionary<string, MenuData> cache = new Dictionary<string, MenuData>();
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var menu = new MenuData
                    {
                        Id = dr["id"].ToString(),
                        Code = dr["code"].ToString(),
                        Name = dr["name"].ToString(),
                        IconPath = dr["iconPath"].ToString(),
                        Level = dr["level"].ToString(),
                        Type = dr["type"].ToString(),
                        ParentCode = dr["parentCode"].ToString(),
                        ParentName = dr["parentName"].ToString(),
                        DllPath = dr["dllPath"].ToString(),
                        ClassName = dr["className"].ToString()
                    };
                    data.Add(menu);
                    if (menu.Level == "1")
                    {
                        treeData.Add(menu);
                    }
                    cache.Add(menu.Code, menu);
                }
                foreach (var menu in data)
                {
                    if (!string.IsNullOrEmpty(menu.ParentCode) && menu.ParentCode != menu.Code)
                    {
                        cache[menu.ParentCode].Children.Add(menu);
                    }
                }
            }
            TreeDataSource = treeData;
            DataSource = data;
        }
        public void DeleteMenuById(string id)
        {
            string sql = $"DELETE FROM menu WHERE id = {id}";
            sqliteHelper.ExecuteSql(sql);
        }
    }
}
HMI.MenuManage/Model/RoleManageModel.cs
New file
@@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using HMI.Core.Entity;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Util;
namespace HMI.MenuManage.Model
{
    public class RoleManageModel : NotifyBase
    {
        public SqliteHelper sqliteHelper;
        private ObservableCollection<RoleData> _DataSource;
        public ObservableCollection<RoleData> DataSource
        {
            get { return _DataSource; }
            set { Set(ref _DataSource, value); }
        }
        private RoleData _SelectedValue;
        public RoleData SelectedValue
        {
            get => _SelectedValue;
            set
            {
                Set(ref _SelectedValue, value);
                LoadRelData(value);
            }
        }
        private ObservableCollection<RoleMenuRel> _TreeDataSource;
        public ObservableCollection<RoleMenuRel> TreeDataSource
        {
            get { return _TreeDataSource; }
            set { Set(ref _TreeDataSource, value); }
        }
        public RoleManageModel()
        {
            if (!File.Exists(PathHelper.DatabasePath))
            {
                SqliteHelper.CreateDatabase(PathHelper.DatabasePath);
            }
            sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS role (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    code TEXT NOT NULL,
                    name TEXT,
                    UNIQUE(code)
                    );
CREATE TABLE IF NOT EXISTS role_menu_rel (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    roleCode TEXT NOT NULL,
                    menuCode TEXT NOT NULL,
                    UNIQUE(roleCode,menuCode)
                    );
");
        }
        public void LoadData()
        {
            ObservableCollection<RoleData> data = new ObservableCollection<RoleData>();
            DataTable dt = sqliteHelper.GetDataTable("SELECT * FROM role Order By id;");
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var menu = new RoleData
                    {
                        Id = dr["id"].ToString(),
                        Code = dr["code"].ToString(),
                        Name = dr["name"].ToString()
                    };
                    data.Add(menu);
                }
            }
            DataSource = data;
        }
        public void LoadRelData(RoleData data)
        {
            if (data == null)
            {
                TreeDataSource.Clear();
                return;
            }
            TreeDataSource = RoleMenuHelper.SelectRoleMenuTreeData(sqliteHelper, data.Code);
        }
        public void DelSingleData(RoleData data)
        {
            sqliteHelper.ExecuteBatchSql(new List<string>{
            $"DELETE FROM role WHERE id = '{data.Id}'",
            $"DELETE FROM role_menu_rel WHERE roleCode = '{data.Code}'",
            $"DELETE FROM user_role_rel WHERE roleCode = '{data.Code}'"
            });
        }
        public void SaveRelData(RoleData role)
        {
            if (role == null) return;
            RoleMenuHelper.SaveRelData(sqliteHelper, TreeDataSource, role.Code, null, null);
        }
    }
}
HMI.MenuManage/Model/UserManageModel.cs
New file
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Util;
using HMI.Core.Entity;
namespace HMI.MenuManage.Model
{
    public class UserManageModel : NotifyBase
    {
        public SqliteHelper sqliteHelper;
        private ObservableCollection<UserData> _DataSource;
        public ObservableCollection<UserData> DataSource
        {
            get { return _DataSource; }
            set { Set(ref _DataSource, value); }
        }
        private UserData _SelectedUser;
        public UserData SelectedUser
        {
            get { return _SelectedUser; }
            set
            {
                Set(ref _SelectedUser, value);
                LoadUserRole();
            }
        }
        private ObservableCollection<UserRoleRel> _Roles;
        public ObservableCollection<UserRoleRel> Roles
        {
            get => _Roles;
            set { Set(ref _Roles, value); }
        }
        public UserManageModel()
        {
            if (!File.Exists(PathHelper.DatabasePath))
            {
                SqliteHelper.CreateDatabase(PathHelper.DatabasePath);
            }
            sqliteHelper = new SqliteHelper(PathHelper.DatabasePath);
            sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS user (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    username TEXT NOT NULL,
                    password TEXT NOT NULL,
                    name TEXT,
                    UNIQUE(username)
                    );
CREATE TABLE IF NOT EXISTS user_role_rel (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    username TEXT NOT NULL,
                    roleCode TEXT NOT NULL,
                    UNIQUE(username,roleCode)
                    );
");
        }
        public void LoadData()
        {
            ObservableCollection<UserData> data = new ObservableCollection<UserData>();
            DataTable dt = sqliteHelper.GetDataTable("SELECT * FROM user Order By id;");
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var menu = new UserData
                    {
                        Id = dr["id"].ToString(),
                        Username = dr["username"].ToString(),
                        Password = dr["password"].ToString(),
                        Name = dr["name"].ToString()
                    };
                    data.Add(menu);
                }
            }
            DataSource = data;
        }
        public void LoadUserRole()
        {
            Roles = SelectedUser == null ? new ObservableCollection<UserRoleRel>()
                : UserRoleRelHelper.SelectUserRoleData(sqliteHelper, SelectedUser.Username);
        }
        public void SaveUserRole()
        {
            UserRoleRelHelper.SaveUserRoleRel(sqliteHelper, Roles, SelectedUser, null, null);
        }
    }
}
HMI.MenuManage/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,54 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using HMI.Core.Annotation;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.MenuManage")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.MenuManage")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: PluginClassName("HMI.MenuManage.MenuManage")]
HMI.MenuManage/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.MenuManage.Properties {
    using System;
    /// <summary>
    ///   一个强类型的资源类,用于查找本地化的字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存的 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.MenuManage.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.MenuManage/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.MenuManage/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.MenuManage.Properties {
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
    }
}
HMI.MenuManage/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.MenuManage/RoleManage.xaml
New file
@@ -0,0 +1,127 @@
<hb:PluginControl x:Class="HMI.MenuManage.RoleManage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.MenuManage"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             mc:Ignorable="d"
             Padding="10"
             Background="#f0f2f5"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="6*"/>
            <ColumnDefinition Width="4*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe61a;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="角色列表"/>
                </DockPanel>
                <DataGrid x:Name="table" Style="{StaticResource DefaultDataGrid}"
                      SelectedValue="{Binding SelectedValue}"
    ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                                              HorizontalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="#"
                            Binding="{Binding Converter={StaticResource rowNumber},
                                    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                            CanUserResize="False"/>
                        <DataGridTextColumn Width="*" Binding="{Binding Code}" Header="角色编号" />
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="角色名称" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource LinkButton}"
                                            Margin="0,0,10,0"
                                            Click="Button_Click"
                                            DataContext="{Binding }">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                                            DataContext="{Binding }"
                                            Click="Button_Click">删除</Button>
                                    </StackPanel>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsMouseOver,
                                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsSelected,
                                        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                                                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" x:Name="saveMenuBtn" Click="Button_Click"
                            pu:ButtonHelper.Icon="&#xe60d;" Content="保存"/>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="菜单授权"/>
                </DockPanel>
                <TreeView x:Name="treeViewer" Grid.Column="1"
                          Style="{StaticResource DefaultTreeView}" ItemsSource="{Binding TreeDataSource}">
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected}"
Command="{Binding CheckCommand}"
CommandParameter="{Binding Path=IsChecked,RelativeSource={RelativeSource Mode=Self}}"
/>
                                <Image Width="{StaticResource ContentFontSize}"
                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <TextBlock VerticalAlignment="Center" Text="{Binding MenuName}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.MenuManage/RoleManage.xaml.cs
New file
@@ -0,0 +1,159 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using log4net;
using log4net.Repository.Hierarchy;
using Panuon.WPF.UI;
using HMI.Core.Api;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Model;
using HMI.Base;
namespace HMI.MenuManage
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class RoleManage : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        #region 属性
        private RoleManageModel Model;
        private ILog log;
        #endregion
        public RoleManage()
        {
            Model = new RoleManageModel();
            this.DataContext = Model;
            this.Loaded += RoleManage_Loaded;
            InitializeComponent();
        }
        private void RoleManage_Loaded(object sender, RoutedEventArgs e)
        {
            Model.LoadData();
            this.Loaded -= RoleManage_Loaded;
        }
        #region 属性
        #endregion
        public object Active()
        {
            return null;
        }
        public object DeActive()
        {
            return null;
        }
        public object Init()
        {
            return null;
        }
        public void OnTabChange(object sender, object args)
        {
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Button btn = (Button)sender;
                if (btn.Name == "addBtn")
                {
                    RoleModal modal = new RoleModal
                    {
                        Title = "新增角色",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (modal.Open(new RoleData()) == true)
                    {
                        Model.LoadData();
                    }
                }
                else if (btn.Name == "refreshBtn")
                {
                    Model.LoadData();
                }
                else if (btn.Name == "menuBtn")
                {
                    Model.LoadRelData((RoleData)btn.DataContext);
                }
                else if (btn.Name == "editBtn")
                {
                    RoleModal modal = new RoleModal
                    {
                        Title = "编辑角色",
                        Owner = Window.GetWindow(this),
                        sqliteHelper = Model.sqliteHelper
                    };
                    if (modal.Open((RoleData)btn.DataContext) == true)
                    {
                        Model.LoadData();
                    }
                }
                else if (btn.Name == "delBtn")
                {
                    try
                    {
                        Model.DelSingleData((RoleData)btn.DataContext);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Delete Role Failed.", ex);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "删除失败", ToastPosition.Top);
                    }
                }
                else if (btn.Name == "saveMenuBtn")
                {
                    try
                    {
                        Model.SaveRelData(Model.SelectedValue);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "保存成功", ToastPosition.Top);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Save Role-Menu Failed.", ex);
                        Panuon.WPF.UI.Toast.Show((WindowX)Window.GetWindow(this), "保存失败", ToastPosition.Top);
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error("123", ex);
            }
        }
        public void SetLogger(ILog logger)
        {
            this.log = logger;
        }
    }
}
HMI.MenuManage/UserManage.xaml
New file
@@ -0,0 +1,132 @@
<hb:PluginControl x:Class="HMI.MenuManage.UserManage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.MenuManage"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
             mc:Ignorable="d"
             Background="{StaticResource WorkspaceBackground}"
             Padding="10"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="6*"/>
            <ColumnDefinition Width="4*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10">
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe60f;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
                            pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
                            pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="用户列表"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="table" Style="{StaticResource DefaultDataGrid}"
                          SelectedItem="{Binding SelectedUser}" ItemsSource="{Binding DataSource}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                          IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
    RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*"  Binding="{Binding Username}" Header="账号" />
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="用户名称" />
                        <!--<DataGridTextColumn Width="*" Binding="{Binding RoleCode}" Header="角色编号" />
    <DataGridTextColumn Width="*" Binding="{Binding RoleName}" Header="角色名称" />-->
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editBtn" Style="{StaticResource LinkButton}" Margin="0,0,10,0"
                        DataContext="{Binding }"
                        Click="Button_Click">编辑</Button>
                                        <Button x:Name="delBtn" Style="{StaticResource DangerLinkButton}"
                        DataContext="{Binding }"
                        Click="Button_Click">删除</Button>
                                    </StackPanel>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsMouseOver,
        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsSelected,
        RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                 Value="True">
                                            <Setter TargetName="editBtn" Property="Foreground" Value="#FFF"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
                            x:Name="saveRoleBtn" Click="Button_Click" pu:ButtonHelper.Icon="&#xe60d;" Content="保存"/>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                           VerticalAlignment="Center" HorizontalAlignment="Left" Text="用户角色"/>
                </DockPanel>
                <DataGrid x:Name="roleTable" Style="{StaticResource DefaultDataGrid}" SelectionMode="Single"
                          ItemsSource="{Binding Roles}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="授予" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                    HorizontalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="#" Binding="{Binding Converter={StaticResource rowNumber},
          RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
                  CanUserResize="False"/>
                        <DataGridTextColumn Width="*" Binding="{Binding RoleCode}" Header="角色编号" />
                        <DataGridTextColumn Width="*" Binding="{Binding RoleName}" Header="角色名称" />
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.MenuManage/UserManage.xaml.cs
New file
@@ -0,0 +1,93 @@
using System;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using HMI.MenuManage.Entity;
using HMI.MenuManage.Model;
using HMI.UI.Util;
using log4net;
namespace HMI.MenuManage
{
    /// <summary>
    /// UserManage.xaml 的交互逻辑
    /// </summary>
    public partial class UserManage : PluginControl
    {
        UserManageModel Model;
        private ILog log;
        public UserManage()
        {
            Model = new UserManageModel();
            DataContext = Model;
            Loaded += UserManage_Loaded;
            InitializeComponent();
        }
        private void UserManage_Loaded(object sender, RoutedEventArgs e)
        {
            Model.LoadData();
            this.Loaded -= UserManage_Loaded;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
            if (btn.Name == "addBtn")
            {
                UserModal userModal = new UserModal
                {
                    Title = "新增用户",
                    Owner = Window.GetWindow(this),
                    sqliteHelper = Model.sqliteHelper
                };
                if (userModal.Open(new UserData()) == true)
                {
                    Model.LoadData();
                }
            }
            else if (btn.Name == "refreshBtn")
            {
                Model.LoadData();
            }
            else if (btn.Name == "editBtn")
            {
                UserModal userModal = new UserModal
                {
                    Title = "编辑用户",
                    Owner = Window.GetWindow(this),
                    sqliteHelper = Model.sqliteHelper
                };
                if (userModal.Open((UserData)btn.DataContext) == true)
                {
                    Model.LoadData();
                }
            }
            else if (btn.Name == "delBtn") { }
            else if (btn.Name == "saveRoleBtn")
            {
                try
                {
                    Model.SaveUserRole();
                    Model.LoadUserRole();
                    ToastHelper.Success(Window.GetWindow(this), "保存成功");
                }
                catch (Exception ex)
                {
                    log.Error("Update User-Role Rel Failed.", ex);
                }
            }
        }
        public void SetLogger(ILog logger)
        {
            log = logger;
        }
        public void SetBaseFolder(string folder)
        {
            throw new NotImplementedException();
        }
    }
}
HMI.MenuManage/Util/RoleHelper.cs
New file
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using DatabaseHelper;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
namespace HMI.MenuManage.Util
{
    public class RoleHelper
    {
        /// <summary>
        /// 根据roleCode获取rel数据
        /// </summary>
        /// <param name="sqliteHelper"></param>
        /// <param name="roleCode"></param>
        /// <returns></returns>
        public static ObservableCollection<RoleData> SelectUserRoleData(SqliteHelper sqliteHelper,
            string roleCode)
        {
            ObservableCollection<RoleData> datas = new ObservableCollection<RoleData>();
            DataTable dt = sqliteHelper.GetDataTable($@"SELECT * FROM role AS r ;");
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var role = new RoleData
                    {
                        Id = dr["id"].ToString(),
                        Code = dr["code"].ToString(),
                        Name = dr["name"].ToString()
                    };
                    datas.Add(role);
                }
            }
            return datas;
        }
        public static void SaveRelData(SqliteHelper sqliteHelper, ObservableCollection<RoleMenuRel> rels, string roleCode)
        {
            List<RoleMenuRel> datas = new List<RoleMenuRel>();
            GetSelectedMenu(rels, datas);
            List<string> sqls = new List<string>() {
                $"DELETE FROM role_menu_rel WHERE roleCode='{roleCode}'"
            };
            foreach (var data in datas)
            {
                sqls.Add($"INSERT INTO role_menu_rel (roleCode,menuCode) VALUES ('{roleCode}','{data.MenuCode}');");
            }
            sqliteHelper.ExecuteBatchSql(sqls);
        }
        public static void GetSelectedMenu(IEnumerable<RoleMenuRel> rels, List<RoleMenuRel> datas)
        {
            foreach (var dr in rels)
            {
                if (dr.Selected)
                {
                    datas.Add(dr);
                }
                GetSelectedMenu(dr.Children, datas);
            }
        }
    }
}
HMI.MenuManage/Util/RoleMenuHelper.cs
New file
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using DatabaseHelper;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
namespace HMI.MenuManage.Util
{
    public class RoleMenuHelper
    {
        /// <summary>
        /// 根据roleCode获取rel数据
        /// </summary>
        /// <param name="sqliteHelper"></param>
        /// <param name="roleCode"></param>
        /// <returns></returns>
        public static ObservableCollection<RoleMenuRel> SelectRoleMenuTreeData(SqliteHelper sqliteHelper,
            string roleCode)
        {
            ObservableCollection<RoleMenuRel> treeData = new ObservableCollection<RoleMenuRel>();
            Dictionary<string, RoleMenuRel> cache = new Dictionary<string, RoleMenuRel>();
            DataTable dt = sqliteHelper.GetDataTable($@"SELECT m.*,t.roleCode,t.menuCode FROM menu AS m
LEFT JOIN (
SELECT rel.roleCode,rel.menuCode FROM role AS r
INNER JOIN role_menu_rel AS rel
ON r.code = rel.roleCode AND rel.roleCode='{roleCode}'
) AS t
ON m.code=t.menuCode
Order By m.level,m.id;");
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var menu = new RoleMenuRel
                    {
                        MenuCode = dr["code"].ToString(),
                        RoleCode = roleCode,
                        ParentCode = dr["parentCode"].ToString(),
                        MenuName = dr["name"].ToString(),
                        MenuIcon = dr["iconPath"].ToString(),
                        Selected = !(dr["menuCode"] is System.DBNull),
                    };
                    cache[menu.MenuCode] = menu;
                    if (cache.ContainsKey(menu.ParentCode))
                    {
                        cache[menu.ParentCode].AddChild(menu);
                    }
                    else
                    {
                        treeData.Add(menu);
                    }
                }
            }
            return treeData;
        }
        public static void SaveRelData(SqliteHelper sqliteHelper, IEnumerable<RoleMenuRel> rels, string roleCode, string firstSql, string lastSql)
        {
            List<RoleMenuRel> datas = new List<RoleMenuRel>();
            GetSelectedMenu(rels, datas);
            List<string> sqls = new List<string>() {
                firstSql, $"DELETE FROM role_menu_rel WHERE roleCode='{roleCode}'"
            };
            foreach (var data in datas)
            {
                sqls.Add($"INSERT INTO role_menu_rel (roleCode,menuCode) VALUES ('{roleCode}','{data.MenuCode}');");
            }
            sqls.Add(lastSql);
            sqliteHelper.ExecuteBatchSql(sqls);
        }
        public static void GetSelectedMenu(IEnumerable<RoleMenuRel> rels, List<RoleMenuRel> datas)
        {
            foreach (var dr in rels)
            {
                if (dr.Selected)
                {
                    datas.Add(dr);
                }
                GetSelectedMenu(dr.Children, datas);
            }
        }
    }
}
HMI.MenuManage/Util/UserRoleRelHelper.cs
New file
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Security;
using System.Windows.Markup;
using DatabaseHelper;
using HMI.Core.Util;
using HMI.MenuManage.Entity;
namespace HMI.MenuManage.Util
{
    public class UserRoleRelHelper
    {
        /// <summary>
        /// 根据roleCode获取rel数据
        /// </summary>
        /// <param name="sqliteHelper"></param>
        /// <param name="roleCode"></param>
        /// <returns></returns>
        public static ObservableCollection<UserRoleRel> SelectUserRoleData(SqliteHelper sqliteHelper, string username)
        {
            ObservableCollection<UserRoleRel> datas = new ObservableCollection<UserRoleRel>();
            DataTable dt = sqliteHelper.GetDataTable($@"SELECT r.*,t.username FROM role AS r LEFT JOIN
(SELECT u.username,rel.roleCode FROM user AS u INNER JOIN user_role_rel AS rel ON u.username = rel.username
AND u.username='{username}') AS t
ON r.code = t.roleCode ORDER BY r.id;");
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var role = new UserRoleRel
                    {
                        Id = dr["id"].ToString(),
                        RoleCode = dr["code"].ToString(),
                        RoleName = dr["name"].ToString(),
                        Selected = !(dr["username"] is DBNull)
                    };
                    datas.Add(role);
                }
            }
            return datas;
        }
        public static void SaveUserRoleRel(SqliteHelper sqliteHelper, IEnumerable<UserRoleRel> roles,
            UserData data, string firstSql, string lastSql)
        {
            var rels = roles.Where(d => d.Selected).ToList();
            var sqls = new List<string> {
                firstSql,
                $"DELETE FROM user_role_rel WHERE username = '{data.Username}';",
            };
            foreach (var rel in rels)
            {
                sqls.Add($"INSERT INTO user_role_rel (username,roleCode) " +
                    $"VALUES ('{data.Username}','{rel.RoleCode}');");
            }
            sqls.Add(lastSql);
            sqliteHelper.ExecuteBatchSql(sqls);
        }
    }
}
HMI.MenuManage/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.ParameterManage/ControlModel.cs
New file
@@ -0,0 +1,98 @@
using System.Collections.ObjectModel;
using System.Data;
using HMI.Core.Entity;
using HMI.Core.Util;
using HMI.ParameterManage.Entity;
namespace HMI.ParameterManage
{
    public class ControlModel : NotifyBase
    {
        public ControlModel(SqliteHelper sqliteHelper)
        {
            SqliteHelper = sqliteHelper;
        }
        public SqliteHelper SqliteHelper { get; private set; }
        private ObservableCollection<ProductData> products;
        public ObservableCollection<ProductData> Products
        {
            get { return products; }
            set { Set(ref products, value); }
        }
        private ObservableCollection<ParameterData> parameters;
        public ObservableCollection<ParameterData> Parameters
        {
            get { return parameters; }
            set { Set(ref parameters, value); }
        }
        private ProductData selectedProduct;
        public ProductData SelectedProduct
        {
            get { return selectedProduct; }
            set
            {
                Set(ref selectedProduct, value);
                LoadParameters();
            }
        }
        public void LoadProducts()
        {
            DataTable dt = SqliteHelper.GetDataTable("SELECT * FROM product ORDER BY id;");
            ObservableCollection<ProductData> products = new ObservableCollection<ProductData>();
            if (dt != null)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    var p = new ProductData
                    {
                        Id = dr["id"].ToString(),
                        Name = dr["name"].ToString(),
                        IsMaster = dr["isMaster"].ToString() == "1",
                        ParentId = dr["parentId"].ToString(),
                    };
                    products.Add(p);
                }
            }
            Products = products;
        }
        public void LoadParameters()
        {
            ObservableCollection<ParameterData> parameters = new ObservableCollection<ParameterData>();
            if (SelectedProduct != null)
            {
                DataTable dt = SqliteHelper.GetDataTable($"SELECT * FROM parameter WHERE productName='{SelectedProduct.Name}' ORDER BY id;");
                if (dt != null)
                {
                    foreach (DataRow dr in dt.Rows)
                    {
                        var p = new ParameterData
                        {
                            Id = dr["id"].ToString(),
                            Name = dr["name"].ToString(),
                            Value = dr["value"].ToString(),
                            Unit = dr["unit"].ToString(),
                            Description = dr["description"].ToString(),
                            PGroup = dr["pGroup"].ToString(),
                            ProductName = dr["productName"].ToString(),
                        };
                        parameters.Add(p);
                    }
                }
            }
            Parameters = parameters;
        }
    }
}
HMI.ParameterManage/Entity/ParameterData.cs
New file
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HMI.ParameterManage.Entity
{
    public class ParameterData
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
        public string Unit { get; set; }
        public string Description { get; set; }
        public string PGroup { get; set; }
        public string ProductName { get; set; }
    }
}
HMI.ParameterManage/Entity/ProductData.cs
New file
@@ -0,0 +1,11 @@

namespace HMI.ParameterManage.Entity
{
    public class ProductData
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsMaster { get; set; }
        public string ParentId { get; set; }
    }
}
HMI.ParameterManage/HMI.ParameterManage.csproj
New file
@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{736CD77D-B81E-4866-BB00-84B0B57110F0}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.ParameterManage</RootNamespace>
    <AssemblyName>HMI.ParameterManage</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="ParameterModal.xaml.cs">
      <DependentUpon>ParameterModal.xaml</DependentUpon>
    </Compile>
    <Compile Include="ProductModal.xaml.cs">
      <DependentUpon>ProductModal.xaml</DependentUpon>
    </Compile>
    <Page Include="ParameterManage.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="ControlModel.cs" />
    <Compile Include="Entity\ParameterData.cs" />
    <Compile Include="Entity\ProductData.cs" />
    <Compile Include="ParameterManage.xaml.cs">
      <DependentUpon>ParameterManage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="ParameterModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="ProductModal.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetPath)" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetFileName)"
copy /Y "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetName).pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
HMI.ParameterManage/ParameterManage.xaml
New file
@@ -0,0 +1,126 @@
<hb:PluginControl x:Class="HMI.ParameterManage.ParameterManage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:HMI.ParameterManage"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             mc:Ignorable="d"
             Padding="10"
             Loaded="UserControl_Loaded"
             Background="{StaticResource WorkspaceBackground}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="8*"/>
        </Grid.ColumnDefinitions>
        <Border Style="{StaticResource CardBoarder}" Margin="0,0,2,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" Cursor="">
                    <Button DockPanel.Dock="Right" x:Name="delBatchBtn" pu:ButtonHelper.Icon="&#xe60f;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
                        pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
                        pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                       VerticalAlignment="Center" HorizontalAlignment="Left" Text="产品列表"/>
                </DockPanel>
                <DataGrid Grid.Column="1" x:Name="table" Style="{StaticResource DefaultDataGrid}"
                      SelectedItem="{Binding SelectedProduct}" ItemsSource="{Binding Products}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                      IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="#" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Converter={StaticResource rowNumber},
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="产品名称" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" ui:StackPanelHelper.ChildSpace="10">
                                        <Button x:Name="editProductBtn" Style="{StaticResource RowLink}"  DataContext="{Binding }"
                    Click="Button_Click">编辑</Button>
                                        <Button x:Name="delProductBtn" Style="{StaticResource DangerLinkButton}"
                    DataContext="{Binding }"
                    Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
        <Border Grid.Column="1" Style="{StaticResource CardBoarder}" Margin="2,0,0,0">
            <StackPanel>
                <DockPanel Margin="0,0,0,10" >
                    <Button DockPanel.Dock="Right" pu:ButtonHelper.Icon="&#xe60f;" Style="{StaticResource DangerButton}">删除</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
    pu:ButtonHelper.Icon="&#xe631;" Margin="0,0,10,0" x:Name="refreshParameterBtn" Click="Button_Click">刷新</Button>
                    <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}"
    pu:ButtonHelper.Icon="&#xe61a;" Margin="0,0,10,0" x:Name="addParameterBtn" Click="Button_Click">新增</Button>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
   VerticalAlignment="Center" HorizontalAlignment="Left" Text="产品列表"/>
                    <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
                       VerticalAlignment="Center" HorizontalAlignment="Left" Text="产品参数"/>
                </DockPanel>
                <DataGrid x:Name="roleTable" Style="{StaticResource DefaultDataGrid}" SelectionMode="Single"
                      ItemsSource="{Binding Parameters}">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox VerticalAlignment="Center"
                HorizontalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="#" Binding="{Binding Converter={StaticResource rowNumber},
      RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
              CanUserResize="False"/>
                        <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="名称" />
                        <DataGridTextColumn Width="*" Binding="{Binding Value}" Header="值" />
                        <DataGridTextColumn Width="*" Binding="{Binding Unit}" Header="单位" />
                        <DataGridTextColumn Width="*" Binding="{Binding Description}" Header="描述" />
                        <DataGridTextColumn Width="*" Binding="{Binding PGroup}" Header="分组" />
                        <DataGridTemplateColumn Header="操作" Width="100" IsReadOnly="True" CanUserResize="False">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button x:Name="editParameterBtn" Style="{StaticResource RowLink}" Margin="0,0,10,0"
                DataContext="{Binding }"
                Click="Button_Click">编辑</Button>
                                        <Button x:Name="delParameterBtn" Style="{StaticResource DangerLinkButton}"
                DataContext="{Binding }"
                Click="Button_Click">删除</Button>
                                    </StackPanel>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.ParameterManage/ParameterManage.xaml.cs
New file
@@ -0,0 +1,119 @@
using System.IO;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Util;
using HMI.ParameterManage.Entity;
using HMI.UI.Util;
namespace HMI.ParameterManage
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class ParameterManage : PluginControl
    {
        private ControlModel Model;
        private SqliteHelper SqliteHelper;
        public ParameterManage()
        {
            InitializeComponent();
        }
        public override void InitFinish()
        {
            string dbpath = Path.Combine(BaseFolder, "ParameterManage.db");
            if (!File.Exists(dbpath))
            {
                SqliteHelper.CreateDatabase(dbpath);
            }
            SqliteHelper = new SqliteHelper(dbpath);
            SqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS product (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name TEXT NOT NULL,
                    isMaster INTEGER,
                    parentId INTEGER,
                    UNIQUE(name)
                    );
CREATE TABLE IF NOT EXISTS parameter (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name TEXT NOT NULL,
                    value TEXT,
                    unit TEXT,
                    description TEXT,
                    pGroup TEXT,
                    productName TEXT,
                    UNIQUE(productName,name)
                    );
");
            Model = new ControlModel(SqliteHelper);
            DataContext = Model;
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Model.LoadProducts();
            Loaded -= UserControl_Loaded;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var btn = (Button)sender;
            switch (btn.Name)
            {
                case "addBtn":
                    OpenAddDialog();
                    break;
                case "refrshBtn":
                    Model.LoadProducts();
                    break;
                case "refreshParameterBtn":
                    Model.LoadParameters();
                    break;
                case "addParameterBtn":
                    OpenAddParameterDialog(null);
                    break;
                case "editParameterBtn":
                    OpenAddParameterDialog((ParameterData)btn.DataContext);
                    break;
                default:
                    break;
            }
        }
        private void OpenAddDialog()
        {
            var win = new ProductModal
            {
                Title = "新增产品",
                Owner = Window.GetWindow(this),
                sqliteHelper = SqliteHelper
            };
            if (win.Open(new ProductData()) == true)
            {
                Model.LoadProducts();
            }
        }
        private void OpenAddParameterDialog(ParameterData data)
        {
            if (Model.SelectedProduct == null)
            {
                ToastHelper.Warning(Window.GetWindow(this), "请先选择一项产品");
                return;
            }
            var win = new ParameterModal
            {
                Title = data == null ? "新增参数" : "编辑参数",
                Owner = Window.GetWindow(this),
                sqliteHelper = SqliteHelper
            };
            if (win.Open(data ?? new ParameterData { ProductName = Model.SelectedProduct.Name }) == true)
            {
                Model.LoadParameters();
            }
        }
    }
}
HMI.ParameterManage/ParameterModal.xaml
New file
@@ -0,0 +1,62 @@
<pu:WindowX x:Class="HMI.ParameterManage.ParameterModal"
            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:HMI.ParameterManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Grid Margin="10">
        <StackPanel x:Name="formPanel">
            <pu:FormGroup x:Name="FgProductName" ui:FormGroupHelper.IsRequired="True"
                          Header="产品名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbProductName" IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgName" ui:FormGroupHelper.IsRequired="True"
                          ui:FormGroupHelper.ValidateMethod="ValidateName"
                              Header="参数名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgValue" Style="{StaticResource RequiredInputFormGroupStyle}"
                          ui:FormGroupHelper.IsRequired="True"
                  Header="参数值" GroupName="MyFormGroup">
                <TextBox x:Name="TbValue" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgUnit" Style="{StaticResource InputFormGroupStyle}"
      Header="单位" GroupName="MyFormGroup">
                <TextBox x:Name="TbUnit" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgDescription" Style="{StaticResource InputFormGroupStyle}"
Header="描述" GroupName="MyFormGroup">
                <TextBox x:Name="TbDescription" Style="{StaticResource MultilineEditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgPGroup" Style="{StaticResource InputFormGroupStyle}"
Header="组" GroupName="MyFormGroup">
                <TextBox x:Name="TbPGroup" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.ParameterManage/ParameterModal.xaml.cs
New file
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;
using Panuon.WPF.UI;
using HMI.Core.Annotation;
using HMI.Core.Entity;
using HMI.Core.Exceptions;
using HMI.Core.Util;
using HMI.ParameterManage.Entity;
using HMI.UI;
namespace HMI.ParameterManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class ParameterModal : WindowX
    {
        public ParameterModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        public ParameterData Data { get; set; }
        public SqliteHelper sqliteHelper { get; set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    Panuon.WPF.UI.Toast.Show(this, "Error!", ToastPosition.Top);
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        public bool ValidateName(object value, out string err)
        {
            err = null;
            string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
            string sql = $"SELECT 1 FROM parameter WHERE name='{TbName.Text}' AND productName='{Data.ProductName}'{segment};";
            if (sqliteHelper.HasData(sql))
            {
                err = "参数名称不能重复";
                return false;
            }
            return true;
        }
        private bool ValidateForm()
        {
            bool flag = FormGroupHelper.ValidateForm(formPanel);
            if (flag)
            {
                Data.Name = TbName.Text;
                Data.Value = TbValue.Text;
                Data.Unit = TbUnit.Text;
                Data.Description = TbDescription.Text;
                Data.ProductName = TbProductName.Text;
                Data.PGroup = TbPGroup.Text;
            }
            return flag;
        }
        public bool? Open(ParameterData data)
        {
            Data = data;
            TbName.Text = Data.Name;
            TbValue.Text = Data.Value;
            TbUnit.Text = Data.Unit;
            TbProductName.Text = Data.ProductName;
            TbDescription.Text = Data.Description;
            TbPGroup.Text = Data.PGroup;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (string.IsNullOrEmpty(Data.Id))
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(Data);
            }
        }
        public void InsertData(ParameterData data)
        {
            string sql = $@"INSERT INTO parameter (name,value,unit,description,productName,pGroup)
                VALUES
                ('{data.Name}','{data.Value}','{data.Unit}','{data.Description}','{data.ProductName}','{data.PGroup}');";
            sqliteHelper.ExecuteSql(sql);
        }
        public void UpdateData(ParameterData data)
        {
            string sql = $@"UPDATE parameter SET name='{data.Name}',value='{data.Value}',unit='{data.Unit}',
description='{data.Description}',productName='{data.ProductName}',pGroup='{data.PGroup}' WHERE id='{data.Id}';";
            sqliteHelper.ExecuteSql(sql);
        }
    }
}
HMI.ParameterManage/ProductModal.xaml
New file
@@ -0,0 +1,57 @@
<pu:WindowX x:Class="HMI.ParameterManage.ProductModal"
            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:HMI.ParameterManage"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            mc:Ignorable="d"
            Title="Form"
            SizeToContent="Height"
            Width="600"
            WindowStartupLocation="CenterOwner"
            BorderBrush="#3E3E45"
            BorderThickness="1"
            Foreground="Black"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXModalDialog.ButtonStyle="{StaticResource ModalButton}"
            pu:WindowXCaption.Foreground="#FFF"
            pu:WindowXCaption.Buttons="Close"
            pu:WindowXModalDialog.Buttons="OKCancel"
            pu:WindowXModalDialog.CancelButtonContent="取消"
            pu:WindowXModalDialog.OKButtonContent="保存"
            pu:WindowXModalDialog.ButtonPanelMargin="0,30,10,10">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TreeView" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </Window.Resources>
    <Grid Margin="10">
        <StackPanel>
            <pu:FormGroup x:Name="FgName" Style="{StaticResource RequiredInputFormGroupStyle}"
                              Header="产品名称" GroupName="MyFormGroup">
                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgIsMaster" Style="{StaticResource InputFormGroupStyle}"
                          Header="是否样件" GroupName="MyFormGroup">
                <CheckBox x:Name="TbIsMaster"  />
            </pu:FormGroup>
            <pu:FormGroup x:Name="FgParentId" Style="{StaticResource InputFormGroupStyle}"
                  Header="继承" GroupName="MyFormGroup">
                <TextBox x:Name="TbParentId" Style="{StaticResource EditorTextBoxStyle}" />
            </pu:FormGroup>
        </StackPanel>
    </Grid>
</pu:WindowX>
HMI.ParameterManage/ProductModal.xaml.cs
New file
@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;
using Panuon.WPF.UI;
using HMI.Core.Annotation;
using HMI.Core.Entity;
using HMI.Core.Exceptions;
using HMI.Core.Util;
using HMI.ParameterManage.Entity;
using HMI.UI;
namespace HMI.ParameterManage
{
    /// <summary>
    /// ModalWindow.xaml 的交互逻辑
    /// </summary>
    public partial class ProductModal : WindowX
    {
        public ProductModal()
        {
            this.DataContext = this;
            InitializeComponent();
        }
        public ProductData Data { get; set; }
        public SqliteHelper sqliteHelper { get; set; }
        private bool _isProcessing;
        protected override void OnClosing(CancelEventArgs e)
        {
            //Press Continue Button
            if (DialogResult == true)
            {
                if (_isProcessing)
                {
                    e.Cancel = true;
                    return;
                }
                var okButton = WindowXModalDialog.GetOKButton(this);
                try
                {
                    _isProcessing = true;
                    ButtonHelper.SetIsPending(okButton, true);
                    if (!ValidateForm())
                    {
                        e.Cancel = true;
                        return;
                    }
                    SaveOrUpdateData();
                }
                catch (Exception ex)
                {
                    Panuon.WPF.UI.Toast.Show(this, "Error!", ToastPosition.Top);
                    e.Cancel = true;
                    return;
                }
                finally
                {
                    _isProcessing = false;
                    ButtonHelper.SetIsPending(okButton, false);
                }
            }
            base.OnClosing(e);
        }
        private bool ValidateForm()
        {
            try
            {
                //
                FormValidateException ex = new FormValidateException();
                string segment = string.IsNullOrEmpty(Data.Id) ? "" : $" AND id!='{Data.Id}'";
                string sql = $"SELECT 1 FROM product WHERE name='{TbName.Text}'{segment};";
                if (string.IsNullOrEmpty(TbName.Text))
                {
                    ex.Result.Add(new FormGroupValidateResult("请输入产品名称", FgName));
                }
                else if (sqliteHelper.HasData(sql))
                {
                    ex.Result.Add(new FormGroupValidateResult("产品名称不能重复", FgName));
                }
                else
                {
                    FgName.ValidateResult = ValidateResult.None;
                    FgName.Message = null;
                }
                if (!ex.Success)
                {
                    throw ex;
                }
            }
            catch (FormValidateException ex)
            {
                foreach (var data in ex.Result)
                {
                    var fg = (FormGroup)data.Sender;
                    fg.ValidateResult = ValidateResult.Error;
                    fg.Message = data.Message;
                }
                return false;
            }
            Data.Name = TbName.Text;
            Data.IsMaster = TbIsMaster.IsChecked == true;
            Data.ParentId = TbParentId.Text;
            return true;
        }
        public bool? Open(ProductData data)
        {
            Data = data;
            TbName.Text = Data.Name;
            TbIsMaster.IsChecked = Data.IsMaster;
            TbParentId.Text = Data.ParentId;
            return this.ShowDialog();
        }
        private void SaveOrUpdateData()
        {
            if (string.IsNullOrEmpty(Data.Id))
            {
                InsertData(Data);
            }
            else
            {
                UpdateData(Data);
            }
        }
        public void InsertData(ProductData data)
        {
            string sql = $@"INSERT INTO product (name,isMaster,parentId)
                VALUES
                ('{data.Name}',{(data.IsMaster ? 1 : 0)} ,{data.ParentId} );";
            sqliteHelper.ExecuteSql(sql);
        }
        public void UpdateData(ProductData data)
        {
            string sql = $@"UPDATE product SET name='{data.Name}',isMaster='{(data.IsMaster ? 1 : 0)}',parentId = {data.ParentId}
WHERE id='{data.Id}';";
            sqliteHelper.ExecuteSql(sql);
        }
    }
}
HMI.ParameterManage/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.ParameterManage")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.ParameterManage")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.ParameterManage/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.ParameterManage.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.ParameterManage.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.ParameterManage/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.ParameterManage/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.ParameterManage.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.ParameterManage/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.SettingControl/HMI.SettingControl.csproj
New file
@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{3DD748AC-9708-49AD-B846-659214035588}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>HMI.SettingControl</RootNamespace>
    <AssemblyName>HMI.SettingControl</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="SystemConfig.xaml.cs">
      <DependentUpon>SystemConfig.xaml</DependentUpon>
    </Compile>
    <Page Include="SettingControl.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="SettingControl.xaml.cs">
      <DependentUpon>SettingControl.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="SystemConfig.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetPath)" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetFileName)"
copy /Y "$(TargetDir)$(TargetName).pdb" "$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName)\Plugin\$(TargetName).pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
HMI.SettingControl/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.SettingControl")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("HMI.SettingControl")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.SettingControl/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.SettingControl.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.SettingControl.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.SettingControl/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.SettingControl/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.SettingControl.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
HMI.SettingControl/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.SettingControl/SettingControl.xaml
New file
@@ -0,0 +1,77 @@
<hb:PluginControl x:Class="HMI.SettingControl.SettingControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:local="clr-namespace:HMI.SettingControl"
             mc:Ignorable="d"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI"
             Loaded="UserControl_Loaded"
             Background="{StaticResource WorkspaceBackground}"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Border Style="{StaticResource CardBoarder}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock VerticalAlignment="Center" Text="系统设置" Margin="0,0,0,10"
                           FontSize="{StaticResource TitleFontSize}" />
                <TabControl Grid.Row="1"
pu:TabControlHelper.HeaderPanelAlignment="Front"
pu:TabControlHelper.ItemsBackground="{StaticResource PrimaryColorContract}"
pu:TabControlHelper.ItemsForeground="{StaticResource PrimaryColor}"
pu:TabControlHelper.ItemsHeight="40"
pu:TabControlHelper.ItemsHorizontalHeaderAlignment="Left"
pu:TabControlHelper.ItemsHoverBackground="{StaticResource SecondPrimaryColor}"
pu:TabControlHelper.ItemsHoverForeground="{StaticResource SecondPrimaryColorContract}"
pu:TabControlHelper.ItemsRibbonLineMargin="0,5,0,5"
pu:TabControlHelper.ItemsRibbonLineThickness="4"
pu:TabControlHelper.ItemsRibbonLineVisibility="Visible"
pu:TabControlHelper.ItemsSelectedBackground="{StaticResource PrimaryColor}"
pu:TabControlHelper.ItemsSelectedForeground="{StaticResource PrimaryColorContract}"
pu:TabControlHelper.ItemsSelectedRibbonLineBrush="#FFFFFF"
pu:TabControlHelper.ItemsSelectedRibbonLineThickness="2"
TabStripPlacement="Left"
        BorderThickness="0"
        BorderBrush="{StaticResource CommonBoarder}"
        ItemsSource="{Binding TabItems}">
                <TabControl.Resources>
                    <Style TargetType="TabControl" BasedOn="{StaticResource {x:Static pu:StyleKeys.TabControlStyle}}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="TabControl">
                                    <Border BorderBrush="Black" BorderThickness="1">
                                            <ContentPresenter/>
                                        </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TabControl.Resources>
                </TabControl>
            </Grid>
        </Border>
    </Grid>
</hb:PluginControl>
HMI.SettingControl/SettingControl.xaml.cs
New file
@@ -0,0 +1,104 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using log4net;
namespace HMI.SettingControl
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class SettingControl : PluginControl
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void Set(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                Set(propertyName);
            }
        }
        #endregion
        #region 属性
        private ILog log;
        private ObservableCollection<TabItem> tabItems;
        public ObservableCollection<TabItem> TabItems
        {
            get { return tabItems; }
            set { Set(ref tabItems, value); }
        }
        #endregion
        #region PluginApi
        public object Init()
        {
            return null;
        }
        public object Active()
        {
            return null;
        }
        public object DeActive()
        {
            return null;
        }
        public void OnTabChange(object sender, object args)
        {
        }
        public void SetLogger(ILog logger)
        {
            log = logger;
        }
        #endregion
        public SettingControl()
        {
            DataContext = this;
            InitializeComponent();
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            TabItems = new ObservableCollection<TabItem>() {
                new TabItem
                {
                    Header = "系统设置",
                    Content = new SystemConfig()
                }
            };
            foreach (var d in PluginLoader.Instance.Plugins)
            {
                if (d.SettingControl == null) continue;
                TabItems.Add(new TabItem
                {
                    Header = d.Name,
                    Tag = d,
                    Content = d.SettingControl
                });
            }
            Loaded -= UserControl_Loaded;
        }
    }
}
HMI.SettingControl/SystemConfig.xaml
New file
@@ -0,0 +1,46 @@
<UserControl x:Class="HMI.SettingControl.SystemConfig"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:HMI.SettingControl"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
            mc:Ignorable="d"
            Background="#FFF"
            d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <pu:StyleDictionary Includes="TabControl" />
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <GroupBox Header="程序设置" Padding="10">
            <StackPanel>
                <pu:FormGroup Header="Logo" GroupName="MyFormGroup">
                    <TextBox Text="{Binding LogoPath}"/>
                </pu:FormGroup>
                <pu:FormGroup Header="标题" GroupName="MyFormGroup">
                    <TextBox Text="{Binding WindowTitle}"/>
                </pu:FormGroup>
            </StackPanel>
        </GroupBox>
        <DockPanel Grid.Row="1" LastChildFill="False">
            <Button DockPanel.Dock="Right" Style="{StaticResource PrimaryButton}" Click="Button_Click">保存</Button>
        </DockPanel>
    </Grid>
</UserControl>
HMI.SettingControl/SystemConfig.xaml.cs
New file
@@ -0,0 +1,27 @@
using System.Windows.Controls;
using HMI.Core.Setting;
using HMI.Core.Util;
namespace HMI.SettingControl
{
    /// <summary>
    /// SystemConfig.xaml 的交互逻辑
    /// </summary>
    public partial class SystemConfig : UserControl
    {
        public SystemConfig()
        {
            var model = new SystemSettingModel();
            BeanUtil.Copy(SystemSetting.Instance, model);
            DataContext = model;
            InitializeComponent();
        }
        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            SystemSettingModel settingModel = (SystemSettingModel)DataContext;
            XmlHelper.SerializeToXml(PathHelper.ConfigFolder + "\\SystemConfig.xml",settingModel);
            SystemSetting.ReLoad();
        }
    }
}
HMI.SettingControl/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.UI/Converter/DataGridRowNumberConverter.cs
New file
@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
namespace HMI.UI.Converter
{
    public class DataGridRowNumberConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is DataGridRow row)
            {
                return row.GetIndex() + 1;
            }
            return -1;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
}
HMI.UI/Converter/PathImageConverter.cs
New file
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media.Imaging;
using HMI.Core.Util;
namespace HMI.UI.Converter
{
    public class PathImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;
            string imagePath = Path.Combine(PathHelper.IconFolder, value.ToString());
            if (!File.Exists(imagePath)) return null;
            return new BitmapImage(new Uri(imagePath));
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
}
HMI.UI/DpUtil/DpPlugin.cs
New file
@@ -0,0 +1,22 @@
using System.Windows.Media;
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI
{
    public class DpPlugin
    {
        public static Brush GetHoverColor(Button button)
        {
            return (Brush)button.GetValue(HoverColorProperty);
        }
        public static void SetHoverColor(Button button, Brush value)
        {
            button.SetValue(HoverColorProperty, value);
        }
        public static readonly DependencyProperty HoverColorProperty = DependencyProperty.RegisterAttached("HoverColor", typeof(Brush), typeof(DpPlugin),
            new PropertyMetadata(new SolidColorBrush(Color.FromRgb(255, 255, 255))));
    }
}
HMI.UI/DpUtil/FormGroupHelper.cs
New file
@@ -0,0 +1,99 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Panuon.WPF.UI;
using Panuon.WPF.UI.Configurations;
namespace HMI.UI
{
    public class FormGroupHelper
    {
        public static bool GetIsRequired(FormGroup fg)
        {
            return (bool)fg.GetValue(IsRequiredProperty);
        }
        public static void SetIsRequired(FormGroup fg, bool value)
        {
            fg.SetValue(IsRequiredProperty, value);
        }
        public static readonly DependencyProperty IsRequiredProperty =
            DependencyProperty.RegisterAttached("IsRequired", typeof(bool), typeof(FormGroupHelper),
            new PropertyMetadata(false, (d, e) =>
            {
                if (!(d is FormGroup fg)) return;
                if ((bool)e.NewValue)
                {
                    var style = Application.Current.FindResource("RequiredInputFormGroupStyle") as Style;
                    fg.Style = style;
                }
                else
                {
                    var style = Application.Current.FindResource("RequiredInputFormGroupStyle") as Style;
                    fg.Style = style;
                }
            }));
        public delegate bool ValidateMethodDelegate(object value, out string msg);
        public static ValidateMethodDelegate GetValidateMethod(FormGroup fg)
        {
            return (ValidateMethodDelegate)fg.GetValue(ValidateMethodProperty);
        }
        public static void SetValidateMethod(FormGroup fg, ValidateMethodDelegate value)
        {
            fg.SetValue(ValidateMethodProperty, value);
        }
        public static readonly DependencyProperty ValidateMethodProperty =
            DependencyProperty.RegisterAttached("ValidateMethod", typeof(ValidateMethodDelegate), typeof(FormGroupHelper),
            new PropertyMetadata(null));
        public static bool ValidateForm(StackPanel fgPanel)
        {
            if (fgPanel.Children == null) return true;
            bool res = true;
            foreach (var child in fgPanel.Children)
            {
                if (child is FormGroup fg)
                {
                    object content = fg.Content;
                    object value = GetValidateValue(content);
                    if (GetIsRequired(fg) && string.IsNullOrEmpty(value.ToString()))
                    {
                        fg.ValidateResult = ValidateResult.Error;
                        fg.Message = $"{fg.Header}不能为空";
                        res = false;
                    }
                    else
                    {
                        ValidateMethodDelegate method = GetValidateMethod(fg);
                        if (method != null && !method(value, out string err))
                        {
                            fg.ValidateResult = ValidateResult.Error;
                            fg.Message = err;
                            res = false;
                        }
                    }
                }
            }
            return res;
        }
        private static object GetValidateValue(object content)
        {
            if (content == null) return null;
            if (content is UIElementCollection elementCollection)
                return elementCollection.Count > 0 ? GetValidateValue(elementCollection[0]) : null;
            if (content is TextBox tb) return tb.Text;
            if (content is CheckBox cb) return cb.IsChecked;
            if (content is ComboBox cBox) return cBox.Text;
            if (content is Panel panel) return GetValidateValue(panel.Children);
            return null;
        }
    }
}
HMI.UI/DpUtil/StackPanelHelper.cs
New file
@@ -0,0 +1,38 @@
using System.Windows;
using System.Windows.Controls;
namespace HMI.UI
{
    public class StackPanelHelper
    {
        public static double GetChildSpace(StackPanel sp)
        {
            return (double)sp.GetValue(ChildSpaceProperty);
        }
        public static void SetChildSpace(StackPanel sp, double value)
        {
            sp.SetValue(ChildSpaceProperty, value);
        }
        public static readonly DependencyProperty ChildSpaceProperty =
            DependencyProperty.RegisterAttached("ChildSpace", typeof(double), typeof(StackPanelHelper),
            new PropertyMetadata(0d, (d, e) =>
            {
                if (!(d is StackPanel sp)) return;
                if (sp.Children == null) return;
                var children = sp.Children;
                double space = (double)e.NewValue;
                if (sp.Orientation == Orientation.Horizontal)
                {
                    for (int i = 0; i < children.Count; i++)
                    {
                        if (children[i] is FrameworkElement ui)
                        {
                            ui.Margin = new Thickness(0, 0, (i == children.Count - 1) ? 0 : space, 0);
                        }
                    }
                }
            }));
    }
}
HMI.UI/DpUtil/TreeViewHelper.cs
New file
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using System.ComponentModel;
namespace HMI.UI
{
    public class TreeViewHelper
    {
        public static bool GetAutoExpand(TreeView treeView)
        {
            return (bool)treeView.GetValue(AutoExpandProperty);
        }
        public static void SetAutoExpand(TreeView treeView, bool value)
        {
            treeView.SetValue(AutoExpandProperty, value);
        }
        public static readonly DependencyProperty AutoExpandProperty =
            DependencyProperty.RegisterAttached("AutoExpand", typeof(bool), typeof(TreeViewHelper),
            new PropertyMetadata(false, (d, e) =>
            {
                if (!(d is TreeView treeView)) return;
                if ((bool)e.NewValue)
                {
                    var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(DataGrid));
                    dpd?.AddValueChanged(treeView, TreeView_ItemSourceChanged);
                }
                else
                {
                    var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(DataGrid));
                    dpd?.RemoveValueChanged(treeView, TreeView_ItemSourceChanged);
                }
            }));
        private static void TreeView_ItemSourceChanged(object sender, EventArgs e)
        {
            TreeView tree = sender as TreeView;
            if (!tree.IsLoaded)
            {
                tree.Loaded += Tree_Loaded;
            }
            else
            {
                ExpandAll(tree);
            }
        }
        private static void Tree_Loaded(object sender, RoutedEventArgs e)
        {
            TreeView tree = sender as TreeView;
            ExpandAll(tree);
            tree.Loaded -= Tree_Loaded;
        }
        public static void ExpandAll(TreeView treeView)
        {
            foreach (var item in treeView.Items)
            {
                var temp = treeView.ItemContainerGenerator.ContainerFromItem(item);
                if (temp is TreeViewItem treeViewItem)
                {
                    ExpandTreeViewItem(treeViewItem);
                }
            }
        }
        public static void ExpandTreeViewItem(TreeViewItem item)
        {
            item.IsExpanded = true;
            // 确保子项已经生成
            item.ApplyTemplate();
            if (item.Items.Count > 0)
            {
                foreach (var subItem in item.Items)
                {
                    if (item.ItemContainerGenerator.ContainerFromItem(subItem) is TreeViewItem childItem)
                    {
                        ExpandTreeViewItem(childItem);
                    }
                }
            }
        }
    }
}
HMI.UI/Entity/NotifyBase.cs
New file
@@ -0,0 +1,97 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace HMI.UI.Entity
{
    public class RelayCommand<T> : ICommand
    {
        /// <summary>
        /// 命令能否执行
        /// </summary>
        readonly Func<bool> _canExecute;
        /// <summary>
        /// 命令执行的方法
        /// </summary>
        readonly Action<T> _execute;
        /// <summary>
        /// 命令的构造函数
        /// </summary>
        /// <param name="action">命令需执行的方法</param>
        /// <param name="canExecute">命令是否可以执行的方法</param>
        public RelayCommand(Action<T> action, Func<bool> canExecute)
        {
            _execute = action;
            _canExecute = canExecute;
        }
        public RelayCommand(Action<T> action)
        {
            _execute = action;
        }
        /// <summary>
        /// 判断命令是否可以执行
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(Object parameter)
        {
            if (_canExecute == null)
                return true;
            return _canExecute();
        }
        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(Object parameter)
        {
            _execute((T)parameter);
        }
        /// <summary>
        /// 事件追加、移除
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }
    }
    public class NotifyBase : INotifyPropertyChanged
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
    }
}
HMI.UI/HMI.UI.csproj
New file
@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>HMI.UI</RootNamespace>
    <AssemblyName>HMI.UI</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup>
    <StartupObject />
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Converter\DataGridRowNumberConverter.cs" />
    <Compile Include="Converter\PathImageConverter.cs" />
    <Compile Include="DpUtil\FormGroupHelper.cs" />
    <Compile Include="DpUtil\StackPanelHelper.cs" />
    <Compile Include="DpUtil\TreeViewHelper.cs" />
    <Compile Include="Style\BaseWindow.xaml.cs">
      <DependentUpon>BaseWindow.xaml</DependentUpon>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Compile>
    <Compile Include="DpUtil\DpPlugin.cs" />
    <Compile Include="Util\IValueValidator.cs" />
    <Compile Include="Util\MsgBoxHelper.cs" />
    <Compile Include="Util\ToastHelper.cs" />
    <Resource Include="Resources\Font\Font.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Resource>
    <Page Include="Style\MsgBoxStyle.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Style\PanuonResource.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Page>
    <Page Include="Style\BaseWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Page>
    <Page Include="Style\Colors.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Page>
    <Page Include="Style\FormStyle.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Page>
    <Page Include="Style\WindowResources.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <Resource Include="Resources\Font\iconfont.ttf">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Resource>
  </ItemGroup>
  <ItemGroup>
    <Resource Include="Resources\Icon\xinzeng_1.png" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.UI/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("HMI.UI")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HMI.UI")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                                     //(未在页面中找到资源时使用,
                                     //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                              //(未在页面中找到资源时使用,
                                              //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.UI/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.UI.Properties {
    using System;
    /// <summary>
    ///   一个强类型的资源类,用于查找本地化的字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存的 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.UI.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.UI/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.UI/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.UI.Properties {
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
    }
}
HMI.UI/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.UI/Resources/Font/Font.xaml
New file
@@ -0,0 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <FontFamily x:Key="iconfont">
        /WPFProject;component/Resources/Font/#iconfont
    </FontFamily>
</ResourceDictionary>
HMI.UI/Resources/Font/iconfont.ttf
Binary files differ
HMI.UI/Resources/Icon/xinzeng_1.png
HMI.UI/Style/BaseWindow.xaml
New file
@@ -0,0 +1,62 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ui="clr-namespace:HMI.UI"
                    x:Class="HMI.UI.BaseWindowTemplate">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="ui:BaseWindow" x:Key="BaseWindow">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CornerRadius="0" CaptionHeight="40" GlassFrameThickness="0"
      UseAeroCaptionButtons="False" NonClientFrameEdges="None"  />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ui:BaseWindow">
                    <Border BorderThickness="1" BorderBrush="Gray">
                        <Grid x:Name="mainGrid" Background="#FFF">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="40"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Grid Grid.Row="0" Background="{StaticResource MainBorder}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <StackPanel x:Name="LeftPanel" Orientation="Horizontal" Grid.Column="0">
                                    <Border x:Name="TitlePanel">
                                        <Label VerticalContentAlignment="Center" Foreground="#FFF" Content="{TemplateBinding Title}"/>
                                    </Border>
                                </StackPanel>
                                <StackPanel x:Name="RightPanel" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
                                    <Button Style="{StaticResource WindowButton}" Click="Min_Click">&#xe650;</Button>
                                    <Button Style="{StaticResource WindowButton}" Click="Max_Click">&#xe602;</Button>
                                    <Button Style="{StaticResource WindowCloseButton}" Click="Close_Click">&#xe609;</Button>
                                </StackPanel>
                            </Grid>
                            <AdornerDecorator Grid.Row="1">
                                <ContentPresenter  Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}"
ContentTemplate="{TemplateBinding ContentTemplate}"  />
                            </AdornerDecorator>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="WindowState" Value="Maximized">
                            <Setter TargetName="mainGrid" Property="Margin" Value="8,8,8,8"></Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
HMI.UI/Style/BaseWindow.xaml.cs
New file
@@ -0,0 +1,45 @@

using System.Windows;
using System.Windows.Controls;
namespace HMI.UI
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class BaseWindowTemplate
    {
        void Min_Click(object sender, RoutedEventArgs e)
        {
            Window.GetWindow(sender as FrameworkElement).WindowState = WindowState.Minimized;
        }
        void Max_Click(object sender, RoutedEventArgs e)
        {
            var temp = Window.GetWindow(sender as FrameworkElement).WindowState;
            Window.GetWindow(sender as FrameworkElement).WindowState = temp == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
        }
        void Close_Click(object sender, RoutedEventArgs e)
        {
            Window.GetWindow(sender as FrameworkElement).Close();
        }
    }
    public partial class BaseWindow : Window
    {
        public void AddButton(Button button)
        {
            var panelsda = Template.FindName("RightPanel", this);
            var panel = (StackPanel)this.Template.FindName("RightPanel", this);
            panel?.Children.Add(button);
        }
        public void InsertButton(Button button,int index)
        {
            var panelsda = Template.FindName("RightPanel", this);
            var panel = (StackPanel)this.Template.FindName("RightPanel", this);
            panel?.Children.Insert(index, button);
        }
    }
}
HMI.UI/Style/Colors.xaml
New file
@@ -0,0 +1,39 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Brush x:Key="WorkspaceBackground">#f0f2f5</Brush>
    <Brush x:Key="TableHeaderBackground">#fafafa</Brush>
    <Brush x:Key="TableBoarder">#e8e8e8</Brush>
    <Brush x:Key="CommonBoarder">#e8e8e8</Brush>
    <Brush x:Key="PrimaryColor">#0d4d84</Brush>
    <Brush x:Key="PrimaryColorContract">#FFF</Brush>
    <Brush x:Key="SecondPrimaryColor">#3692e1</Brush>
    <Brush x:Key="SecondPrimaryColorContract">#FFF</Brush>
    <Brush x:Key="DangerColor">#d64343</Brush>
    <Brush x:Key="DangerColorContrast">#FFF</Brush>
    <Brush x:Key="SuccessColor">Green</Brush>
    <Brush x:Key="SuccessColorContrast">#FFF</Brush>
    <Brush x:Key="WarningColor">#e17933</Brush>
    <Brush x:Key="WarningColorContract">#FFF</Brush>
    <Brush x:Key="WindowBar">#0d4d84</Brush>
    <Brush x:Key="MainBorder">#0d4d84</Brush>
    <Brush x:Key="MainMenu">#1d6eb2</Brush>
    <Brush x:Key="ChildBorder">#3692e1</Brush>
    <Brush x:Key="MainStatusBar">#1d6eb2</Brush>
    <Brush x:Key="SecondaryImportant">Orange</Brush>
    <Brush x:Key="Important">#e17933</Brush>
    <Brush x:Key="ImportantFont">#FFF</Brush>
    <Brush x:Key="ButtonColor">#103156</Brush>
    <Brush x:Key="ButtonSelected">#02579E</Brush>
    <Brush x:Key="ButtonFont">White</Brush>
    <Brush x:Key="ButtonFontSelected">White</Brush>
</ResourceDictionary>
HMI.UI/Style/FormStyle.xaml
New file
@@ -0,0 +1,148 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui">
    <ResourceDictionary.MergedDictionaries>
        <pu:StyleDictionary Includes="TextBox,ComboBox,Button" />
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="ModalButton" TargetType="Button" BasedOn="{StaticResource {x:Static pu:WindowXModalDialog.ButtonStyleKey}}">
        <Setter Property="pu:ButtonHelper.PendingSpinStyle">
            <Setter.Value>
                <Style TargetType="pu:Spin" BasedOn="{StaticResource {x:Static pu:ButtonHelper.PendingSpinStyleKey}}">
                    <Setter Property="SpinStyle" Value="Ring2" />
                </Style>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="pu:ButtonHelper.IsPending" Value="True">
                <Setter Property="pu:ButtonHelper.ClickEffect" Value="Shake" />
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBlock Text="请稍后..." />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="Tag" Value="OK">
                <Setter Property="Background" Value="{StaticResource PrimaryColor}" />
                <Setter Property="Foreground" Value="{StaticResource PrimaryColorContract}" />
                <Setter Property="Padding" Value="20,0" />
                <Setter Property="Height" Value="35" />
            </Trigger>
            <Trigger Property="Tag" Value="Cancel">
                <Setter Property="Background" Value="{x:Null}" />
                <Setter Property="Foreground" Value="Gray" />
                <Setter Property="Cursor" Value="Hand" />
                <Setter Property="Padding" Value="10,0" />
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBlock Text="{Binding}" TextDecorations="Underline" />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="SearchButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="pu:ButtonHelper.CornerRadius" Value="0" />
        <Setter Property="Background" Value="{StaticResource PrimaryColor}" />
        <Setter Property="Foreground" Value="{StaticResource PrimaryColorContract}" />
        <Setter Property="FontFamily" Value="{StaticResource iconfont}" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Content" Value="&#xe752;"/>
        <Setter Property="pu:ButtonHelper.PendingSpinStyle">
            <Setter.Value>
                <Style TargetType="pu:Spin" BasedOn="{StaticResource {x:Static pu:ButtonHelper.PendingSpinStyleKey}}">
                    <Setter Property="SpinStyle" Value="Ring2" />
                </Style>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="pu:ButtonHelper.IsPending" Value="True">
                <Setter Property="pu:ButtonHelper.ClickEffect" Value="Shake" />
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBlock Text="Click again ?" />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="EditorTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
        <Setter Property="pu:TextBoxHelper.FocusedBorderBrush" Value="#6CBCEA" />
        <Setter Property="pu:TextBoxHelper.FocusedShadowColor" Value="#6CBCEA" />
        <Setter Property="pu:TextBoxHelper.CornerRadius" Value="0" />
        <Setter Property="pu:TextBoxHelper.WatermarkForeground" Value="#4A4A4A" />
        <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5,0" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Style.Triggers>
            <Trigger Property="IsReadOnly" Value="True">
                <Setter Property="Background" Value="LightGray"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="MultilineEditorTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
        <Setter Property="Height" Value="100" />
        <Setter Property="Padding" Value="5,10" />
        <Setter Property="AcceptsReturn" Value="True" />
        <Setter Property="TextWrapping" Value="Wrap" />
        <Setter Property="VerticalContentAlignment" Value="Top" />
    </Style>
    <Style x:Key="InputFormGroupStyle" TargetType="pu:FormGroup">
        <Style.Resources>
            <Style TargetType="TextBox" BasedOn="{StaticResource EditorTextBoxStyle}">
            </Style>
        </Style.Resources>
        <Setter Property="HeaderWidth" Value="70" />
        <Setter Property="HeaderHeight" Value="35" />
        <Setter Property="Orientation" Value="Horizontal" />
        <Setter Property="Margin" Value="0,10,0,0" />
    </Style>
    <Style x:Key="RequiredInputFormGroupStyle" TargetType="pu:FormGroup" BasedOn="{StaticResource InputFormGroupStyle}">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="*" Foreground="Red" />
                        <ContentControl Content="{Binding}" />
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="FormComboxStyle" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
        <Setter Property="pu:ComboBoxHelper.CornerRadius" Value="0"/>
        <Setter Property="pu:ComboBoxHelper.ItemsHoverBackground" Value="{StaticResource WindowBar}"/>
        <Setter Property="pu:ComboBoxHelper.ItemsHoverForeground" Value="#FFF"/>
        <Setter Property="pu:ComboBoxHelper.ItemsSelectedForeground" Value="Black"/>
        <Setter Property="pu:ComboBoxHelper.HoverBorderBrush" Value="#007ACC" />
        <Setter Property="pu:ComboBoxHelper.FocusedBorderBrush" Value="#007ACC" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Margin" Value="0" />
        <Setter Property="Background" Value="#FFF" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="SelectedValuePath" Value="Content"/>
    </Style>
</ResourceDictionary>
HMI.UI/Style/MsgBoxStyle.xaml
New file
@@ -0,0 +1,133 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                     xmlns:pw="https://opensource.panuon.com/wpf"
                        xmlns:pu="https://opensource.panuon.com/wpf-ui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:puconfig="clr-namespace:Panuon.WPF.UI.Configurations;assembly=Panuon.WPF.UI">
    <pu:MessageBoxXSettings x:Key="MsgBoxWarn"
                                 InteropOwnersMask="True"
                                 InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
                <Setter Property="pu:WindowXCaption.Background" Value="{StaticResource WarningColor}" />
                <Setter Property="pu:WindowXCaption.Foreground" Value="{StaticResource WarningColorContract}" />
            </Style>
        </pu:MessageBoxXSettings.WindowXStyle>
        <pu:MessageBoxXSettings.ButtonStyle>
            <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:MessageBoxX.ButtonStyleKey}}">
                <Style.Triggers>
                    <Trigger Property="Tag" Value="OK">
                        <Setter Property="Foreground" Value="{StaticResource WarningColorContract}" />
                        <Setter Property="Background" Value="{StaticResource WarningColor}" />
                    </Trigger>
                    <Trigger Property="Tag" Value="Cancel">
                        <Setter Property="Background" Value="{x:Null}" />
                        <Setter Property="Foreground" Value="Gray" />
                        <Setter Property="Cursor" Value="Hand" />
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <TextBlock Text="{Binding}" TextDecorations="Underline" />
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </pu:MessageBoxXSettings.ButtonStyle>
    </pu:MessageBoxXSettings>
    <pu:MessageBoxXSettings x:Key="MsgBoxError"
                             InteropOwnersMask="True"
                             InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
                <Setter Property="pu:WindowXCaption.Background" Value="{StaticResource DangerColor}" />
                <Setter Property="pu:WindowXCaption.Foreground" Value="{StaticResource DangerColorContrast}" />
            </Style>
        </pu:MessageBoxXSettings.WindowXStyle>
        <pu:MessageBoxXSettings.ButtonStyle>
            <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:MessageBoxX.ButtonStyleKey}}">
                <Style.Triggers>
                    <Trigger Property="Tag" Value="OK">
                        <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}" />
                        <Setter Property="Background" Value="{StaticResource DangerColor}" />
                    </Trigger>
                    <Trigger Property="Tag" Value="Cancel">
                        <Setter Property="Background" Value="{x:Null}" />
                        <Setter Property="Foreground" Value="Gray" />
                        <Setter Property="Cursor" Value="Hand" />
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <TextBlock Text="{Binding}" TextDecorations="Underline" />
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </pu:MessageBoxXSettings.ButtonStyle>
    </pu:MessageBoxXSettings>
    <pu:MessageBoxXSettings x:Key="MsgBoxInfo"
                             InteropOwnersMask="True"
                             InverseButtonsSequence="False">
        <pu:MessageBoxXSettings.WindowXStyle>
            <Style TargetType="pu:WindowX" BasedOn="{StaticResource {x:Static pu:MessageBoxX.WindowXStyleKey}}">
                <Setter Property="pu:WindowXCaption.Background" Value="{StaticResource PrimaryColor}" />
                <Setter Property="pu:WindowXCaption.Foreground" Value="{StaticResource PrimaryColorContract}" />
            </Style>
        </pu:MessageBoxXSettings.WindowXStyle>
        <pu:MessageBoxXSettings.ButtonStyle>
            <Style TargetType="Button" BasedOn="{StaticResource {x:Static pu:MessageBoxX.ButtonStyleKey}}">
                <Style.Triggers>
                    <Trigger Property="Tag" Value="OK">
                        <Setter Property="Foreground" Value="{StaticResource PrimaryColorContract}" />
                        <Setter Property="Background" Value="{StaticResource PrimaryColor}" />
                    </Trigger>
                    <Trigger Property="Tag" Value="Cancel">
                        <Setter Property="Background" Value="{x:Null}" />
                        <Setter Property="Foreground" Value="Gray" />
                        <Setter Property="Cursor" Value="Hand" />
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <TextBlock Text="{Binding}" TextDecorations="Underline" />
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </pu:MessageBoxXSettings.ButtonStyle>
    </pu:MessageBoxXSettings>
    <pu:ToastSettings x:Key="toastSuccess" Spacing="25">
        <pu:ToastSettings.LabelStyle>
            <Style TargetType="Label" BasedOn="{StaticResource {x:Static pu:Toast.LabelStyleKey}}">
                <Setter Property="Background" Value="{StaticResource SuccessColor}" />
                <Setter Property="Foreground" Value="{StaticResource SuccessColorContrast}" />
            </Style>
        </pu:ToastSettings.LabelStyle>
    </pu:ToastSettings>
    <pu:ToastSettings x:Key="toastWarning" Spacing="25">
        <pu:ToastSettings.LabelStyle>
            <Style TargetType="Label" BasedOn="{StaticResource {x:Static pu:Toast.LabelStyleKey}}">
                <Setter Property="Background" Value="{StaticResource WarningColor}" />
                <Setter Property="Foreground" Value="{StaticResource SuccessColorContrast}" />
            </Style>
        </pu:ToastSettings.LabelStyle>
    </pu:ToastSettings>
</ResourceDictionary>
HMI.UI/Style/PanuonResource.xaml
New file
@@ -0,0 +1,133 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ui="clr-namespace:HMI.UI"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:pw="https://opensource.panuon.com/wpf"
                    xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI;assembly=HMI.UI">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
        <pu:StyleDictionary Includes="TextBox,ComboBox,Button,DataGrid,TreeView" />
        <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <sys:Double x:Key="TitleFontSize">20</sys:Double>
    <sys:Double x:Key="ContentFontSize">16</sys:Double>
    <Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource {x:Static pu:StyleKeys.ButtonStyle}}">
        <Setter Property="Background" Value="{StaticResource WindowBar}"/>
        <Setter Property="Foreground" Value="#FFF"/>
        <Setter Property="pu:IconHelper.FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="pu:IconHelper.Margin" Value="0,0,10,0"/>
        <Setter Property="Padding" Value="10"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Cursor" Value="Hand"/>
    </Style>
    <Style x:Key="CommonButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
        <Setter Property="Background" Value="#FFF"/>
        <Setter Property="Foreground" Value="Black"/>
        <Setter Property="BorderBrush" Value="LightGray"/>
    </Style>
    <Style x:Key="DangerButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
        <Setter Property="Background" Value="{StaticResource DangerColor}"/>
        <Setter Property="Foreground" Value="{StaticResource DangerColorContrast}"/>
    </Style>
    <Style x:Key="LinkButton" TargetType="Button" BasedOn="{StaticResource {x:Static pu:StyleKeys.ButtonStyle}}">
        <Setter Property="Background" Value="{x:Null}" />
        <Setter Property="Foreground" Value="{StaticResource PrimaryColor}" />
        <Setter Property="Cursor" Value="Hand" />
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock Text="{Binding}" TextDecorations="Underline" />
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="DangerLinkButton" TargetType="Button" BasedOn="{StaticResource LinkButton}">
        <Setter Property="Foreground" Value="{StaticResource DangerColor}"/>
    </Style>
    <Style x:Key="CardBoarder" TargetType="Border">
        <Setter Property="BorderBrush" Value="{StaticResource CommonBoarder}"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Background" Value="#FFF"/>
        <Setter Property="Margin" Value="10"/>
        <Setter Property="Padding" Value="10"/>
    </Style>
    <Style x:Key="DefaultDataGrid" TargetType="DataGrid" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridStyle}}">
        <Style.Resources>
            <Style TargetType="DataGridCell"
                   BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="VerticalAlignment" Value="Stretch"/>
                <Setter Property="Background" Value="Transparent"/>
            </Style>
            <Style TargetType="TextBlock">
                <Setter Property="VerticalAlignment" Value="Center"/>
            </Style>
        </Style.Resources>
        <Setter Property="pu:DataGridHelper.ColumnHeaderBackground" Value="{StaticResource TableHeaderBackground}"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderHoverBackground" Value="{StaticResource TableHeaderBackground}"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderSeparatorBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderSeparatorVisibility" Value="Visible"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderVerticalContentAlignment" Value="Center"/>
        <Setter Property="pu:DataGridHelper.RowHeaderBackground" Value="#ffffff"/>
        <Setter Property="pu:DataGridHelper.RowHeaderSeparatorBrush" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowHoverForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="pu:DataGridHelper.RowHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowSelectedBackground" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:DataGridHelper.RowSelectedForeground" Value="#ffffff"/>
        <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="VerticalGridLinesBrush" Value="{StaticResource TableBoarder}"/>
        <Setter Property="BorderBrush" Value="{StaticResource TableBoarder}" />
        <Setter Property="AutoGenerateColumns" Value="False"/>
        <Setter Property="CanUserAddRows" Value="False"/>
        <Setter Property="CanUserSortColumns" Value="False"/>
        <Setter Property="CanUserDeleteRows" Value="False"/>
        <Setter Property="RowHeaderWidth" Value="0"/>
        <Setter Property="IsReadOnly" Value="True"/>
        <Setter Property="GridLinesVisibility" Value="All"/>
        <Setter Property="BorderThickness" Value="1,1,0,0"/>
        <Setter Property="FontSize" Value="{StaticResource ContentFontSize}"/>
        <Setter Property="RowHeight" Value="24"/>
        <Setter Property="ForceCursor" Value="True"/>
        <Setter Property="SelectionMode" Value="Single"/>
    </Style>
    <Style x:Key="DefaultTreeView" TargetType="TreeView" BasedOn="{StaticResource {x:Static pu:StyleKeys.TreeViewStyle}}">
        <Setter Property="pu:TreeViewHelper.ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
        <Setter Property="pu:TreeViewHelper.ItemsHoverForeground" Value="{StaticResource SecondPrimaryColorContract}"/>
        <Setter Property="pu:TreeViewHelper.ItemsSelectedBackground" Value="{StaticResource PrimaryColor}"/>
        <Setter Property="pu:TreeViewHelper.ItemsSelectedForeground" Value="{StaticResource PrimaryColorContract}"/>
        <Setter Property="pu:TreeViewHelper.ItemsMargin" Value="0"/>
        <Setter Property="pu:TreeViewHelper.ItemsPadding" Value="7,5,7,5"/>
        <Setter Property="dp:TreeViewHelper.AutoExpand" Value="True"/>
        <Setter Property="FontSize" Value="{StaticResource ContentFontSize}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="0"/>
    </Style>
    <Style x:Key="RowLink" TargetType="Button" BasedOn="{StaticResource LinkButton}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=IsMouseOver,
                RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
         Value="True">
                <Setter Property="Foreground" Value="#FFF"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=IsSelected,
       RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"
Value="True">
                <Setter Property="Foreground" Value="#FFF"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>
HMI.UI/Style/WindowResources.xaml
File was renamed from WPFProject/Style/WindowResources.xaml
@@ -1,15 +1,15 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ctls="clr-namespace:WPFProject.Controls"
                    xmlns:ui="clr-namespace:HMI.UI"
                    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/WPFProject;component/Style/Colors.xaml"/>
        <ResourceDictionary Source="/WPFProject;component/Resources/Font/Font.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="WindowButton" TargetType="Button">
        <Setter Property="Background" Value="{StaticResource MainBorder}"/>
        <Setter Property="ctls:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="FontWeight" Value="SemiBold"/>
        <Setter Property="Foreground" Value="White"/>
@@ -17,7 +17,8 @@
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
        
        <Setter Property="Template">
            <Setter.Value>
@@ -33,7 +34,7 @@
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="border" Property="Background" Value="{Binding 
                                Path=(ctls:DpPlugin.HoverColor),RelativeSource={RelativeSource TemplatedParent}}"/>
                                Path=(ui:DpPlugin.HoverColor),RelativeSource={RelativeSource TemplatedParent}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
@@ -42,22 +43,22 @@
    </Style>
    <Style x:Key="WindowCloseButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
        <Setter Property="ctls:DpPlugin.HoverColor" Value="Red"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="Red"/>
    </Style>
    <Style x:Key="ChildWindowButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
        <Setter Property="FontSize" Value="15"/>
        <Setter Property="Width" Value="30"/>
        <Setter Property="Background" Value="{StaticResource ChildBorder}"/>
        <Setter Property="ctls:DpPlugin.HoverColor" Value="{StaticResource MainBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource MainBorder}"/>
    </Style>
    <Style x:Key="ChildWindowCloseButton" BasedOn="{StaticResource ChildWindowButton}" TargetType="Button">
        <Setter Property="ctls:DpPlugin.HoverColor" Value="Red"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="Red"/>
    </Style>
    <Style x:Key="LinkButton" TargetType="Button">
        <Setter Property="ctls:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="ui:DpPlugin.HoverColor" Value="{StaticResource ChildBorder}"/>
        <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
        <Setter Property="Foreground" Value="{StaticResource ChildBorder}"/>
@@ -71,12 +72,11 @@
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="label" Property="Foreground" Value="{Binding 
                                Path=(ctls:DpPlugin.HoverColor),RelativeSource={RelativeSource TemplatedParent}}"/>
                                Path=(ui:DpPlugin.HoverColor),RelativeSource={RelativeSource TemplatedParent}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
HMI.UI/Util/IValueValidator.cs
New file
@@ -0,0 +1,7 @@
namespace HMI.UI.Util
{
    public interface IValueValidator
    {
        bool Validate(object input);
    }
}
HMI.UI/Util/MsgBoxHelper.cs
New file
@@ -0,0 +1,48 @@
using Panuon.WPF.UI;
using System.Windows;
using Panuon.WPF.UI.Configurations;
namespace HMI.UI.Util
{
    public class MsgBoxHelper
    {
        #region Warn
        public static MessageBoxResult WarnMessage(Window owner,
            string message,
            string caption = "警告",
            MessageBoxButton button = MessageBoxButton.OKCancel,
            MessageBoxIcon icon = MessageBoxIcon.Warning,
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxWarn") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
        #region Error
        public static MessageBoxResult ErrorMessage(Window owner,
            string message,
            string caption = "错误",
            MessageBoxButton button = MessageBoxButton.OKCancel,
            MessageBoxIcon icon = MessageBoxIcon.Error,
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxError") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
        #region Info
        public static MessageBoxResult InfoMessage(Window owner,
            string message,
            string caption = "提示",
            MessageBoxButton button = MessageBoxButton.OKCancel,
            MessageBoxIcon icon = MessageBoxIcon.Info,
            DefaultButton defaultButton = DefaultButton.Unset, int? countdown = null)
        {
            var setting = Application.Current.FindResource("MsgBoxInfo") as MessageBoxXSetting;
            return MessageBoxX.Show(owner, message, caption, button, icon, defaultButton, setting, countdown);
        }
        #endregion
    }
}
HMI.UI/Util/ToastHelper.cs
New file
@@ -0,0 +1,48 @@
using Panuon.WPF.UI;
using System.Windows;
namespace HMI.UI.Util
{
    public interface ToastHandler
    {
        void Success(Window window,
                string message,
                MessageBoxIcon icon,
                ToastPosition position, double offset, int durationMs,
                ToastWindow targetWindow);
        void Warning(Window window,
                string message,
                MessageBoxIcon icon,
                ToastPosition position, double offset, int durationMs,
                ToastWindow targetWindow);
    }
    public class ToastHelper
    {
        private static ToastHandler Instance;
        public static void Register(ToastHandler helper)
        {
            if (Instance == null) { Instance = helper; }
        }
        public static void Success(Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Success,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            Instance.Success(window, message, icon, position, offset, durationMs, targetWindow);
        }
        public static void Warning(Window window,
            string message,
            MessageBoxIcon icon = MessageBoxIcon.Warning,
            ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
            ToastWindow targetWindow = ToastWindow.ActiveWindow)
        {
            Instance.Warning(window, message, icon, position, offset, durationMs, targetWindow);
        }
    }
}
HMI.UI/packages.config
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
HMI.UnitTest/App.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
</configuration>
HMI.UnitTest/App.xaml
New file
@@ -0,0 +1,16 @@
<Application x:Class="UnitTest.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:UnitTest"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Style/FormStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="{x:Type pu:FormGroup}" BasedOn="{StaticResource InputFormGroupStyle}"></Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>
HMI.UnitTest/App.xaml.cs
New file
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace UnitTest
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : Application
    {
    }
}
HMI.UnitTest/HMI.UnitTest.csproj
New file
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{325A7F52-FE51-44F0-B3AC-5F42F5356605}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <RootNamespace>HMI.UnitTest</RootNamespace>
    <AssemblyName>HMI.UnitTest</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <ApplicationDefinition Include="App.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </ApplicationDefinition>
    <Page Include="MainWindow.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="App.xaml.cs">
      <DependentUpon>App.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="MainWindow.xaml.cs">
      <DependentUpon>MainWindow.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.SettingControl\HMI.SettingControl.csproj">
      <Project>{3dd748ac-9708-49ad-b846-659214035588}</Project>
      <Name>HMI.SettingControl</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
HMI.UnitTest/MainWindow.xaml
New file
@@ -0,0 +1,12 @@
<Window x:Class="UnitTest.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:UnitTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid x:Name="page">
    </Grid>
</Window>
HMI.UnitTest/MainWindow.xaml.cs
New file
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using HMI.SettingControl;
namespace UnitTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            page.Children.Clear();
            page.Children.Add(new SettingControl());
        }
    }
}
HMI.UnitTest/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("UnitTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("UnitTest")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                                     //(未在页面中找到资源时使用,
                                     //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                              //(未在页面中找到资源时使用,
                                              //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
HMI.UnitTest/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.UnitTest.Properties {
    using System;
    /// <summary>
    ///   一个强类型的资源类,用于查找本地化的字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存的 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMI.UnitTest.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
HMI.UnitTest/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
HMI.UnitTest/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace HMI.UnitTest.Properties {
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
    }
}
HMI.UnitTest/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
HMI.UnitTest/packages.config
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
</packages>
Libs/DatabaseHelper.dll
Binary files differ
Libs/SQLite.Interop.dll
Binary files differ
Libs/System.Data.SQLite.EF6.dll
Binary files differ
Libs/System.Data.SQLite.Linq.dll
Binary files differ
Libs/System.Data.SQLite.dll
Binary files differ
Libs/System.Data.SQLite.dll.config
New file
@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!--
 *
 * System.Data.SQLite.dll.config -
 *
 * WARNING: This XML configuration file is intended for use within the NuGet
 *          package directories only.  Please do not copy this file to any of
 *          the build output directories because it could interfere with the
 *          native library pre-loading feature.
 *
 * Written by Joe Mistachkin.
 * Released to the public domain, use at your own risk!
 *
-->
<configuration>
  <appSettings>
    <add key="PreLoadSQLite_BaseDirectory"
         value="%PreLoadSQLite_AssemblyDirectory%\..\..\build\%PreLoadSQLite_TargetFramework%" />
  </appSettings>
  <dllmap dll="SQLite.Interop" target="SQLite.Interop.dll" />
</configuration>
Libs/System.Data.SQLite.xml
New file
Diff too large
VisionBuilder.Core/VisionBuilder.Core.csproj
File was deleted
VisionBuilder.Engine/VisionBuilder.Engine.csproj
@@ -30,6 +30,24 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
@@ -45,9 +63,9 @@
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\VisionBuilder.Core\VisionBuilder.Core.csproj">
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>VisionBuilder.Core</Name>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
VisionBuilder.Engine/app.config
New file
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
VisionBuilder.Run/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,52 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VisionBuilder.Run")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Organization")]
[assembly: AssemblyProduct("VisionBuilder.Run")]
[assembly: AssemblyCopyright("Copyright © Organization 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//在 <PropertyGroup> 中。例如,如果你使用的是美国英语。
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                             //(未在页面中找到资源时使用,
                             //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                      //(未在页面中找到资源时使用,
                                      //、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
VisionBuilder.Run/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace VisionBuilder.Run.Properties {
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if ((resourceMan == null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VisionBuilder.Run.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性,对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
    }
}
VisionBuilder.Run/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
VisionBuilder.Run/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace VisionBuilder.Run.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
VisionBuilder.Run/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
VisionBuilder.Run/VisionBuilder.Run.csproj
New file
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{7C299AED-88CC-437E-A68B-1550AA72E6B0}</ProjectGuid>
    <OutputType>library</OutputType>
    <RootNamespace>VisionBuilder.Run</RootNamespace>
    <AssemblyName>VisionBuilder.Run</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
VisionBuilder.Test/PluginOne.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using VisionBuilder.Core.Api;
using VisionBuilder.Core.Entity;
using HMI.Core.Api;
using HMI.Core.Entity;
namespace VisionBuilder.Test
{
VisionBuilder.Test/VisionBuilder.Test.csproj
@@ -30,6 +30,24 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
@@ -45,9 +63,9 @@
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\VisionBuilder.Core\VisionBuilder.Core.csproj">
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>VisionBuilder.Core</Name>
      <Name>HMI.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
VisionBuilder.Test/app.config
New file
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
WPFProject/App.config
@@ -5,7 +5,19 @@
    </startup>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="Libs;ThirdParty"/>
            <probing privatePath="Libs;ThirdParty;Plugin;" />
            <dependentAssembly>
                <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>
WPFProject/App.xaml
@@ -2,8 +2,24 @@
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WPFProject"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             Startup="Application_Startup">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Style/BaseWindow.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/FormStyle.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/PanuonResource.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/MsgBoxStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
         
            <Style TargetType="{x:Type pu:FormGroup}" BasedOn="{StaticResource InputFormGroupStyle}"></Style>
            <uic:PathImageConverter x:Key="path2Image"/>
            <uic:DataGridRowNumberConverter x:Key="rowNumber"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>
WPFProject/App.xaml.cs
@@ -5,6 +5,10 @@
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using HMI.UI.Util;
using System.Windows.Markup;
using log4net;
using Panuon.WPF.UI;
namespace WPFProject
{
@@ -13,14 +17,26 @@
    /// </summary>
    public partial class App : Application
    {
        public void test()
        private ILog log;
        public App()
        {
            log = LogManager.GetLogger("APP");
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }
           
        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            log.Error("未知异常", e.ExceptionObject as Exception);
            Dispatcher.Invoke(() =>
            {
                MsgBoxHelper.ErrorMessage(Current.MainWindow, $"发生未知异常!程序即将关闭", "错误", MessageBoxButton.OK,
                    MessageBoxIcon.Error, DefaultButton.YesOK, 5);
            });
        }
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            MainWindow window = new MainWindow();
            LoadingWindow window = new LoadingWindow();
            window.Show();
        }
    }
WPFProject/Controls/IconTabItem.cs
@@ -1,8 +1,8 @@
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WPFProject.Controls
@@ -37,6 +37,13 @@
            set => _isFont = value;
        }
        private string _Header;
        public string Header
        {
            get => _Header;
            set => _Header = value;
        }
        private Window _Body;
        public Window Body
        {
@@ -44,6 +51,13 @@
            set => _Body = value;
        }
        private ObservableCollection<IconTabItem> _Children;
        public ObservableCollection<IconTabItem> Children
        {
            get => _Children;
            set => RaiseAndSetIfChanged(ref _Children, value);
        }
        public BitmapImage Icon
        {
            get { return (BitmapImage)GetValue(IconProperty); }
WPFProject/Controls/InnerWindow.xaml
@@ -14,8 +14,8 @@
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/WindowResources.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style x:Key="SliderThumbStyle" TargetType="{x:Type Thumb}">
WPFProject/Controls/MainTabControl.xaml
New file
@@ -0,0 +1,113 @@
<UserControl x:Class="WPFProject.Controls.MainTabControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:WPFProject.Controls"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="ListBox" x:Key="ListBoxItemStyle1">
                <Setter Property="Background" Value="{StaticResource MainMenu}"/>
                <Setter Property="ItemContainerStyle">
                    <Setter.Value>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="Margin" Value="0"/>
                            <Setter Property="Padding" Value="0"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ListBoxItem">
                                        <Border x:Name="border" Background="{StaticResource MainMenu}"
                                                Padding="10" Margin="0" BorderThickness="0,0,1,0" BorderBrush="Gray">
                                            <!-- 内容呈现器 -->
                                            <StackPanel>
                                                <ContentPresenter  Content="{TemplateBinding Content}"
                                                                   ContentStringFormat="{TemplateBinding ContentStringFormat}"
                                                                   ContentTemplate="{TemplateBinding ContentTemplate}"  />
                                            </StackPanel>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsSelected" Value="True">
                                                <Setter TargetName="border" Property="Background" Value="{StaticResource ButtonSelected}" />
                                            </Trigger>
                                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=(ItemsControl.AlternationIndex)}" Value="0">
                                                <Setter TargetName="border" Property="BorderThickness" Value="1,0,1,0" />
                                            </DataTrigger>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter TargetName="border" Property="Background" Value="Red" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemsPanel">
                    <Setter.Value>
                        <ItemsPanelTemplate>
                            <StackPanel HorizontalAlignment="Left" Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
                                <Image Width="20" Height="20" Margin="5" Source="{Binding Icon}"/>
                                <Label x:Name="txt" Visibility="Visible" BorderThickness="0"
                                       Foreground="{StaticResource PrimaryColorContract}"
                                       FontWeight="Bold"
                                       FontSize="16"
                                       VerticalContentAlignment="Center"
                                       HorizontalAlignment="Left"
                                       Content="{Binding Name}" ToolTip="{Binding Name}"/>
                            </StackPanel>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid x:Name="mainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" x:Name="Menu" Margin="0" Padding="-1"
                 Style="{StaticResource ListBoxItemStyle1}"
                 BorderBrush="Gray"
                 BorderThickness="0,1,0,1"
                 Visibility="{Binding MenuVisibility}"
                 SelectionChanged="Menu_SelectionChanged"
                 SelectedItem="{Binding SelectedMenu}"
                 AlternationCount="{Binding MenuItemsCount}" ItemsSource="{Binding MenuItems}">
        </ListBox>
        <ListBox Grid.Row="1" x:Name="SubMenu" Margin="0" Padding="-1"
         Style="{StaticResource ListBoxItemStyle1}"
         BorderBrush="Gray"
                 Visibility="{Binding MenuVisibility}"
                 SelectedItem="{Binding SelectedSubMenu}" AlternationCount="{Binding SubMenuItemsCount}"
                 SelectionChanged="SubMenu_SelectionChanged"
BorderThickness="0,0,0,1" ItemsSource="{Binding SubMenuItems}">
        </ListBox>
        <Border x:Name="page"  Grid.Row="2" BorderThickness="0" Margin="0" Padding="0">
        </Border>
    </Grid>
</UserControl>
WPFProject/Controls/MainTabControl.xaml.cs
New file
@@ -0,0 +1,156 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using HMI.Base;
using HMI.Core.Api;
using WPFProject.UI;
namespace WPFProject.Controls
{
    /// <summary>
    /// MainTabControl.xaml 的交互逻辑
    /// </summary>
    public partial class MainTabControl : UserControl, INotifyPropertyChanged
    {
        #region ...
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        public MainTabControl()
        {
            IPlugin plugin = new WorkControl();
            MenuItems = PluginLoader.GetLoader().Menus;
            SubMenuItems = MenuItems[0].Children;
            this.DataContext = this;
            InitializeComponent();
        }
        // Tab切换事件
        public static readonly RoutedEvent OnTabChangeEvent =
             EventManager.RegisterRoutedEvent("OnTabChange", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuTabControl));
        public event RoutedEventHandler OnTabChange
        {
            add
            {
                AddHandler(OnTabChangeEvent, value);
            }
            remove
            {
                RemoveHandler(OnTabChangeEvent, value);
            }
        }
        private ObservableCollection<PluginInfo> _MenuItems;
        public ObservableCollection<PluginInfo> MenuItems
        {
            get { return _MenuItems; }
            set
            {
                RaiseAndSetIfChanged(ref _MenuItems, value);
                RaisePropertyChanged("MenuItemsCount");
            }
        }
        public int MenuItemsCount
        {
            get => MenuItems == null ? 0 : MenuItems.Count;
        }
        private PluginInfo _SelectedMenu;
        public PluginInfo SelectedMenu
        {
            get { return _SelectedMenu; }
            set
            {
                RaiseAndSetIfChanged(ref _SelectedMenu, value);
            }
        }
        private ObservableCollection<PluginInfo> _SubMenuItems;
        public ObservableCollection<PluginInfo> SubMenuItems
        {
            get { return _SubMenuItems; }
            set
            {
                RaiseAndSetIfChanged(ref _SubMenuItems, value);
                RaisePropertyChanged("SubMenuItemsCount");
            }
        }
        public int SubMenuItemsCount
        {
            get => MenuItems == null ? 0 : MenuItems.Count;
        }
        private PluginInfo _SelectedSubMenu;
        public PluginInfo SelectedSubMenu
        {
            get { return _SelectedSubMenu; }
            set => RaiseAndSetIfChanged(ref _SelectedSubMenu, value);
        }
        private Visibility _MenuVisibility = Visibility.Collapsed;
        public Visibility MenuVisibility
        {
            get => _MenuVisibility;
            set { RaiseAndSetIfChanged(ref _MenuVisibility, value); }
        }
        public void ChangeItemVisibility()
        {
            MenuVisibility = MenuVisibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
        }
        private void Menu_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems?.Count > 0)
            {
                var data = (PluginInfo)e.AddedItems[0];
                SubMenuItems = data?.Children;
            }
        }
        private void SwithMenu(PluginInfo menu)
        {
        }
        private void SubMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems?.Count > 0)
            {
                var data = (PluginInfo)e.AddedItems[0];
                var content = (UIElement)data.Content;
                var last = (IPlugin)page.Child;
                last?.DeActive();
                page.Child = content;
                ((IPlugin)content)?.Active();
            }
        }
    }
}
WPFProject/Controls/MenuTabControl.xaml
@@ -11,7 +11,7 @@
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style x:Key="tabItemStyle" TargetType="{x:Type ctl:IconTabItem}">
@@ -58,6 +58,88 @@
                </Setter>
            </Style>
            <Style x:Key="SubControlStyle" TargetType="{x:Type TabControl}">
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
                <Setter Property="ItemContainerStyle" Value="{StaticResource tabItemStyle}"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TabControl}">
                            <Grid x:Name="templateRoot" ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition x:Name="ColumnDefinition0" />
                                    <ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
                                    <RowDefinition x:Name="RowDefinition1" Height="*"/>
                                </Grid.RowDefinitions>
                                <!--<UniformGrid x:Name="HeaderPanel" Rows="1" Background="Transparent" Grid.Column="0" IsItemsHost="True" Margin="0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>-->
                                <ScrollViewer Visibility="{Binding ShowMenu}" Grid.Row="0" Grid.Column="0" VerticalScrollBarVisibility="Auto"
                                  HorizontalScrollBarVisibility="Auto" >
                                    <StackPanel Orientation="Horizontal" x:Name="HeaderPanel"
                                ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                IsItemsHost="True"
                                Margin="0" KeyboardNavigation.TabIndex="1"
                                Background="{StaticResource MainMenu}"
                                Panel.ZIndex="1"/>
                                </ScrollViewer>
                                <!--<Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
                      Stroke="Transparent" StrokeThickness="0.1"
                      VerticalAlignment="Bottom" Margin="0" SnapsToDevicePixels="True"/>-->
                                <Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Background="{TemplateBinding Background}"
                        Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained"
                        Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
                                    <ContentPresenter x:Name="PART_SelectedContentHost"
                            ContentTemplate="{TemplateBinding SelectedContentTemplate}"
                            Content="{TemplateBinding SelectedContent}"
                            ContentStringFormat="{TemplateBinding SelectedContentStringFormat}"
                            ContentSource="SelectedContent" Margin="0"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Border>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="TabStripPlacement" Value="Bottom">
                                    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
                                    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
                                    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                                    <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
                                </Trigger>
                                <Trigger Property="TabStripPlacement" Value="Left">
                                    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
                                    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
                                    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
                                    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
                                    <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
                                    <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
                                    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                                    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                                </Trigger>
                                <Trigger Property="TabStripPlacement" Value="Right">
                                    <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
                                    <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
                                    <Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
                                    <Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
                                    <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
                                    <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
                                    <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                                    <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style x:Key="ControlStyle" TargetType="{x:Type TabControl}">
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
@@ -94,12 +176,21 @@
                                    Background="{TemplateBinding Background}" 
                                    Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" 
                                    Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
                                    <ContentPresenter x:Name="PART_SelectedContentHost"
                                    <TabControl x:Name="contentTab"
                                                Style="{StaticResource SubControlStyle}"
                                                ItemsSource="{Binding TabSelectedItem.Children,Mode=TwoWay}">
                                    </TabControl>
                                    <!--<ContentPresenter x:Name="PART_SelectedContentHost"
                                                  ContentTemplate="{TemplateBinding SelectedContentTemplate}" 
                                                  Content="{TemplateBinding SelectedContent}" 
                                                  ContentStringFormat="{TemplateBinding SelectedContentStringFormat}" 
                                                  ContentSource="SelectedContent" Margin="0" 
                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>-->
                                </Border>
                            </Grid>
                            <ControlTemplate.Triggers>
@@ -142,10 +233,12 @@
    <Grid>
        <TabControl x:Name="tabLevel_1" 
                    Grid.Row="0"
                    Style="{StaticResource ControlStyle}" ItemsSource="{Binding Items}" 
                    SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
                    SelectedItem="{Binding TabSelectedItem,Mode=TwoWay}"
                    SelectionChanged="tabLevel_1_SelectionChanged">
        </TabControl>
    </Grid>
</UserControl>
WPFProject/Controls/MenuTabControl.xaml.cs
@@ -4,9 +4,8 @@
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using WPFProject.Events;
using HMI.Core.Api;
using WPFProject.UI;
using WPFProject.Utils;
namespace WPFProject.Controls
{
@@ -70,12 +69,20 @@
            set => RaiseAndSetIfChanged(ref _Items, value);
        }
        private IconTabItem _SelectedItem;
        private ObservableCollection<IconTabItem> _Items_Level2;
        public IconTabItem SelectedItem
        public ObservableCollection<IconTabItem> Items_Level2
        {
            get { return _SelectedItem; }
            set => RaiseAndSetIfChanged(ref _SelectedItem, value);
            get { return _Items_Level2; }
            set => RaiseAndSetIfChanged(ref _Items_Level2, value);
        }
        private IconTabItem _TabSelectedItem;
        public IconTabItem TabSelectedItem
        {
            get { return _TabSelectedItem; }
            set => RaiseAndSetIfChanged(ref _TabSelectedItem, value);
        }
@@ -99,13 +106,18 @@
            InitializeComponent();
            Items = new ObservableCollection<IconTabItem> {
            new IconTabItem(){
                Header="123",
                Header="111",
                Children = new ObservableCollection<IconTabItem>{
                    new IconTabItem(){
                        Header = "112",
                Content = plugin
                    }
                }
            },
            new IconTabItem(){
                Header="321"
            },
                Header="222",
                Children = new ObservableCollection<IconTabItem>()
            }
            };
        }
@@ -144,6 +156,11 @@
            foreach (IconTabItem item in Items)
            {
                item.Visibility = ItemVisibility;
                if (item.Children != null) {
                    foreach (var child in item.Children) {
                        child.Visibility = ItemVisibility;
                    }
                }
            }
        }
    }
WPFProject/Controls/MinInnerWindow.xaml
@@ -10,8 +10,8 @@
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/WindowResources.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
WPFProject/Entity/PluginLoadMessage.cs
New file
@@ -0,0 +1,18 @@
namespace WPFProject.Entity
{
    public enum PluginLoadMessageType
    {
        Message, Completed, Error
    }
    public class PluginLoadMessage
    {
        public PluginLoadMessageType Type { get; set; }
        public string Message { get; set; }
        public int Total { get; set; }
        public int Completed { get; set; }
        public int Error { get; set; }
        public bool Success { get; set; }
        public PluginLoadMessage() { }
    }
}
WPFProject/Entity/PluginSettingInfo.cs
New file
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPFProject.Entity
{
    public class PluginSettingInfo
    {
        public string Name { get; set; }
        public object View { get; set; }
        public object Setting { get; set; }
    }
}
WPFProject/Icon/xinzeng_1.png
WPFProject/LoadingWindow.xaml
New file
@@ -0,0 +1,20 @@
<Window x:Class="WPFProject.LoadingWindow"
        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:WPFProject"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen"
        WindowStyle="None"
        Loaded="Window_Loaded"
        Title="LoadingWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBox x:Name="Console" IsReadOnly="True"/>
        <Button Grid.Row="1" Content="下一步" Click="Button_Click"/>
    </Grid>
</Window>
WPFProject/LoadingWindow.xaml.cs
New file
@@ -0,0 +1,36 @@
using System.Windows;
using HMI.Base;
namespace WPFProject
{
    /// <summary>
    /// LoadingWindow.xaml 的交互逻辑
    /// </summary>
    public partial class LoadingWindow : Window
    {
        public LoadingWindow()
        {
            InitializeComponent();
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            PluginLoader loader = PluginLoader.GetLoader();
            loader.LoadMessageAppend += Loader_LoadMessageAppend;
            loader.LoadMenuAndPlugin();
            Button_Click(null,null);
        }
        private void Loader_LoadMessageAppend(object sender, string e)
        {
            Console.Text += $"{e}\r\n";
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            PanuonWindow window = new PanuonWindow();
            window.Show();
            this.Close();
        }
    }
}
WPFProject/MainWindow.xaml
@@ -17,8 +17,8 @@
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/WindowResources.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
@@ -39,15 +39,19 @@
            <StackPanel Grid.Column="0"></StackPanel>
            <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Style="{StaticResource WindowButton}" x:Name="openCloseButton"  Click="Button_Click_2">&#xe60e;</Button>
                <Button Style="{StaticResource WindowButton}" Click="Button_Click">&#xe650;</Button>
                <Button Style="{StaticResource WindowButton}" Click="Button_Click">&#xe602;</Button>
                <Button Style="{StaticResource WindowCloseButton}" Click="Button_Click_1">&#xe609;</Button>
                <Button Style="{StaticResource WindowButton}" Click="Min_Click">&#xe650;</Button>
                <Button Style="{StaticResource WindowButton}" Click="Max_Click">&#xe602;</Button>
                <Button Style="{StaticResource WindowCloseButton}" Click="Close_Click">&#xe609;</Button>
            </StackPanel>
        </Grid>
        <ctl:MenuTabControl Grid.Row="1" x:Name="mainControl" OnTabChange="mainControl_OnTabChange">
        <ctl:MainTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange">
        </ctl:MenuTabControl>
        </ctl:MainTabControl>
        <!--<ctl:MenuTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange">
        </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="2" Background="{StaticResource MainStatusBar}">99999</StatusBar>
WPFProject/MainWindow.xaml.cs
@@ -1,8 +1,6 @@
using System;
using System.Windows;
using System.Windows.Media;
using WPFProject.Events;
using WPFProject.Utils;
namespace WPFProject
{
@@ -16,12 +14,17 @@
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        private void Min_Click(object sender, RoutedEventArgs e)
        {
            WindowState = WindowState.Minimized;
        }
        private void Max_Click(object sender, RoutedEventArgs e)
        {
            WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
        }
        private void Button_Click_1(object sender, RoutedEventArgs e)
        private void Close_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }
@@ -31,7 +34,7 @@
            // Resize.SetIsResizeable(this, true);
        }
        private void mainControl_OnTabChange(object sender, RoutedEventArgs e)
        private void menuTab_OnTabChange(object sender, RoutedEventArgs e)
        {
            
        }
@@ -43,7 +46,7 @@
            openCloseButton.RenderTransformOrigin = new Point(0.5, 0.5);
            ScaleTransform verticalFlipTransform = new ScaleTransform(1, temp*-1);
            openCloseButton.RenderTransform = verticalFlipTransform;
            mainControl.ChangeItemVisibility();
            menuTab.ChangeItemVisibility();
        }
    }
}
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/NuGetUpgradeLog.html
New file
@@ -0,0 +1,167 @@
<!DOCTYPE html>
<!-- saved from url=(0014)about:internet -->
    <html xmlns:msxsl="urn:schemas-microsoft-com:xslt"><head><meta content="en-us" http-equiv="Content-Language" /><meta content="text/html; charset=utf-16" http-equiv="Content-Type" /><title _locID="NuGetUpgradeReportTitle">
          NuGetMigrationLog
        </title><style>
                    /* Body style, for the entire document */
                    body
                    {
                        background: #F3F3F4;
                        color: #1E1E1F;
                        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
                        font-size: 12pt;
                        padding: 0;
                        margin: 0;
                    }
                    /* Header1 style, used for the main title */
                    h1
                    {
                        padding: 10px 0px 10px 10px;
                        font-size: 21pt;
                        background-color: #E2E2E2;
                        border-bottom: 1px #C1C1C2 solid;
                        color: #201F20;
                        margin: 0;
                        font-weight: normal;
                    }
                    /* Header2 style, used for "Overview" and other sections */
                    h2
                    {
                        font-size: 18pt;
                        font-weight: normal;
                        padding: 15px 0 5px 0;
                        margin: 0;
                    }
                    /* Header3 style, used for sub-sections, such as project name */
                    h3
                    {
                        font-weight: normal;
                        font-size: 15pt;
                        margin: 0;
                        padding: 15px 0 5px 0;
                        background-color: transparent;
                    }
                    .info-text
                    {
                        margin: 0px 0 0.75em 0;
                    }
                    /* Color all hyperlinks one color */
                    a
                    {
                        color: #1382CE;
                    }
                    /* Table styles */
                    table
                    {
                        border-spacing: 0 0;
                        border-collapse: collapse;
                        font-size: 11pt;
                    }
                    table th
                    {
                        background: #E7E7E8;
                        text-align: left;
                        text-decoration: none;
                        font-weight: normal;
                        padding: 3px 6px 3px 6px;
                    }
                    table td
                    {
                        vertical-align: top;
                        padding: 3px 6px 5px 5px;
                        margin: 0px;
                        border: 1px solid #E7E7E8;
                        background: #F7F7F8;
                    }
                    /* Local link is a style for hyperlinks that link to file:/// content, there are lots so color them as 'normal' text until the user mouse overs */
                    .localLink
                    {
                        color: #1E1E1F;
                        background: #EEEEED;
                        text-decoration: none;
                    }
                    .localLink:hover
                    {
                        color: #1382CE;
                        background: #FFFF99;
                        text-decoration: none;
                    }
                    .issueCell
                    {
                        width: 100%;
                    }
                    .packageIssue
                    {
                        margin-left: 25px;
                    }
                    /* Padding around the content after the h1 */
                    #content
                    {
                        padding: 0px 20px 20px 20px;
                    }
                    .issues table
                    {
                        width: 97%;
                    }
                    /* All Icons */
                    .IconSuccessEncoded, .IconInfoEncoded, .IconWarningEncoded, .IconErrorEncoded
                    {
                        min-width:18px;
                        min-height:18px;
                        background-repeat:no-repeat;
                        background-position:center;
                    }
                    .IconSuccessEncoded
                    {
                        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABcElEQVR4Xq2TsUsCURzHv15g8ZJcBWlyiYYgCIWcb9DFRRwMW5TA2c0/QEFwFkxxUQdxVlBwCYWOi6IhWgQhBLHJUCkhLr/BW8S7gvrAg+N+v8/v+x68Z8MGy+XSCyABQAXgBgHGALoASkIIDWSLeLBetdHryMjd5IxQPWT4rn1c/P7+xxp72Cs9m5SZ0Bq2vPnbPFafK2zDvmNHypdC0BPkLlQhxJsCAhQoZwdZU5mwxh720qGo8MzTxTTKZDPCx2HoVzp6lz0Q9tKhyx0kGs8Ny+TkWRKk8lCROwEduhyg9l/6lunOPSfmH3NUH6uQ0KHLAe7JYvJjevm+DAMGJHToKtigE+vwvIidxLamb8IBY9e+C5LiXREkfho3TSd06HJA13/oh6T51MTsfQbHrsMynQ5dDihFjiK8JJAU9AKIWTp76dCVN7HWHrajmUEGvyF9nkbAE6gLIS7kTUyuf2gscLoJrElZo/Mvj+nPz/kLTmfnEwP3tB0AAAAASUVORK5CYII=);
                    }
                    .IconInfoEncoded
                    {
                        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHElEQVR4Xs2TsUoDQRRF7wwoziokjZUKadInhdhukR9YP8DMX1hYW+QvdsXa/QHBbcXC7W0CamWTQnclFutceIQJwwaWNLlwm5k5d94M76mmaeCrrmsLYOocY12FcxZFUeozCqKqqgYA8uevv1H6VuPxcwlfk5N92KHBxfFeCSAxxswlYAW/Xr989x/mv9gkhtyMDhcAxgzRsp7flj8B/HF1RsMXq+NZMkopaHe7lbKxQUEIGbKsYNoGn969060hZBkQex/W8oRQwsQaW2o3Ago2SVcJUzAgY3N0lTCZZm+zPS8HB51gMmS1DEYyOz9acKO1D8JWTlafKIMxdhvlfdyT94Vv5h7P8Ky7nQzACmhvKq3zk3PjW9asz9D/1oigecsioooAAAAASUVORK5CYII=);
                    }
                    .IconWarningEncoded
                    {
                        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAx0lEQVR4XpWSMQ7CMAxFf4xAyBMLCxMrO8dhaBcuwdCJS3RJBw7SA/QGTCxdWJgiQYWKXJWKIXHIlyw5lqr34tQgEOdcBsCOx5yZK3hCCKdYXneQkh4pEfqzLfu+wVDSyyzFoJjfz9NB+pAF+eizx2Vruts0k15mPgvS6GYvpVtQhB61IB/dk6AF6fS4Ben0uIX5odtFe8Q/eW1KvFeH4e8khT6+gm5B+t3juyDt7n0jpe+CANTd+oTUjN/U3yVaABnSUjFz/gFq44JaVSCXeQAAAABJRU5ErkJggg==);
                    }
                    .IconErrorEncoded
                    {
                        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABQElEQVR4XqWTvUoEQRCE6wYPZUA80AfwAQz23uCMjA7MDRQEIzPBVEyNTQUFIw00vcQTTMzuAh/AxEQQT8HF/3G/oGGnEUGuoNnd6qoZuqltyKEsyzVJq5I6rnUp6SjGeGhESikzzlc1eL7opfuVbrqbU1Zw9NCgtQMaZpY0eNnaaL2fHusvTK5vKu7sjSS1Y4y3QUA6K3e3Mau5UFDyMP7tYF9o8cAHZv68vipoIJg971PZIZ5HiwdvYGGvFVFHmGmZ2MxwmQYPXubPl9Up0tfoMQGetXd6mRbvhBw+boZ6WF7Mbv1+GsHRk0fQmPAH1GfmZirbCfDJ61tw3Px8/8pZsPAG4jlVhcPgZ7adwNWBB68lkRQWFiTgFlbnLY3DGGM7izIJIyT/jjIvEJw6fdJTc6krDzh6aMwMP9bvDH4ADSsa9uSWVJkAAAAASUVORK5CYII=);
                    }
          </style></head><body><h1>
          NuGet Migration Report - VisionBuilder.Core</h1><div id="content"><h2 _locID="OverviewTitle">Overview</h2><div class="info-text">Migration to PackageReference was completed successfully. Please build and run your solution to verify that all packages are available.</div><div class="info-text">
            If you run into any problems, have feedback, questions, or concerns, please
            <a href="https://github.com/NuGet/Home/issues/">file an issue on the NuGet GitHub repository.</a></div><div class="info-text">
            Changed files and this report have been backed up here:
            <a href="C:\Main\Workspace\VisualStudio\WPFProject\WPFProject\MigrationBackup\fde93b81\VisionBuilder.Core">C:\Main\Workspace\VisualStudio\WPFProject\WPFProject\MigrationBackup\fde93b81\VisionBuilder.Core</a></div><div class="info-text"><a href="https://aka.ms/nuget-pc2pr-migrator-rollback">Help me rollback to packages.config</a></div><h2 _locID="PackagesTitle">Packages processed</h2><h3 _locID="IncludePackagesTitle">Top-level dependencies:</h3><div class="issues"><table><tr><th class="issueCell">Package Id</th><th>Version</th></tr><tr><td class="issueCell"><span>CommunityToolkit.Mvvm</span></td><td><span>
                v8.2.2</span></td></tr></table></div><p /><h3 _locID="IncludePackagesTitle">Transitive dependencies:</h3><div class="issues"><table><tr><th class="issueCell">Package Id</th><th>Version</th></tr><tr><td class="issueCell"><span>Microsoft.Bcl.AsyncInterfaces</span></td><td><span>
                v7.0.0</span></td></tr><tr><td class="issueCell"><span>System.Buffers</span></td><td><span>
                v4.5.1</span></td></tr><tr><td class="issueCell"><span>System.ComponentModel.Annotations</span></td><td><span>
                v5.0.0</span></td></tr><tr><td class="issueCell"><span>System.Memory</span></td><td><span>
                v4.5.5</span></td></tr><tr><td class="issueCell"><span>System.Numerics.Vectors</span></td><td><span>
                v4.5.0</span></td></tr><tr><td class="issueCell"><span>System.Runtime.CompilerServices.Unsafe</span></td><td><span>
                v6.0.0</span></td></tr><tr><td class="issueCell"><span>System.Threading.Tasks.Extensions</span></td><td><span>
                v4.5.4</span></td></tr></table></div><h2 _locID="IssuesTitle">Package compatibility issues</h2><div class="issues"><table><tr><th /><th class="issueCell" _locID="DescriptionTableHeader">Description</th></tr><tr><td class="IconInfoEncoded" /><td class="issueCell">
                No issues were found.
              </td></tr></table></div></div></body></html>
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/VisionBuilder.Core.csproj
New file
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{3E3BF44A-0B69-45C5-9512-528CB48A7106}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>VisionBuilder.Core</RootNamespace>
    <AssemblyName>VisionBuilder.Core</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="CommunityToolkit.Mvvm, Version=8.2.0.0, Culture=neutral, PublicKeyToken=4aff67a105548ee2, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\CommunityToolkit.Mvvm.8.2.2\lib\netstandard2.0\CommunityToolkit.Mvvm.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\Microsoft.Bcl.AsyncInterfaces.7.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
    </Reference>
    <Reference Include="System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.ComponentModel.Annotations.5.0.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
    </Reference>
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Core" />
    <Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
    </Reference>
    <Reference Include="System.Numerics" />
    <Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
    </Reference>
    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
    </Reference>
    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\WPFProject\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
    </Reference>
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Api\IProgram.cs" />
    <Compile Include="Api\ISequnce.cs" />
    <Compile Include="Api\IStep.cs" />
    <Compile Include="Api\IStepRunner.cs" />
    <Compile Include="Entity\Param.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="TestModel.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="app.config" />
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\WPFProject\packages\CommunityToolkit.Mvvm.8.2.2\build\netstandard2.0\CommunityToolkit.Mvvm.targets" Condition="Exists('..\WPFProject\packages\CommunityToolkit.Mvvm.8.2.2\build\netstandard2.0\CommunityToolkit.Mvvm.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\WPFProject\packages\CommunityToolkit.Mvvm.8.2.2\build\netstandard2.0\CommunityToolkit.Mvvm.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\WPFProject\packages\CommunityToolkit.Mvvm.8.2.2\build\netstandard2.0\CommunityToolkit.Mvvm.targets'))" />
  </Target>
</Project>
WPFProject/MigrationBackup/fde93b81/VisionBuilder.Core/packages.config
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="CommunityToolkit.Mvvm" version="8.2.2" targetFramework="net472" />
  <package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net472" />
  <package id="System.Buffers" version="4.5.1" targetFramework="net472" />
  <package id="System.ComponentModel.Annotations" version="5.0.0" targetFramework="net472" />
  <package id="System.Memory" version="4.5.5" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
</packages>
WPFProject/Models/Program.cs
@@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using VisionBuilder.Core.Api;
using VisionBuilder.Core.Entity;
using HMI.Core.Api;
using HMI.Core.Entity;
namespace WPFProject.Models
{
WPFProject/Models/Sequence.cs
@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using VisionBuilder.Core.Api;
namespace WPFProject.Models
{
WPFProject/Models/Step.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
using VisionBuilder.Core.Api;
using HMI.Core.Api;
namespace WPFProject.Models
{
WPFProject/PanuonWindow.xaml
New file
@@ -0,0 +1,58 @@
<pu:WindowX x:Class="WPFProject.PanuonWindow"
            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:WPFProject"
            xmlns:pw="https://opensource.panuon.com/wpf"
            xmlns:pu="https://opensource.panuon.com/wpf-ui"
            xmlns:ctl="clr-namespace:WPFProject.Controls"
            mc:Ignorable="d"
            WindowStartupLocation="CenterOwner"
            WindowState="Maximized"
            x:Name="window"
            BorderBrush="{StaticResource MainBorder}"
            Loaded="window_Loaded"
            pu:WindowXCaption.Background="{StaticResource MainBorder}"
            pu:WindowXCaption.Foreground="#FFF" d:DesignHeight="115.146" d:DesignWidth="341.223">
    <pu:WindowXCaption.HeaderTemplate>
        <DataTemplate>
            <StackPanel DataContext="{Binding ElementName=window}" Orientation="Horizontal"
                    Margin="5,0,0,0">
                <Image Source="{Binding WindowLogo}"
                   RenderOptions.BitmapScalingMode="HighQuality"
                   Height="{StaticResource TitleFontSize}" />
                <TextBlock FontSize="{StaticResource TitleFontSize}" VerticalAlignment="Center" Text="{Binding WindowTitle}"/>
            </StackPanel>
        </DataTemplate>
    </pu:WindowXCaption.HeaderTemplate>
    <pu:WindowXCaption.ExtendControl>
        <StackPanel Orientation="Horizontal">
            <Button x:Name="BtnSwitchTheme"
                FontSize="18"
                ToolTip="Light / Dark"
                Style="{StaticResource {x:Static pu:WindowXCaption.MinimizeButtonStyleKey}}"
                FontFamily="/HMI.UI;component/Resources/Font/#iconfont"
                Content="&#xe60e;"
                Click="Button_Click_2"/>
        </StackPanel>
    </pu:WindowXCaption.ExtendControl>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <ctl:MainTabControl Grid.Row="0" x:Name="menuTab">
        </ctl:MainTabControl>
        <!--<ctl:MenuTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange"> </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="1" Background="{StaticResource MainStatusBar}">99999</StatusBar>
    </Grid>
</pu:WindowX>
WPFProject/PanuonWindow.xaml.cs
New file
@@ -0,0 +1,93 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Extension;
using HMI.Core.Api;
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.UI.Util;
using Panuon.WPF.UI;
using WPFProject.Utils;
namespace WPFProject
{
    /// <summary>
    /// PanuonWindow.xaml 的交互逻辑
    /// </summary>
    public partial class PanuonWindow : WindowX
    {
        public PanuonWindow()
        {
            ToastHelper.Register(new MainToastHandler());
            DataContext = this;
            RefrshConfig();
            InitializeComponent();
        }
        private void RefrshConfig()
        {
            WindowTitle = SystemSetting.Instance.WindowTitle;
            try
            {
                WindowLogo = new BitmapImage(new Uri(Path.Combine(PathHelper.IconFolder,
                    SystemSetting.Instance.LogoPath)));
            }
            catch (Exception ex)
            {
                WindowLogo = null;
            }
        }
        private string windowTitle;
        public string WindowTitle
        {
            get { return windowTitle; }
            set { Set(ref windowTitle, value); }
        }
        private BitmapImage windowLogo;
        public BitmapImage WindowLogo
        {
            get { return windowLogo; }
            set { Set(ref windowLogo, value); }
        }
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
            double temp = btn.RenderTransform.Value.Determinant;
            btn.RenderTransformOrigin = new Point(0.5, 0.5);
            ScaleTransform verticalFlipTransform = new ScaleTransform(1, temp * -1);
            btn.RenderTransform = verticalFlipTransform;
            menuTab.ChangeItemVisibility();
        }
        private void window_Loaded(object sender, RoutedEventArgs e)
        {
            SystemSetting.SystemSettingChanged += SystemSetting_SystemSettingChanged;
        }
        private void SystemSetting_SystemSettingChanged(object sender, SystemSetting e)
        {
            RefrshConfig();
        }
        //private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
        //{
        //    var s = (Style)this.Resources["WindowButton"];
        //    Button button = new Button
        //    {
        //        Content = "\ue60e",
        //        Style = (Style)this.Resources["WindowButton"]
        //    };
        //    WindowChrome.SetIsHitTestVisibleInChrome(button, true);
        //    button.Click += Button_Click_2;
        //    this.InsertButton(button, 0);
        //}
    }
}
WPFProject/Properties/AssemblyInfo.cs
@@ -3,6 +3,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using log4net;
using log4net.Config;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
@@ -53,3 +55,4 @@
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
WPFProject/Style/Colors.xaml
File was deleted
WPFProject/Style/CommonTree.xaml
@@ -5,7 +5,7 @@
                    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
        <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="TreeViewItem" BasedOn="{StaticResource TreeViewItemBaseStyle}">
WPFProject/Style/MainTab.xaml
@@ -2,7 +2,7 @@
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
        <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="tabItemStyle" TargetType="TabItem">
WPFProject/TestWindow.xaml
New file
@@ -0,0 +1,37 @@
<ui:BaseWindow x:Class="WPFProject.TestWindow"
        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:WPFProject"
        xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
        xmlns:ctl="clr-namespace:WPFProject.Controls"
        mc:Ignorable="d"
        Loaded="BaseWindow_Loaded"
        WindowState="Maximized"
        Style="{StaticResource BaseWindow}"
        Title="TestWindow">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <ctl:MainTabControl Grid.Row="0" x:Name="menuTab" OnTabChange="menuTab_OnTabChange">
        </ctl:MainTabControl>
        <!--<ctl:MenuTabControl Grid.Row="1" x:Name="menuTab" OnTabChange="menuTab_OnTabChange"> </ctl:MenuTabControl>-->
        <StatusBar Grid.Row="1" Background="{StaticResource MainStatusBar}">99999</StatusBar>
    </Grid>
</ui:BaseWindow>
WPFProject/TestWindow.xaml.cs
New file
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Shell;
using HMI.UI;
namespace WPFProject
{
    /// <summary>
    /// TestWindow.xaml 的交互逻辑
    /// </summary>
    public partial class TestWindow : BaseWindow
    {
        public TestWindow()
        {
            InitializeComponent();
        }
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
            double temp = btn.RenderTransform.Value.Determinant;
            btn.RenderTransformOrigin = new Point(0.5, 0.5);
            ScaleTransform verticalFlipTransform = new ScaleTransform(1, temp * -1);
            btn.RenderTransform = verticalFlipTransform;
            menuTab.ChangeItemVisibility();
        }
        private void menuTab_OnTabChange(object sender, RoutedEventArgs e)
        {
        }
        private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var s = (Style)this.Resources["WindowButton"];
            Button button = new Button
            {
                Content = "\ue60e",
                Style = (Style)this.Resources["WindowButton"]
            };
            WindowChrome.SetIsHitTestVisibleInChrome(button, true);
            button.Click += Button_Click_2;
            this.InsertButton(button,0);
        }
    }
}
WPFProject/UI/BottomBar.xaml.cs
@@ -15,8 +15,6 @@
            InitializeComponent();
        }
        public void AddMinWindow(MinInnerWindow min)
        {
            stackPanel.Children.Add(min);
WPFProject/UI/StepSetting.xaml
@@ -12,7 +12,7 @@
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/MainTab.xaml"/>
            </ResourceDictionary.MergedDictionaries>
WPFProject/UI/Test/FunctionSetting.xaml
@@ -16,7 +16,7 @@
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/CommonTree.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/MainTab.xaml"/>
            </ResourceDictionary.MergedDictionaries>
WPFProject/UI/Test/SequnceList.xaml
@@ -15,8 +15,8 @@
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/WindowResources.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/WindowResources.xaml"/>
                <ResourceDictionary Source="/WPFProject;component/Style/ListViewStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
@@ -193,7 +193,7 @@
                      
                      ItemsSource="{Binding Steps}">
                <DataGrid.Columns>
                    <DataGridTextColumn IsReadOnly="True" Binding="{Binding Index}" Header="序号"/>
                    <DataGridTextColumn IsReadOnly="True" Binding="{Binding Index}" Header="#"/>
                    <DataGridTextColumn Header="标题1" Binding="{Binding Name}"></DataGridTextColumn>
                    <DataGridTextColumn Header="标题2" Binding="{Binding Name}"></DataGridTextColumn>
WPFProject/UI/WorkControl.xaml
@@ -1,9 +1,10 @@
<UserControl x:Class="WPFProject.UI.WorkControl"
<hb:PluginControl x:Class="WPFProject.UI.WorkControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WPFProject.UI"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:ui="clr-namespace:WPFProject.UI"
             xmlns:vm="clr-namespace:WPFProject.ViewModels"
             xmlns:view="clr-namespace:WPFProject.UI"
             xmlns:utils="clr-namespace:WPFProject.Utils"
@@ -13,18 +14,18 @@
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPFProject;component/Style/colors.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Canvas x:Name="WorkArea" Background="{StaticResource MainMenu}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <Canvas x:Name="WorkArea" Background="#FFF" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <!--<vt:ProgramList BorderThickness="1" BorderBrush="#000" Canvas.Left="335" Canvas.Top="121" utils:Resize.IsResizeable="True"></vt:ProgramList>
                <vt:ProgramList BorderThickness="1" BorderBrush="#000" Canvas.Left="600" Canvas.Top="121" FocusableChanged="ProgramList_FocusableChanged"></vt:ProgramList>-->
                <local:BottomBar x:Name="BottomBar" Canvas.Bottom="0" Panel.ZIndex="9999999"/>
                <ui:BottomBar x:Name="BottomBar" Canvas.Bottom="0" Panel.ZIndex="9999999"/>
            </Canvas>
        </ScrollViewer>
@@ -66,4 +67,4 @@
            <vt:ProgramList Grid.Row="2" BorderThickness="1" BorderBrush="#000"></vt:ProgramList>
        </Grid>-->
    </Grid>
</UserControl>
</hb:PluginControl>
WPFProject/UI/WorkControl.xaml.cs
@@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HMI.Base;
using log4net;
using WPFProject.Controls;
using WPFProject.Models;
using WPFProject.UI.Test;
using WPFProject.Utils;
namespace WPFProject.UI
@@ -13,8 +13,9 @@
    /// <summary>
    /// WorkControl.xaml 的交互逻辑
    /// </summary>
    public partial class WorkControl : UserControl, IPlugin
    public partial class WorkControl : PluginControl
    {
        private ILog log;
        public WorkControl()
        {
            InitializeComponent();
@@ -68,49 +69,49 @@
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            List<CanvasPosition> pos = InitGridSize();
            ProgramList programList = new ProgramList();
            InnerWindow p = new InnerWindow(pos[0])
            {
                Title = "程序列表",
                Content = programList
            };
            HandleInnerWindow(p);
            //List<CanvasPosition> pos = InitGridSize();
            //ProgramList programList = new ProgramList();
            //InnerWindow p = new InnerWindow(pos[0])
            //{
            //    Title = "程序列表",
            //    Content = programList
            //};
            //HandleInnerWindow(p);
            InnerWindow p1 = new InnerWindow(pos[1])
            {
                Title = "图片",
            };
            HandleInnerWindow(p1);
            //InnerWindow p1 = new InnerWindow(pos[1])
            //{
            //    Title = "图片",
            //};
            //HandleInnerWindow(p1);
            SequnceList sequnceList = new SequnceList();
            programList.ProgramChanged += sequnceList.Selected_Program_Changed;
            //SequnceList sequnceList = new SequnceList();
            //programList.ProgramChanged += sequnceList.Selected_Program_Changed;
            InnerWindow p2 = new InnerWindow(pos[2])
            {
                Title = "序列",
                Content = sequnceList
            };
            HandleInnerWindow(p2);
            //InnerWindow p2 = new InnerWindow(pos[2])
            //{
            //    Title = "序列",
            //    Content = sequnceList
            //};
            //HandleInnerWindow(p2);
            FunctionSetting functionSetting = new FunctionSetting();
            InnerWindow p3 = new InnerWindow(pos[3])
            {
                Title = "函数",
                Content = functionSetting
            };
            HandleInnerWindow(p3);
            //FunctionSetting functionSetting = new FunctionSetting();
            //InnerWindow p3 = new InnerWindow(pos[3])
            //{
            //    Title = "函数",
            //    Content = functionSetting
            //};
            //HandleInnerWindow(p3);
            StepSetting stepSetting = new StepSetting();
            InnerWindow p4 = new InnerWindow(pos[4])
            {
                Title = "单步设置",
                Content = stepSetting
            };
            HandleInnerWindow(p4);
            //StepSetting stepSetting = new StepSetting();
            //InnerWindow p4 = new InnerWindow(pos[4])
            //{
            //    Title = "单步设置",
            //    Content = stepSetting
            //};
            //HandleInnerWindow(p4);
            SizeChanged += WorkControl_SizeChanged;
            //SizeChanged += WorkControl_SizeChanged;
            Loaded -= UserControl_Loaded;
        }
@@ -283,5 +284,30 @@
                }
            }
        }
        public object Active()
        {
            return null;
        }
        public object DeActive()
        {
            return null;
        }
        public void OnTabChange(object sender, object args)
        {
        }
        public object Init()
        {
            return null;
        }
        public void SetLogger(ILog logger)
        {
            log = logger;
        }
    }
}
WPFProject/Utils/IPlugin.cs
File was deleted
WPFProject/Utils/MainToastHandler.cs
New file
@@ -0,0 +1,42 @@
using System.Windows;
using Panuon.WPF.UI;
using Panuon.WPF.UI.Configurations;
using HMI.UI.Util;
namespace WPFProject.Utils
{
    public class MainToastHandler : ToastHandler
    {
        public void Success(Window window, string message,
            MessageBoxIcon icon, ToastPosition position,
            double offset, int durationMs, ToastWindow targetWindow)
        {
            var setting = Application.Current.FindResource("toastSuccess") as ToastSetting;
            if (window is PanuonWindow pw)
            {
                double num = 20 + (pw.menuTab.MenuVisibility == Visibility.Visible ? pw.menuTab.Menu.ActualHeight + pw.menuTab.SubMenu.ActualHeight : 0);
                Toast.Show(pw, message, icon, position, num, durationMs, setting);
            }
            else if (window is WindowX wx)
            {
                Toast.Show(wx, message, icon, position, 0, durationMs, setting);
            }
        }
        public void Warning(Window window, string message,
            MessageBoxIcon icon, ToastPosition position,
            double offset, int durationMs, ToastWindow targetWindow)
        {
            var setting = Application.Current.FindResource("toastWarning") as ToastSetting;
            if (window is PanuonWindow pw)
            {
                double num = 20 + (pw.menuTab.MenuVisibility == Visibility.Visible ? pw.menuTab.Menu.ActualHeight + pw.menuTab.SubMenu.ActualHeight : 0);
                Toast.Show(pw, message, icon, position, num, durationMs, setting);
            }
            else if (window is WindowX wx)
            {
                Toast.Show(wx, message, icon, position, 0, durationMs, setting);
            }
        }
    }
}
WPFProject/Utils/Resize.cs
@@ -44,7 +44,7 @@
            {
                FrameworkElement element = d as FrameworkElement;
                if (element == null) return;
                var resize = GetResizeable(element);
                Resizeable resize = GetResizeable(element);
                if (resize == null) return;
                resize.Detach();
                resize.Attach();
WPFProject/WPFProject.csproj
@@ -35,6 +35,26 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>latest</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>latest</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="AIStudio.Wpf.Controls, Version=1.2.2.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>packages\AIStudio.Wpf.Controls.1.2.2\lib\net461\AIStudio.Wpf.Controls.dll</HintPath>
@@ -45,16 +65,26 @@
    <Reference Include="HandyControl, Version=3.4.0.0, Culture=neutral, PublicKeyToken=45be8712787a1e5b, processorArchitecture=MSIL">
      <HintPath>packages\HandyControl.3.4.0\lib\net472\HandyControl.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
      <HintPath>packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Xaml.Behaviors, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>packages\Microsoft.Xaml.Behaviors.Wpf.1.1.39\lib\net45\Microsoft.Xaml.Behaviors.dll</HintPath>
    </Reference>
    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF, Version=1.1.2.2, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
    </Reference>
    <Reference Include="Panuon.WPF.UI, Version=1.2.4.8, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>packages\Panuon.WPF.UI.1.2.4.9\lib\net472\Panuon.WPF.UI.dll</HintPath>
    </Reference>
    <Reference Include="PresentationFramework.Aero" />
    <Reference Include="System" />
    <Reference Include="System.ComponentModel.Composition" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Data" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
@@ -121,15 +151,22 @@
    <Compile Include="Controls\InnerWindow.xaml.cs">
      <DependentUpon>InnerWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="Controls\MainTabControl.xaml.cs">
      <DependentUpon>MainTabControl.xaml</DependentUpon>
    </Compile>
    <Compile Include="Controls\MinInnerWindow.xaml.cs">
      <DependentUpon>MinInnerWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="Events\TabChange.cs" />
    <Compile Include="Entity\PluginLoadMessage.cs" />
    <Compile Include="Entity\PluginSettingInfo.cs" />
    <Compile Include="Forms\Form1.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Forms\Form1.Designer.cs">
      <DependentUpon>Form1.cs</DependentUpon>
    </Compile>
    <Compile Include="LoadingWindow.xaml.cs">
      <DependentUpon>LoadingWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="Models\CanvasPosition.cs" />
    <Compile Include="Models\FuncModel.cs" />
@@ -138,6 +175,12 @@
    <Compile Include="Models\Program.cs" />
    <Compile Include="Models\Sequence.cs" />
    <Compile Include="Models\Step.cs" />
    <Compile Include="PanuonWindow.xaml.cs">
      <DependentUpon>PanuonWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="TestWindow.xaml.cs">
      <DependentUpon>TestWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="UI\BottomBar.xaml.cs">
      <DependentUpon>BottomBar.xaml</DependentUpon>
    </Compile>
@@ -165,12 +208,16 @@
    <Compile Include="Utils\CanvasHelper.cs" />
    <Compile Include="Utils\DdDragHandler.cs" />
    <Compile Include="Utils\DdDropHandler.cs" />
    <Compile Include="Utils\IPlugin.cs" />
    <Compile Include="Utils\MainToastHandler.cs" />
    <Compile Include="Utils\Resize.cs" />
    <Compile Include="Utils\Win32Helper.cs" />
    <Compile Include="Utils\XmlUtil.cs" />
    <Compile Include="ViewModels\ProgramListViewModel.cs" />
    <Page Include="Controls\InnerWindow.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="Controls\MainTabControl.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
@@ -179,6 +226,10 @@
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Controls\MinInnerWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="LoadingWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
@@ -198,15 +249,47 @@
      <DependentUpon>MainWindow.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="PanuonWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\BottomBar.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\StepSetting.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\TestWindow.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\Test\FunctionSetting.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\Test\ProgramList.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\Test\SequnceList.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\WorkControl.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="UI\WorkWindow.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Resource Include="Resources\Font\Font.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Resource>
    <Page Include="Style\Colors.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Style\ListViewStyle.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
@@ -219,39 +302,7 @@
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Style\WindowResources.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\BottomBar.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\StepSetting.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\TestWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\Test\FunctionSetting.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\Test\ProgramList.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\Test\SequnceList.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\WorkControl.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="UI\WorkWindow.xaml">
    <Page Include="TestWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
@@ -277,6 +328,9 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <Content Include="log4net.config">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
@@ -296,10 +350,49 @@
    <Resource Include="Resources\Icon\xinzeng_1.png" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\VisionBuilder.Core\VisionBuilder.Core.csproj">
    <ProjectReference Include="..\HMI.AlermInfoManage\HMI.AlermInfoManage.csproj">
      <Project>{945c2577-4558-45e3-891a-096cb3047304}</Project>
      <Name>HMI.AlermInfoManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Base\HMI.Base.csproj">
      <Project>{2a6f9720-a890-4036-9d73-41a0dae28341}</Project>
      <Name>HMI.Base</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.IOMonitor\HMI.IOMonitor.csproj">
      <Project>{f7663c79-7047-42f3-b129-73a2575ee7c6}</Project>
      <Name>HMI.IOMonitor</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.ParameterManage\HMI.ParameterManage.csproj">
      <Project>{736cd77d-b81e-4866-bb00-84b0b57110f0}</Project>
      <Name>HMI.ParameterManage</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.SettingControl\HMI.SettingControl.csproj">
      <Project>{3dd748ac-9708-49ad-b846-659214035588}</Project>
      <Name>HMI.SettingControl</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.UI\HMI.UI.csproj">
      <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
      <Name>HMI.UI</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.Core\HMI.Core.csproj">
      <Project>{3e3bf44a-0b69-45c5-9512-528cb48a7106}</Project>
      <Name>VisionBuilder.Core</Name>
      <Name>HMI.Core</Name>
    </ProjectReference>
    <ProjectReference Include="..\HMI.MenuManage\HMI.MenuManage.csproj">
      <Project>{de4205c6-5923-46fd-a4b9-65169bf61c75}</Project>
      <Name>HMI.MenuManage</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="Icon\xinzeng_1.png">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>copy /Y "$(TargetDir)HMI.MenuManage.dll" "$(TargetDir)\Plugin\HMI.MenuManage.dll"
copy /Y "$(TargetDir)HMI.MenuManage.pdb" "$(TargetDir)\Plugin\HMI.MenuManage.pdb"
copy /Y "$(TargetDir)HMI.AlermInfoManage.dll" "$(TargetDir)\Plugin\HMI.AlermInfoManage.dll"
copy /Y "$(TargetDir)HMI.AlermInfoManage.pdb" "$(TargetDir)\Plugin\HMI.AlermInfoManage.pdb"</PostBuildEvent>
  </PropertyGroup>
</Project>
WPFProject/WPFProject.sln
@@ -1,38 +1,156 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.33328.57
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFProject", "WPFProject.csproj", "{D47D2314-5DD5-4899-ADF9-15ED057289D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Core", "..\VisionBuilder.Core\VisionBuilder.Core.csproj", "{3E3BF44A-0B69-45C5-9512-528CB48A7106}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Core", "..\HMI.Core\HMI.Core.csproj", "{3E3BF44A-0B69-45C5-9512-528CB48A7106}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Test", "..\VisionBuilder.Test\VisionBuilder.Test.csproj", "{6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Engine", "..\VisionBuilder.Engine\VisionBuilder.Engine.csproj", "{8E5E8A68-0B9D-4A6A-B325-730C64603FDF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.UI", "..\HMI.UI\HMI.UI.csproj", "{F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.MenuManage", "..\HMI.MenuManage\HMI.MenuManage.csproj", "{DE4205C6-5923-46FD-A4B9-65169BF61C75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.Run", "..\VisionBuilder.Run\VisionBuilder.Run.csproj", "{7C299AED-88CC-437E-A68B-1550AA72E6B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.UnitTest", "..\HMI.UnitTest\HMI.UnitTest.csproj", "{325A7F52-FE51-44F0-B3AC-5F42F5356605}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.AlermInfoManage", "..\HMI.AlermInfoManage\HMI.AlermInfoManage.csproj", "{945C2577-4558-45E3-891A-096CB3047304}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.SettingControl", "..\HMI.SettingControl\HMI.SettingControl.csproj", "{3DD748AC-9708-49AD-B846-659214035588}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Base", "..\HMI.Base\HMI.Base.csproj", "{2A6F9720-A890-4036-9D73-41A0DAE28341}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.DataViewer", "..\HMI.DataViewer\HMI.DataViewer.csproj", "{A580DE4A-34E7-4EFE-AD2C-0871396000E9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.IOMonitor", "..\HMI.IOMonitor\HMI.IOMonitor.csproj", "{F7663C79-7047-42F3-B129-73A2575EE7C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.ParameterManage", "..\HMI.ParameterManage\HMI.ParameterManage.csproj", "{736CD77D-B81E-4866-BB00-84B0B57110F0}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
        Debug|x64 = Debug|x64
        Release|Any CPU = Release|Any CPU
        Release|x64 = Release|x64
    EndGlobalSection
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Debug|x64.ActiveCfg = Debug|x64
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Debug|x64.Build.0 = Debug|x64
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Release|Any CPU.Build.0 = Release|Any CPU
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Release|x64.ActiveCfg = Release|x64
        {D47D2314-5DD5-4899-ADF9-15ED057289D3}.Release|x64.Build.0 = Release|x64
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Debug|x64.ActiveCfg = Debug|x64
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Debug|x64.Build.0 = Debug|x64
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|Any CPU.Build.0 = Release|Any CPU
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|x64.ActiveCfg = Release|x64
        {3E3BF44A-0B69-45C5-9512-528CB48A7106}.Release|x64.Build.0 = Release|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x64.ActiveCfg = Debug|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Debug|x64.Build.0 = Debug|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|Any CPU.Build.0 = Release|Any CPU
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x64.ActiveCfg = Release|x64
        {6E1CC97A-B3CB-4E9F-94E0-F264DE0D8298}.Release|x64.Build.0 = Release|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x64.ActiveCfg = Debug|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Debug|x64.Build.0 = Debug|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|Any CPU.Build.0 = Release|Any CPU
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x64.ActiveCfg = Release|x64
        {8E5E8A68-0B9D-4A6A-B325-730C64603FDF}.Release|x64.Build.0 = Release|x64
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|x64.ActiveCfg = Debug|x64
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Debug|x64.Build.0 = Debug|x64
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Release|Any CPU.Build.0 = Release|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Release|x64.ActiveCfg = Release|Any CPU
        {F17DE6B5-AF1D-48C6-94A0-6B763A6546A6}.Release|x64.Build.0 = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Debug|x64.ActiveCfg = Debug|x64
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Debug|x64.Build.0 = Debug|x64
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|Any CPU.Build.0 = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|x64.ActiveCfg = Release|Any CPU
        {DE4205C6-5923-46FD-A4B9-65169BF61C75}.Release|x64.Build.0 = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x64.ActiveCfg = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Debug|x64.Build.0 = Debug|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|Any CPU.Build.0 = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x64.ActiveCfg = Release|Any CPU
        {7C299AED-88CC-437E-A68B-1550AA72E6B0}.Release|x64.Build.0 = Release|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|x64.ActiveCfg = Debug|x64
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Debug|x64.Build.0 = Debug|x64
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Release|Any CPU.Build.0 = Release|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Release|x64.ActiveCfg = Release|Any CPU
        {325A7F52-FE51-44F0-B3AC-5F42F5356605}.Release|x64.Build.0 = Release|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Debug|x64.ActiveCfg = Debug|x64
        {945C2577-4558-45E3-891A-096CB3047304}.Debug|x64.Build.0 = Debug|x64
        {945C2577-4558-45E3-891A-096CB3047304}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Release|Any CPU.Build.0 = Release|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Release|x64.ActiveCfg = Release|Any CPU
        {945C2577-4558-45E3-891A-096CB3047304}.Release|x64.Build.0 = Release|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Debug|x64.ActiveCfg = Debug|x64
        {3DD748AC-9708-49AD-B846-659214035588}.Debug|x64.Build.0 = Debug|x64
        {3DD748AC-9708-49AD-B846-659214035588}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Release|Any CPU.Build.0 = Release|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Release|x64.ActiveCfg = Release|Any CPU
        {3DD748AC-9708-49AD-B846-659214035588}.Release|x64.Build.0 = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.ActiveCfg = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Debug|x64.Build.0 = Debug|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|Any CPU.Build.0 = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|x64.ActiveCfg = Release|Any CPU
        {2A6F9720-A890-4036-9D73-41A0DAE28341}.Release|x64.Build.0 = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Debug|x64.ActiveCfg = Debug|x64
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Debug|x64.Build.0 = Debug|x64
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|Any CPU.Build.0 = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|x64.ActiveCfg = Release|Any CPU
        {A580DE4A-34E7-4EFE-AD2C-0871396000E9}.Release|x64.Build.0 = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x64.ActiveCfg = Debug|x64
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Debug|x64.Build.0 = Debug|x64
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|Any CPU.Build.0 = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x64.ActiveCfg = Release|Any CPU
        {F7663C79-7047-42F3-B129-73A2575EE7C6}.Release|x64.Build.0 = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|x64.ActiveCfg = Debug|x64
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Debug|x64.Build.0 = Debug|x64
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|Any CPU.Build.0 = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|x64.ActiveCfg = Release|Any CPU
        {736CD77D-B81E-4866-BB00-84B0B57110F0}.Release|x64.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
WPFProject/log4net.config
New file
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<log4net>
    <!-- 控制台日志配置 -->
    <appender name="Console" type="log4net.Appender.ConsoleAppender">
        <!-- 日志输出格式 -->
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%5level [%thread] (%file:%line) - %message%newline" />
        </layout>
    </appender>
    <!-- 文件存储日志配置 -->
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
        <appendToFile value="true" />
        <rollingStyle value="Date" />
        <datePattern value="'log_'yyyyMMdd'.log'"/>
        <staticLogFileName value="false"/>
        <!-- 保存文件的名称 -->
        <file value="./Log/" />
        <!-- 文件的编码方式 -->
        <param name="Encoding" value="UTF-8"/>
        <!-- 每个文件的大小 -->
        <maximumFileSize value="100KB" />
        <!-- 保存文件数量 -->
        <maxSizeRollBackups value="2" />
        <!-- 日志输出格式 -->
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%logger: [%date] [%thread] [%level] [%class:%L] - %message%newline" />
        </layout>
    </appender>
    <root>
        <level value="INFO" />
        <appender-ref ref="Console" />
        <appender-ref ref="RollingFile" />
    </root>
</log4net>
WPFProject/packages.config
@@ -3,8 +3,11 @@
  <package id="AIStudio.Wpf.Controls" version="1.2.2" targetFramework="net472" />
  <package id="gong-wpf-dragdrop" version="3.2.1" targetFramework="net472" />
  <package id="HandyControl" version="3.4.0" targetFramework="net472" />
  <package id="log4net" version="3.0.3" targetFramework="net472" />
  <package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.39" targetFramework="net472" />
  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.2.2" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.2.4.9" targetFramework="net472" />
  <package id="System.IO" version="4.3.0" targetFramework="net472" />
  <package id="System.Net.Http" version="4.3.4" targetFramework="net472" />
  <package id="System.Runtime" version="4.3.0" targetFramework="net472" />