ECErsel ÇAKMAK
Back to articles
Desktop EngineeringApr 28, 2026

Saving and Loading Templates With Protobuf

A short practical pattern for storing template content, loading the selected template at startup, and keeping template save and load flows simple.

  • Protobuf
  • Serialization
  • .NET
  • Desktop
  • Templates

Why I prefer a single template snapshot

For template-like assets, I usually want one clear persistence boundary. A template is one logical thing, so save and load become easier when the application treats it as one snapshot instead of spreading it across multiple sidecar files.

This is especially useful in desktop applications where templates carry both visible geometry and supporting data such as snap points, parameters, or small metadata fields.

What I keep inside the template

The template should only contain the data needed to rebuild the design correctly. I prefer keeping the stored shape explicit and small.

  • Entities or geometry data
  • Snap points or interaction helpers
  • Template parameters and user-facing values
  • A small amount of metadata such as name or version
  • Anything required to rebuild the runtime object safely

Serialize the template with Protobuf

A simple DTO-based snapshot is usually enough. The main goal is to serialize one payload that the rest of the application can save, load, move, or cache without extra coordination logic.

csharp

TemplateSerializer.cs

The DTO shape can change, but the useful part is having one clear persistence model for the template.

using ProtoBuf;

[ProtoContract]
public sealed class TemplateFile
{
    [ProtoMember(1)] public string Name { get; set; } = string.Empty;
    [ProtoMember(2)] public List<EntityDto> Entities { get; set; } = new();
    [ProtoMember(3)] public List<SnapPointDto> SnapPoints { get; set; } = new();
    [ProtoMember(4)] public List<TemplateParameterDto> Parameters { get; set; } = new();
}

public static class TemplateSerializer
{
    public static byte[] ToBytes(TemplateFile template)
    {
        using var stream = new MemoryStream();
        Serializer.Serialize(stream, template);
        return stream.ToArray();
    }

    public static TemplateFile FromBytes(byte[] payload)
    {
        using var stream = new MemoryStream(payload, writable: false);
        return Serializer.Deserialize<TemplateFile>(stream);
    }
}

Load the selected template at startup

At startup, I usually read the last selected template key from settings, pull the serialized bytes from storage, and rebuild the template only if that data exists.

csharp

StartupTemplateLoader.cs

The storage layer can be file-based, SQLite-based, or anything else. The app only cares about getting the payload back.

public TemplateFile? LoadStartupTemplate(IAppSettings settings, ITemplateStore store)
{
    if (string.IsNullOrWhiteSpace(settings.LastSelectedTemplate))
    {
        return null;
    }

    byte[]? payload = store.TryLoad(settings.LastSelectedTemplate);
    if (payload is null)
    {
        return null;
    }

    return TemplateSerializer.FromBytes(payload);
}

Save the current template

When the user saves, I prefer building the DTO from the current editor state and writing one payload through a storage abstraction.

csharp

TemplateSaveService.cs

This keeps template building, serialization, and persistence readable instead of scattering save logic across several files.

public void SaveTemplate(TemplateEditorState editorState, ITemplateStore store)
{
    var template = new TemplateFile
    {
        Name = editorState.Name,
        Entities = editorState.Entities.Select(EntityDto.FromDomain).ToList(),
        SnapPoints = editorState.SnapPoints.Select(SnapPointDto.FromDomain).ToList(),
        Parameters = editorState.Parameters.Select(TemplateParameterDto.FromDomain).ToList(),
    };

    store.Save(template.Name, TemplateSerializer.ToBytes(template));
}

A few practical notes

I would keep transient UI state outside the template file. Things like panel layout, temporary selections, or viewport-only flags usually do not belong in the persisted snapshot.

I would also think about versioning early. Once a Protobuf payload becomes a durable file format, backward compatibility matters.

For me, the main benefit of this approach is clarity. Template selection, save, load, move, and caching flows all become easier when the application treats the template as one stored unit.

Share