【问题标题】:Create WPF UI from JSON schema从 JSON 架构创建 WPF UI
【发布时间】:2017-09-19 12:53:29
【问题描述】:

有没有办法使用 JSON 架构创建 WPF UI?我知道可以在 AngularJS 和其他人的帮助下将其转换为 HTML 表单。但是寻找一种方法来创建 WPF 并没有取得成果。

存在一个Source by Rico Suter 关于如何创建 Visual Json 编辑器。我的要求与这里给出的略有不同。就我而言,我想基于架构和架构中提到的属性创建 WPF 控件。而且,在 UI 的帮助下,我希望能够通过在 UI 控件中输入值来创建尽可能多的 JSON 对象。

例如,让我们以下面的 JSON 架构为例。

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {},
  "id": "http://example.com/example.json",
  "properties": {
    "checked": {
      "default": false,
      "description": "An explanation about the purpose of this instance.",
      "id": "/properties/checked",
      "title": "The Checked Schema",
      "type": "boolean"
    },
    "dimensions": {
      "id": "/properties/dimensions",
      "properties": {
        "height": {
          "default": 10,
          "description": "An explanation about the purpose of this instance.",
          "id": "/properties/dimensions/properties/height",
          "title": "The Height Schema",
          "type": "integer"
        },
        "width": {
          "default": 5,
          "description": "An explanation about the purpose of this instance.",
          "id": "/properties/dimensions/properties/width",
          "title": "The Width Schema",
          "type": "integer"
        }
      },
      "type": "object"
    },
    "id": {
      "default": 1,
      "description": "An explanation about the purpose of this instance.",
      "id": "/properties/id",
      "title": "The Id Schema",
      "type": "integer"
    },
    "name": {
      "default": "A green door",
      "description": "An explanation about the purpose of this instance.",
      "id": "/properties/name",
      "title": "The Name Schema",
      "type": "string"
    },
    "price": {
      "default": 12.5,
      "description": "An explanation about the purpose of this instance.",
      "id": "/properties/price",
      "title": "The Price Schema",
      "type": "number"
    },
    "tags": {
      "id": "/properties/tags",
      "items": {
        "default": "home",
        "description": "An explanation about the purpose of this instance.",
        "id": "/properties/tags/items",
        "title": "The Empty Schema",
        "type": "string"
      },
      "type": "array"
    }
  },
  "type": "object"
}

我希望能够为checked 属性显示一个复选框。同样,GroupBox 或带有 2 个TextBox 的控件可以输入尺寸(高度和宽度)。此 UI 应使用户能够输入所需的值,基于该值可以生成 JSON 对象。类似的,

{
  "checked": false,
  "dimensions": {
    "width": 5,
    "height": 10
  },
  "id": 1,
  "name": "A green door",
  "price": 12.5,
  "tags": [
    "home",
    "green"
  ]
}

目前,我正在创建一个 JSchema 对象列表,并将每个属性反序列化为 JSchema 类型,然后将其添加到列表中。此后,我试图创建相同的控件。这只是一团糟,我还没有完全达到我的目标。然而,我不觉得我会对最终结果感到满意。如果您可以提出一种实现相同目标的方法,那将有很大帮助。谢谢。

样本取自

here.

【问题讨论】:

  • “我希望能够为选中的属性显示一个复选框” - 好主意,你实现它的程度如何?
  • 为什么不使用任何库将 json 数据转换为普通模型类,用于制作视图模型。例如Newtonsoft.json
  • 这样做当然可能,但是没有内置任何东西,实现起来并不容易。基本思想是递归遍历 JSON,并以编程方式实例化控件,在它们上设置属性,然后将它们添加到当前的父项中。也可以通过创造性地使用 MVVM 和数据绑定来完成。
  • @MaheshMalpani 我没有任何 json 数据。我只有架构。
  • @BradleyUffner 目前,我正在递归地浏览 json 并创建控件。只是它使包括 UI 在内的所有内容都变得非常混乱。这就是为什么我需要知道是否已经存在任何东西。

标签: c# json wpf jsonschema


