How do I restore controls and there properties from a .json file?

Cynolycus 305 Reputation points
2026-08-13T05:43:00.5433333+00:00

I am trying to learn some new things with VB.Net that I have never done before, like creating custom controls, and so far I have been able to look things up on Google and modify them to suit my needs.

I have come to a situation where I now need to actually ask a question about .json.

I have never used a .json file before but have been able to create one that stores all the information that I think I will need.

My problem is that although I have been able to save the properties of each control I have no idea on how to load them back when the program runs and the user selects which .json file to use by selecting an option in a ComboBox which is filled with a list of saved files.

The program has a static list of controls that it creates when it starts. It can be a around one hundred custom controls on a single FlowLayoutPanel, but this is only likely to happen on two, and there are five FlowLayoutPanels, one on each tab of a TabControl. Then there are controls added to these FlowLayoutPanels depending on the users choice from a CheckedListBox which also contains user data, not hard coded.

When the user saves thier work it adds an option to the ComboBox so it can be loaded again. This is where the .json file is needed.

If the .json file loads the CheckedListBox with values before it goes on to create the other controls then the program should create the extra controls.

Since I don't know anything about .json, I am not counting on the controls being present.

I need help to load these controls from the .json file and create the missing controls if necessary.

It is the last part of this program that needs to be done, but I have no idea on what I am doing with .json so any help would be appreciated.

The code that I have used to create the .json file is.


' Root object holding all application state
Public Class AppState
    Public Property CheckedItems As New List(Of String)
    Public Property SingleLabelControls As New List(Of SingleLabelControlData)
    Public Property MultiLabelControls As New List(Of MultiLabelControlData)
End Class

' Base class for the shared components
Public Class CustomControlDataBase
    Public Property ParentName As String
    Public Property ControlName As String
    Public Property Title As String
    Public Property ImagePath As String
    Public Property Forecolor As Color

End Class

' For the control with 1 additional label
Public Class SingleLabelControlData
    Inherits CustomControlDataBase
    Public Property ExtraLabelText As String
End Class

' For the control with 6 additional labels
Public Class MultiLabelControlData
    Inherits CustomControlDataBase
    Public Property ExtraLabelsText As New List(Of String)
End Class

