从JSON模式创建WPF UI

时间:2017-09-19 12:53:30

标签: c# json wpf jsonschema

有没有办法使用JSON Schema创建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.

2 个答案:

答案 0 :(得分:1)

所以,这当然是可能的。您需要做的是定义反序列化例程以生成实现List<T>/ObservableCollection<T>的{​​{1}}个对象。您可以通过INotifyPropeertyChanged执行此操作,也可以将Newtonsoft Json写入JSchema转换器

接下来,您可以将ViewModel甚至ContentControl绑定到此可枚举,如主详细信息视图,并且详细信息视图可以在所选对象上实现Listbox/StackPanelExample of Property grid

确保所有绑定均为Property Grid,以保留您所做的更改。 此外,您可以在TwoWay上实施OnSelectionChanged事件以序列化更改。

资源

Master detail view

Property grid source

答案 1 :(得分:0)

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

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;
        }
    }
}