【解决方案1】:

所以,这当然是可能的。您需要做的是定义反序列化例程以生成实现INotifyPropeertyChangedList<T>/ObservableCollection<T> 对象。你可以通过Newtonsoft Json 或写一个JSchemaViewModel 转换器

接下来,您可以将ContentControl 甚至Listbox/StackPanel 绑定到此枚举,就像主从视图一样,详细信息视图可以在选定对象上实现Property GridExample of Property grid.

确保您的所有绑定都是TwoWay 以保留您所做的更改。 此外,您可以在 StackPanel 上实现 OnSelectionChanged 事件以序列化更改。

资源

Master detail view

Property grid source

【讨论】:

    【解决方案2】:

    我想为 UWP 做同样的事情,但没有找到我可以使用的有效解决方案。 除了上面提到的 PropertyGrid 我还发现 DataGrid 来自 Windows 社区工具包,DataForm 来自 Telerik。 使用这些仍然需要将 Json 转换为 Object 模型并返回。 事实证明,Newtonsoft.Json 在构建时考虑了数据绑定,因此从 Json 生成将绑定到 Json 属性的控件非常容易。 这是一个代码 sn-p 来做到这一点:

    private void RenderForm(JArray jArray)
    {
        StackPanel stackPanel = new StackPanel() { Orientation = Orientation.Vertical };
        this.Content = stackPanel;
        stackPanel.Height = this.Height;
        stackPanel.Width = this.Width;
        stackPanel.Children.Add(button);
        foreach (JObject element in jArray)
        {
            String type = element["type"].ToString();
            TextBlock textBlock = new TextBlock() { Text = element["name"].ToString() };
            textBlock.Padding = new Thickness() { Top = 5 };
            switch (type)
            {
                case "hiddendata":
                    break;
                case "bool":
                    CheckBox checkBox = new CheckBox();
                    checkBox.DataContext = element;
                    Binding checkBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
                    checkBoxBinding.Source = element;
                    checkBox.SetBinding(CheckBox.IsCheckedProperty, checkBoxBinding);
                    stackPanel.Children.Add(textBlock);
                    stackPanel.Children.Add(checkBox);
                    break;
                case "image":
                    if (!String.IsNullOrEmpty(element["value"].Value<String>()))
                    {
                        Image image = new Image();
                        image.MaxHeight = 200;
                        image.MaxWidth = 200;
                        var ignore = SetImageSource(element["value"].Value<String>(), image);
                        stackPanel.Children.Add(textBlock);
                        stackPanel.Children.Add(image);
                    }
                    break;
                case "info":
                    if (!String.IsNullOrEmpty(element["value"].Value<String>()))
                    {
                        TextBlock displayTextBlock = new TextBlock();
                        displayTextBlock.DataContext = element;
                        Binding displayTextBlockBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.OneWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
                        displayTextBlockBinding.Source = element;
                        displayTextBlock.SetBinding(TextBlock.TextProperty, displayTextBlockBinding);
                        stackPanel.Children.Add(textBlock);
                        stackPanel.Children.Add(displayTextBlock);
                    }
                    break;
                case "password":
                    PasswordBox passwordBox = new PasswordBox();
                    passwordBox.DataContext = element;
                    Binding passwordBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
                    passwordBoxBinding.Source = element;
                    passwordBox.SetBinding(PasswordBox.PasswordProperty, passwordBoxBinding);
                    stackPanel.Children.Add(textBlock);
                    stackPanel.Children.Add(passwordBox);
                    break;
                case "string":
                default:
                    TextBox textBox = new TextBox();
                    textBox.DataContext = element;
                    Binding textBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
                    textBoxBinding.Source = element;
                    textBox.SetBinding(TextBox.TextProperty, textBoxBinding);
                    stackPanel.Children.Add(textBlock);
                    stackPanel.Children.Add(textBox);
                    break;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      • 1970-01-01
      • 2017-12-07
      • 2017-10-13
      • 2011-06-03
      • 2010-11-25
      • 2018-05-26
      相关资源
      最近更新 更多