Private Sub SaveDataToInterface()
    Dim state As New AppState()

    ' Save CheckedListBox State
    For Each item In CheckedListBox1.CheckedItems
        state.CheckedItems.Add(item.ToString())
    Next

    Dim saveFlpList As New List(Of FlowLayoutPanel)
    saveFlpList.AddRange(GetAllControls(Me).OfType(Of FlowLayoutPanel)())
    For Each flp As FlowLayoutPanel In saveFlpList
        For Each ctrl As Control In flp.Controls

            ' Check for Control Type 1 (Single Label)
            If TypeOf ctrl Is LitratureItem Then
                Dim customCtrl = DirectCast(ctrl, LitratureItem)
                Dim data As New SingleLabelControlData With {
                .ParentName = flp.Name,
                .ControlName = customCtrl.Name,
                .Title = customCtrl.LblTitle.Text,
                .Forecolor = customCtrl.LblTitle.ForeColor,
                .ImagePath = CStr(customCtrl.PicImage.Tag),
                .ExtraLabelText = customCtrl.LblStatus.Text
            }
                state.SingleLabelControls.Add(data)

                ' Check for Control Type 2 (Multi-Label)
            ElseIf TypeOf ctrl Is SkillItem Then
                Dim customCtrl = DirectCast(ctrl, SkillItem)
                Dim data As New MultiLabelControlData With {
                .ParentName = flp.Name,
                .ControlName = customCtrl.Name,
                .Title = customCtrl.LblTitle.Text,
                .Forecolor = customCtrl.LblTitle.ForeColor,
                .ImagePath = CStr(customCtrl.PicImage.Tag)
            }

                ' Collect texts from the 6 labels
                data.ExtraLabelsText.Add(customCtrl.LblSkill1.Text)
                data.ExtraLabelsText.Add(customCtrl.LblSkill2.Text)
                data.ExtraLabelsText.Add(customCtrl.LblSkill3.Text)
                data.ExtraLabelsText.Add(customCtrl.LblSkill4.Text)
                data.ExtraLabelsText.Add(customCtrl.LblSkill5.Text)
                data.ExtraLabelsText.Add(customCtrl.LblSkillAll.Text)

                state.MultiLabelControls.Add(data)
            End If
        Next
    Next

    ' Serialize and Write to File
    Dim options As New JsonSerializerOptions With {
        .WriteIndented = True
    }

    Dim jsonString As String = JsonSerializer.Serialize(state, options)
    File.WriteAllText(jsonFilePath, jsonString)

    MessageBox.Show("Configuration saved successfully!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub

Developer technologies | VB

Answer accepted by question author

AI answer

2026-08-13T05:43:36.43+00:00

Use System.Text.Json to deserialize the saved file back into the same AppState type, then rebuild the UI in this order:

  1. Read the JSON file.
  2. Deserialize it into AppState.
  3. Restore the CheckedListBox checked items first.
  4. Let that checked state create any dependent controls.
  5. Walk through SingleLabelControls and MultiLabelControls and either:
    • find the existing control and set its properties, or
    • create the missing control, add it to the correct FlowLayoutPanel, and then set its properties.

System.Text.Json ignores JSON properties that are not represented in the class by default, so deserializing into the same model classes is the right approach.

Example load routine in VB:

Imports System.IO
Imports System.Text.Json

Private Sub LoadDataFromInterface(jsonFilePath As String)
    Dim jsonString = File.ReadAllText(jsonFilePath)
    Dim state = JsonSerializer.Deserialize(Of AppState)(jsonString)

    If state Is Nothing Then Exit Sub

    ' 1. Restore CheckedListBox first
    For i As Integer = 0 To CheckedListBox1.Items.Count - 1
        CheckedListBox1.SetItemChecked(i, False)
    Next

    For Each savedItem In state.CheckedItems
        For i As Integer = 0 To CheckedListBox1.Items.Count - 1
            If CheckedListBox1.Items(i).ToString() = savedItem Then
                CheckedListBox1.SetItemChecked(i, True)
                Exit For
            End If
        Next
    Next

    ' 2. Restore single-label controls
    For Each data In state.SingleLabelControls
        Dim parent = FindFlowLayoutPanelByName(data.ParentName)
        If parent Is Nothing Then Continue For

        Dim ctrl = parent.Controls.OfType(Of LitratureItem)().
            FirstOrDefault(Function(c) c.Name = data.ControlName)

        If ctrl Is Nothing Then
            ctrl = New LitratureItem()
            ctrl.Name = data.ControlName
            parent.Controls.Add(ctrl)
        End If

        ctrl.LblTitle.Text = data.Title
        ctrl.LblTitle.ForeColor = data.Forecolor
        ctrl.PicImage.Tag = data.ImagePath
        ctrl.LblStatus.Text = data.ExtraLabelText
    Next

    ' 3. Restore multi-label controls
    For Each data In state.MultiLabelControls
        Dim parent = FindFlowLayoutPanelByName(data.ParentName)
        If parent Is Nothing Then Continue For

        Dim ctrl = parent.Controls.OfType(Of SkillItem)().
            FirstOrDefault(Function(c) c.Name = data.ControlName)

        If ctrl Is Nothing Then
            ctrl = New SkillItem()
            ctrl.Name = data.ControlName
            parent.Controls.Add(ctrl)
        End If

        ctrl.LblTitle.Text = data.Title
        ctrl.LblTitle.ForeColor = data.Forecolor
        ctrl.PicImage.Tag = data.ImagePath

        If data.ExtraLabelsText.Count > 0 Then ctrl.LblSkill1.Text = data.ExtraLabelsText(0)
        If data.ExtraLabelsText.Count > 1 Then ctrl.LblSkill2.Text = data.ExtraLabelsText(1)
        If data.ExtraLabelsText.Count > 2 Then ctrl.LblSkill3.Text = data.ExtraLabelsText(2)
        If data.ExtraLabelsText.Count > 3 Then ctrl.LblSkill4.Text = data.ExtraLabelsText(3)
        If data.ExtraLabelsText.Count > 4 Then ctrl.LblSkill5.Text = data.ExtraLabelsText(4)
        If data.ExtraLabelsText.Count > 5 Then ctrl.LblSkillAll.Text = data.ExtraLabelsText(5)
    Next
End Sub

Private Function FindFlowLayoutPanelByName(panelName As String) As FlowLayoutPanel
    Return GetAllControls(Me).OfType(Of FlowLayoutPanel)().
        FirstOrDefault(Function(f) f.Name = panelName)
End Function

Important points for this design:

  • The JSON file should store data, not the controls themselves.
  • The control classes (LitratureItem, SkillItem) should be recreated by code, then populated from the deserialized data.
  • Restoring the CheckedListBox first is important because that state may cause the program to create additional controls before the saved control data is applied.
  • If a control already exists, update it instead of creating a duplicate.

For custom controls in Windows Forms, persistence is typically handled by saving settings data and then loading it back into the control. If a custom control needs to persist its own settings, Windows Forms guidance is to load settings during initialization and save them explicitly.

One thing to verify: Color is being serialized in Forecolor. If that does not round-trip the way expected in the JSON, store a simpler value such as a string or ARGB integer in the data class and convert it when applying the properties.

A practical pattern is:

Public Property ForeColorArgb As Integer

Save with:

.ForeColorArgb = customCtrl.LblTitle.ForeColor.ToArgb()

Load with:

ctrl.LblTitle.ForeColor = Color.FromArgb(data.ForeColorArgb)

That keeps the JSON model simple and avoids issues with complex type serialization.


References:

AI-generated content may be incorrect. Read our transparency notes for more information.

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Jay Pham (WICLOUD CORPORATION) 4,355 Reputation points Microsoft External Staff Moderator
    2026-08-13T07:34:21.9533333+00:00

    Hi @Cynolycus ,

    Your JSON structure is suitable for storing application state. Deserialize it into AppState, then let the application recreate or update the Windows Forms controls. Microsoft documents this pattern in How to read JSON as .NET objects.

    Recommended restore sequence

    1. Read and deserialize the selected file into AppState.
    2. Restore checked items and create their dependent controls.
    3. Locate each saved parent by ParentName and each child by ControlName.
    4. Update an existing control; create only when no matching control exists.
    5. Apply text, color, and image values.

    This order is needed because checked items create controls in your application. Adding missing controls through parent.Controls.Add(control) follows Microsoft's documented run-time control creation pattern.

    Use one method such as BuildDependentControlsFromSelection() for control creation. While loading, suppress ItemCheck processing, restore all checked states, and call this method once to avoid duplicates.

    Persist color as ARGB

    Public Property ForeColorArgb As Integer
    
    ' Save
    .ForeColorArgb = customCtrl.LblTitle.ForeColor.ToArgb()
    
    ' Restore
    ctrl.LblTitle.ForeColor = Color.FromArgb(data.ForeColorArgb)
    

    This uses the documented Color.ToArgb and Color.FromArgb(Int32) APIs and avoids relying on the JSON shape of System.Drawing.Color.

    Update or create each control

    Dim parent = FindFlowLayoutPanelByName(data.ParentName)
    If parent Is Nothing Then
        Throw New InvalidDataException($"Panel '{data.ParentName}' was not found.")
    End If
    
    Dim ctrl = parent.Controls.OfType(Of LitratureItem)().
        FirstOrDefault(Function(item) item.Name = data.ControlName)
    
    If ctrl Is Nothing Then
        ctrl = CreateLitratureItem(data.ControlName)
        parent.Controls.Add(ctrl)
    End If
    
    ctrl.LblTitle.Text = data.Title
    ctrl.LblTitle.ForeColor = Color.FromArgb(data.ForeColorArgb)
    ctrl.LblStatus.Text = data.ExtraLabelText
    ctrl.PicImage.Tag = data.ImagePath
    

    CreateLitratureItem should apply the normal defaults and event handlers. Microsoft identifies the control Name as a simple unique settings key. See Settings Keys and Shared Settings.

    Restore the displayed image

    Setting PicImage.Tag restores only the path. Load the image separately:

    ctrl.PicImage.Image?.Dispose()
    ctrl.PicImage.Image = Nothing
    
    If Not String.IsNullOrWhiteSpace(data.ImagePath) AndAlso File.Exists(data.ImagePath) Then
        Using source = Image.FromFile(data.ImagePath)
            ctrl.PicImage.Image = New Bitmap(source)
        End Using
    End If
    

    Copying to a new Bitmap releases the source file after loading. Handle a missing file by skipping it, showing a placeholder, or notifying the user.

    Validation

    Handle IOException, UnauthorizedAccessException, and JsonException. Also reject a missing parent or duplicate (ParentName, ControlName) key.

    1. Save one static control and one checked-item-created control.
    2. Reopen the form and load the JSON.
    3. Confirm the checked state, parent, names, labels, color, image, and control count.
    4. Load the file again and confirm that no duplicate controls appear.

    The Q&A answer follows the correct general approach, but it remains a proposed solution until this round-trip test succeeds. Image loading and checked-item event handling still require application-specific code.

    If you found my response helpful or informative, I would greatly appreciate it if you could provide feedback by interacting with the system or leaving a comment below.

    Thank you.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.