using System; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.PropertyGrid.Models; namespace Avalonia.PropertyGrid.Editors; /// /// Base class for property editors /// public abstract class PropertyEditor : ContentControl { /// /// Defines the PropertyItem property. /// public static readonly StyledProperty PropertyItemProperty = AvaloniaProperty.Register(nameof(PropertyItem)); /// /// Gets or sets the property item being edited. /// public PropertyItem? PropertyItem { get => GetValue(PropertyItemProperty); set => SetValue(PropertyItemProperty, value); } /// /// Called when the PropertyItem changes. /// protected virtual void OnPropertyItemChanged(PropertyItem? oldValue, PropertyItem? newValue) { if (oldValue != null) { UnbindFromProperty(oldValue); } if (newValue != null) { BindToProperty(newValue); } } /// /// Binds the editor to the property item. /// protected abstract void BindToProperty(PropertyItem item); /// /// Unbinds the editor from the property item. /// protected abstract void UnbindFromProperty(PropertyItem item); } /// /// Editor for string properties /// public class StringEditor : PropertyEditor { private TextBox? _textBox; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); _textBox = new TextBox(); Content = _textBox; } /// protected override void BindToProperty(PropertyItem item) { if (_textBox != null) { _textBox.Bind(TextBox.TextProperty, new Avalonia.Data.Binding(nameof(PropertyItem.Value)) { Source = item, Mode = Avalonia.Data.BindingMode.TwoWay }); _textBox.IsReadOnly = item.IsReadOnly; } } /// protected override void UnbindFromProperty(PropertyItem item) { _textBox?.ClearValue(TextBox.TextProperty); } } /// /// Editor for numeric properties /// public class NumericEditor : PropertyEditor { private NumericUpDown? _numericUpDown; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); _numericUpDown = new NumericUpDown { Increment = 1, FormatString = "0.##" }; Content = _numericUpDown; } /// protected override void BindToProperty(PropertyItem item) { if (_numericUpDown != null) { _numericUpDown.Bind(NumericUpDown.ValueProperty, new Avalonia.Data.Binding(nameof(PropertyItem.Value)) { Source = item, Mode = Avalonia.Data.BindingMode.TwoWay }); _numericUpDown.IsReadOnly = item.IsReadOnly; // Adjust increment based on type if (item.PropertyType == typeof(int) || item.PropertyType == typeof(long)) { _numericUpDown.Increment = 1; _numericUpDown.FormatString = "0"; } else if (item.PropertyType == typeof(double) || item.PropertyType == typeof(float)) { _numericUpDown.Increment = 0.1m; _numericUpDown.FormatString = "0.00"; } } } /// protected override void UnbindFromProperty(PropertyItem item) { _numericUpDown?.ClearValue(NumericUpDown.ValueProperty); } } /// /// Editor for boolean properties /// public class BooleanEditor : PropertyEditor { private CheckBox? _checkBox; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); _checkBox = new CheckBox { HorizontalAlignment = Layout.HorizontalAlignment.Left }; Content = _checkBox; } /// protected override void BindToProperty(PropertyItem item) { if (_checkBox != null) { _checkBox.Bind(CheckBox.IsCheckedProperty, new Avalonia.Data.Binding(nameof(PropertyItem.Value)) { Source = item, Mode = Avalonia.Data.BindingMode.TwoWay }); _checkBox.IsEnabled = !item.IsReadOnly; } } /// protected override void UnbindFromProperty(PropertyItem item) { _checkBox?.ClearValue(CheckBox.IsCheckedProperty); } } /// /// Editor for enum properties /// public class EnumEditor : PropertyEditor { private ComboBox? _comboBox; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); _comboBox = new ComboBox(); Content = _comboBox; } /// protected override void BindToProperty(PropertyItem item) { if (_comboBox != null && item.PropertyType.IsEnum) { _comboBox.ItemsSource = Enum.GetValues(item.PropertyType); _comboBox.Bind(ComboBox.SelectedItemProperty, new Avalonia.Data.Binding(nameof(PropertyItem.Value)) { Source = item, Mode = Avalonia.Data.BindingMode.TwoWay }); _comboBox.IsEnabled = !item.IsReadOnly; } } /// protected override void UnbindFromProperty(PropertyItem item) { _comboBox?.ClearValue(ComboBox.SelectedItemProperty); } } /// /// Editor for DateTime properties /// public class DateTimeEditor : PropertyEditor { private DatePicker? _datePicker; /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); _datePicker = new DatePicker(); Content = _datePicker; } /// protected override void BindToProperty(PropertyItem item) { if (_datePicker != null) { _datePicker.Bind(DatePicker.SelectedDateProperty, new Avalonia.Data.Binding(nameof(PropertyItem.Value)) { Source = item, Mode = Avalonia.Data.BindingMode.TwoWay, Converter = new DateTimeToDateTimeOffsetConverter() }); _datePicker.IsEnabled = !item.IsReadOnly; } } /// protected override void UnbindFromProperty(PropertyItem item) { _datePicker?.ClearValue(DatePicker.SelectedDateProperty); } } /// /// Converter for DateTime to DateTimeOffset /// internal sealed class DateTimeToDateTimeOffsetConverter : Avalonia.Data.Converters.IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture) { if (value is DateTime dt) { return new DateTimeOffset(dt); } return null; } public object? ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture) { if (value is DateTimeOffset dto) { return dto.DateTime; } return null; } }