using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using IBKRTrader.App.Shell;
using IBKRTrader.App.ViewModels;
using IBKRTrader.Core.Modularity;
using IBKRTrader.Core.Settings;
using IBKRTrader.Core.Time;
namespace IBKRTrader.App.Views;
///
/// Core-Ansicht: Einstellungen. Ersetzt das PropertyGrid der WinForms-Fassung durch eine
/// aus den Attributen erzeugte Maske (siehe ).
///
/// Geändert wird direkt auf dem AppSettings -Objekt; „Speichern" schreibt es nach
/// settings.json , „Verwerfen" lädt die Datei neu. Zahlen werden ausdrücklich gegen
/// gelesen – die Datei ist maschinenlesbar und darf
/// nicht von der Kultur des Rechners abhängen.
///
public partial class SettingsWindow : Window
{
private readonly SettingsService _settings;
public SettingsWindow(IModuleUiHost uiHost, SettingsService settings)
{
_settings = settings;
InitializeComponent();
WindowMenu.Wire(this.FindControl
("WindowMenuBar")!, uiHost, "core.settings");
this.FindControl("SaveButton")!.Click += (_, _) =>
{
try
{
_settings.Save();
SetStatus("Gespeichert. Zeitzonen-Änderungen greifen erst nach einem Neustart.");
}
catch (Exception ex) { SetStatus($"Speichern fehlgeschlagen: {ex.Message}"); }
};
this.FindControl("ReloadButton")!.Click += (_, _) =>
{
_settings.Load();
BuildForm();
SetStatus("Gespeicherte Werte neu geladen.");
};
BuildForm();
SetStatus("Bereit.");
}
private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
private void BuildForm()
{
var panel = this.FindControl("SectionPanel")!;
panel.Children.Clear();
foreach (var section in SettingsModelBuilder.Build(_settings.Settings))
{
var grid = new Grid
{
ColumnDefinitions = new ColumnDefinitions("240,*"),
Margin = new Thickness(4, 4, 4, 12)
};
for (var i = 0; i < section.Fields.Count; i++)
{
var field = section.Fields[i];
grid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
var label = new TextBlock
{
Text = field.DisplayName,
Margin = new Thickness(0, 6, 10, 6),
VerticalAlignment = VerticalAlignment.Center
};
if (!string.IsNullOrWhiteSpace(field.Description))
ToolTip.SetTip(label, field.Description);
var editor = CreateEditor(field);
editor.Margin = new Thickness(0, 4, 0, 4);
if (!string.IsNullOrWhiteSpace(field.Description))
ToolTip.SetTip(editor, field.Description);
Grid.SetRow(label, i); Grid.SetColumn(label, 0);
Grid.SetRow(editor, i); Grid.SetColumn(editor, 1);
grid.Children.Add(label);
grid.Children.Add(editor);
}
panel.Children.Add(new Expander
{
Header = section.Title,
IsExpanded = true,
Content = grid,
Margin = new Thickness(0, 0, 0, 6),
HorizontalContentAlignment = HorizontalAlignment.Stretch
});
}
}
/// Wählt das Eingabeelement passend zum Typ des Feldes.
private static Control CreateEditor(SettingsField field)
{
if (field.ValueType == typeof(bool))
{
var check = new CheckBox { IsChecked = (bool?)field.Get() };
check.IsCheckedChanged += (_, _) => field.Set(check.IsChecked == true);
return check;
}
if (field.ValueType.IsEnum)
{
var combo = new ComboBox
{
ItemsSource = Enum.GetValues(field.ValueType),
SelectedItem = field.Get(),
HorizontalAlignment = HorizontalAlignment.Left,
MinWidth = 200
};
combo.SelectionChanged += (_, _) => { if (combo.SelectedItem is not null) field.Set(combo.SelectedItem); };
return combo;
}
if (field.ValueType == typeof(int) || field.ValueType == typeof(long) ||
field.ValueType == typeof(double) || field.ValueType == typeof(decimal))
{
var numeric = new NumericUpDown
{
Value = ToDecimal(field.Get()),
Increment = field.ValueType == typeof(double) || field.ValueType == typeof(decimal) ? 0.5m : 1m,
FormatString = field.ValueType == typeof(double) || field.ValueType == typeof(decimal) ? "0.###" : "0",
HorizontalAlignment = HorizontalAlignment.Left,
MinWidth = 200
};
numeric.ValueChanged += (_, _) =>
{
if (numeric.Value is not { } v) return;
field.Set(Convert.ChangeType(v, field.ValueType, CultureInfo.InvariantCulture));
};
return numeric;
}
var box = new TextBox { Text = field.Get()?.ToString() ?? "" };
// Kennwortfelder (DB-Passwort, Flex-Token) nicht im Klartext anzeigen.
if (field.IsPassword) box.PasswordChar = '•';
box.TextChanged += (_, _) => field.Set(box.Text ?? "");
return box;
}
private static decimal ToDecimal(object? value) =>
value is null ? 0m : Convert.ToDecimal(value, CultureInfo.InvariantCulture);
private void SetStatus(string text) =>
this.FindControl("StatusText")!.Text = $"{text} | {AppTimeZone.Now:HH:mm:ss}";
}