实现 "基于 JSON 的扩展配置 (Extended Config)" 功能的详细教程。我们将按照 Model (数据层) -> ViewModel (逻辑层) -> View (UI层) 的顺序进行。
这个功能的 核心思想 是:
- 数据库 存的是一个长字符串(JSON 格式)。
- 界面 显示的是一个表格(List 列表)。
- ViewModel 负责在"字符串"和"列表"之间来回转换。
第一步:修改数据库实体模型 (Model)
我们需要在数据库表中增加一列,用来专门存这些"乱七八糟"的扩展配置。
-
打开文件 : D:\BZW\MyQucikDevFrameWork\QuickNetFrameWork\QuickWpf\QuickWpf.Core\Models\Business\Recipe.cs
-
添加代码 :
在 Recipe 类中,添加一个 ExtendedAttributes 字符串属性。// 在 Recipe 类内部添加:
[SugarTable("Recipes")]
public class Recipe
{
// ... 其他已有属性 .../// <summary> /// 扩展属性 (JSON 格式),用于存储不固定的配方参数 /// ColumnDataType = "TEXT" 告诉数据库这是一个长文本字段, 能存很多内容 /// </summary> [SugarColumn(ColumnDataType = "TEXT", IsNullable = true)] public string ExtendedAttributes { get; set; } = "{} "; // 默认为空 JSON 对象}
第二步:创建列表项辅助类 (Helper Class)
界面上的表格(DataGrid)需要绑定到一个对象列表。我们需要定义这个"对象"长什么样(比如有 Key, Value, Description 三列)。
-
打开文件 : D:\BZW\MyQucikDevFrameWork\QuickNetFrameWork\QuickWpf\QuickWpf.Modules.Hardware\ViewModels\RecipeViewModel.cs (注:你也可以新建一个单独的 .cs 文件,但为了方便,我们先放在 ViewModel 文件里)
-
添加代码 :
在 RecipeViewModel 类的 外面 (也就是 namespace 里面),定义 ExtendedAttributeItem 类。namespace QuickWpf.Modules.Hardware.ViewModels
{
// --- 新增这个类 ---
public class ExtendedAttributeItem : BindableBase
{
private string _key = string.Empty;
public string Key
{
get => _key;
set => SetProperty(ref _key, value);
}private string _value = string.Empty; public string Value { get => _value; set => SetProperty(ref _value, value); } private string _description = string.Empty; public string Description { get => _description; set => SetProperty(ref _description, value); } } // ------------------ public class RecipeViewModel : BindableBase { // ... }}
第三步:在 ViewModel 中编写逻辑 (核心步骤)
这是最关键的一步,我们要处理"加载时把 JSON 变列表"和"保存时把列表变 JSON"。
-
打开文件 : D:\BZW\MyQucikDevFrameWork\QuickNetFrameWork\QuickWpf\QuickWpf.Modules.Hardware\ViewModels\RecipeViewModel.cs
-
修改代码 :在 RecipeViewModel 类内部添加以下内容。
2.1 定义列表属性和命令
// 1. 定义绑定的数据源 private ObservableCollection<ExtendedAttributeItem> _extendedAttributesList; public ObservableCollection<ExtendedAttributeItem> ExtendedAttributesList { get => _extendedAttributesList; set => SetProperty(ref _extendedAttributesList, value); } // 2. 定义选中项(用于删除功能) private ExtendedAttributeItem? _selectedExtendedAttribute; public ExtendedAttributeItem? SelectedExtendedAttribute { get => _selectedExtendedAttribute; set => SetProperty(ref _selectedExtendedAttribute, value); } // 3. 定义按钮命令 public DelegateCommand AddExtendedAttributeCommand { get; } public DelegateCommand RemoveExtendedAttributeCommand { get; }2.2 在构造函数中初始化命令
public RecipeViewModel(...) { // ... 其他初始化 ... // 初始化命令 AddExtendedAttributeCommand = new DelegateCommand (OnAddExtendedAttribute); RemoveExtendedAttributeCommand = new DelegateCommand (OnRemoveExtendedAttribute); }2.3 实现加载逻辑 (JSON -> List) 找到 LoadRecipeDetails 方法(或者你加载配方详情的地方),添加转换逻辑:
private void LoadRecipeDetails() { // ... 加载其他属性 ... // --- 新增:加载扩展属性 --- try { if (string.IsNullOrWhiteSpace(SelectedRecipe. ExtendedAttributes) || SelectedRecipe. ExtendedAttributes == "{}") { // 如果是空的,就创建一个新列表 ExtendedAttributesList = new ObservableCollection<ExtendedAttributeItem> (); } else { // 使用 System.Text.Json 将字符串反序列化为对象 列表 var list = System.Text.Json.JsonSerializer. Deserialize<List<ExtendedAttributeItem>> (SelectedRecipe.ExtendedAttributes); ExtendedAttributesList = new ObservableCollection<ExtendedAttributeItem> (list); } } catch { // 如果解析失败(比如格式坏了),就重置为空列表,防止报 错 ExtendedAttributesList = new ObservableCollection<ExtendedAttributeItem>(); } }2.4 实现保存逻辑 (List -> JSON) 找到 OnSave 方法,添加转换逻辑:
private void OnSave() { if (SelectedRecipe != null) { // --- 新增:保存扩展属性 --- if (ExtendedAttributesList != null) { // 将对象列表序列化为 JSON 字符串,存回 Recipe 实 体 SelectedRecipe.ExtendedAttributes = System. Text.Json.JsonSerializer.Serialize (ExtendedAttributesList); } // ... 执行数据库保存 ... _databaseService.Db.Storageable(SelectedRecipe). ExecuteCommand(); } }2.5 实现增删行逻辑
private void OnAddExtendedAttribute() { if (ExtendedAttributesList == null) ExtendedAttributesList = new ObservableCollection<ExtendedAttributeItem>(); // 添加一行默认数据 ExtendedAttributesList.Add(new ExtendedAttributeItem { Key = "New Key", Value = "", Description = "" }); } private void OnRemoveExtendedAttribute() { if (SelectedExtendedAttribute != null) { ExtendedAttributesList.Remove (SelectedExtendedAttribute); } }
第四步:在 XAML 中添加界面 (View)
最后,我们需要在界面上画出这个表格。
-
打开文件 : D:\BZW\MyQucikDevFrameWork\QuickNetFrameWork\QuickWpf\QuickWpf.Modules.Hardware\Views\RecipePage.xaml
-
添加代码 :
找到 TabControl 部分,添加一个新的 TabItem 。<TabItem Header="Extended Config">
<Grid Margin="0,8,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions></TabItem><!-- 1. 顶部工具栏:添加/删除按钮 --> <!-- Visibility 绑定:只有在编辑模式(IsEditing=True)下 才显示 --> <StackPanel Orientation="Horizontal" Margin="0,0,0, 8" Visibility="{Binding IsEditing, Converter={StaticResource BoolToVisConverter}}"> <Button Command="{Binding AddExtendedAttributeCommand}" Content="Add Attribute" Style="{DynamicResource MahApps.Styles. Button.Square.Accent}" Margin="0,0,8,0"/> <Button Command="{Binding RemoveExtendedAttributeCommand}" Content="Remove" Style="{DynamicResource MahApps.Styles. Button.Square}"/> </StackPanel> <!-- 2. 数据表格 --> <DataGrid Grid.Row="1" ItemsSource="{Binding ExtendedAttributesList}" SelectedItem="{Binding SelectedExtendedAttribute}" AutoGenerateColumns="False" CanUserAddRows="False" IsReadOnly="{Binding IsEditing, Converter={StaticResource InvertBooleanConverter}}"> <!-- IsReadOnly: 如果不是编辑模式,表格就只 读 --> <DataGrid.Columns> <!-- Key 列 --> <DataGridTextColumn Binding="{Binding Key} " Header="Key" Width="150"/> <!-- Value 列 --> <DataGridTextColumn Binding="{Binding Value}" Header="Value" Width="*"/> <!-- Description 列 --> <DataGridTextColumn Binding="{Binding Description}" Header="Description" Width="*"/> </DataGrid.Columns> </DataGrid> </Grid>
总结
做完这四步,流程就通了:
- 用户 点击"Edit" -> 点击"Add Attribute" -> 表格里多了一行 -> 输入 Key="Speed", Value="100" 。
- ViewModel 里的 ExtendedAttributesList 多了一个对象。
- 用户 点击"Save"。
- ViewModel 把 List 变成字符串 [{"Key":"Speed","Value":"100",...}] 。
- Model 把这个字符串存进数据库的 ExtendedAttributes 字段。
- 下次 加载 时,反过来把字符串变回 List 显示在表格上。