Files
openF1-Manager/scripts/FormBinder.cs
2025-09-07 14:57:10 +02:00

89 lines
2.8 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Godot;
public sealed class FormBinder<TViewModel> where TViewModel : INotifyPropertyChanged
{
private readonly TViewModel _vm;
private readonly Control _root;
private readonly Dictionary<LineEdit, PropertyInfo> _lineToProp = new();
private readonly Dictionary<string, LineEdit> _propNameToLine = new(StringComparer.OrdinalIgnoreCase);
private bool _suppress;
public FormBinder(TViewModel vm, Control root)
{
_vm = vm;
_root = root;
}
public FormBinder<TViewModel> Bind()
{
Discover();
_vm.PropertyChanged += OnPropertyChanged;
foreach (var (line, prop) in _lineToProp)
{
line.TextChanged += (string text) =>
{
if (_suppress) return;
if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(string))
prop.SetValue(_vm, text);
};
}
// Initial sync UI <- VM
PushAllFromVm();
return this;
}
private void Discover()
{
var props = typeof(TViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var propMap = new Dictionary<string, PropertyInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var p in props) if (p.CanRead || p.CanWrite) propMap[p.Name] = p;
void Walk(Node n)
{
if (n is LineEdit le)
{
// Prefer explicit metadata "vm_prop" to map to a property name; fallback to node name.
string propName = le.HasMeta("vm_prop") ? (string)le.GetMeta("vm_prop") : StripSuffix(le.Name, "LineEdit");
if (propMap.TryGetValue(propName, out var pi))
{
_lineToProp[le] = pi;
_propNameToLine[propName] = le;
}
}
foreach (var c in n.GetChildren())
if (c is Node child) Walk(child);
}
Walk(_root);
}
private static string StripSuffix(string s, string suffix) =>
s.EndsWith(suffix, StringComparison.Ordinal) ? s[..^suffix.Length] : s;
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == null) return;
if (_propNameToLine.TryGetValue(e.PropertyName, out var le))
{
_suppress = true;
try { le.Text = _lineToProp[le].GetValue(_vm)?.ToString() ?? string.Empty; }
finally { _suppress = false; }
}
}
public void PushAllFromVm()
{
_suppress = true;
try
{
foreach (var (line, prop) in _lineToProp)
line.Text = prop.GetValue(_vm)?.ToString() ?? string.Empty;
}
finally { _suppress = false; }
}
}