using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Reflection; namespace Avalonia.PropertyGrid.Models; /// /// Represents a single property item in the PropertyGrid /// public class PropertyItem : INotifyPropertyChanged { private object? _value; private bool _isExpanded; /// /// Occurs when a property value changes. /// public event PropertyChangedEventHandler? PropertyChanged; /// /// The property name displayed in the grid /// public string Name { get; set; } = string.Empty; /// /// The display name (from DisplayAttribute or DisplayNameAttribute) /// public string DisplayName { get; set; } = string.Empty; /// /// The property description /// public string? Description { get; set; } /// /// The category this property belongs to /// public string Category { get; set; } = "Misc"; /// /// The property value /// public object? Value { get => _value; set { if (_value != value) { _value = value; OnPropertyChanged(nameof(Value)); OnPropertyChanged(nameof(ValueString)); OnPropertyChanged(nameof(NumericValue)); OnPropertyChanged(nameof(ColorValue)); OnPropertyChanged(nameof(BooleanValue)); // Update the actual object property if (PropertyInfo != null && Instance != null) { try { PropertyInfo.SetValue(Instance, value); } catch { // Ignore errors during setting } } } } } /// /// Numeric value for NumericUpDown binding (converts to/from decimal) /// public decimal NumericValue { get { if (Value == null) return 0; try { return Convert.ToDecimal(Value, CultureInfo.InvariantCulture); } catch { return 0; } } set { try { // Convert decimal to the actual property type object? converted = null; if (PropertyType == typeof(int)) converted = (int)value; else if (PropertyType == typeof(long)) converted = (long)value; else if (PropertyType == typeof(short)) converted = (short)value; else if (PropertyType == typeof(byte)) converted = (byte)value; else if (PropertyType == typeof(uint)) converted = (uint)value; else if (PropertyType == typeof(ulong)) converted = (ulong)value; else if (PropertyType == typeof(ushort)) converted = (ushort)value; else if (PropertyType == typeof(sbyte)) converted = (sbyte)value; else if (PropertyType == typeof(float)) converted = (float)value; else if (PropertyType == typeof(double)) converted = (double)value; else if (PropertyType == typeof(decimal)) converted = value; if (converted != null) { Value = converted; } } catch { // Ignore conversion errors } } } /// /// Color value for ColorPicker binding /// public Media.Color ColorValue { get { if (Value is Media.Color color) { System.Diagnostics.Debug.WriteLine($"ColorValue getter: returning {color}"); return color; } System.Diagnostics.Debug.WriteLine($"ColorValue getter: Value is not Color (is {Value?.GetType().Name ?? "null"}), returning Black"); return Media.Colors.Black; } set { System.Diagnostics.Debug.WriteLine($"ColorValue setter: setting to {value}"); Value = value; } } /// /// Boolean value for RadioButton binding /// public bool BooleanValue { get { if (Value is bool boolVal) { return boolVal; } return false; } set { Value = value; } } /// /// String representation of the value for display /// public string ValueString { get { if (Value is Media.Color color) { return $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; } return Value?.ToString() ?? string.Empty; } set { // Try to parse as color if the property type is Color if (PropertyType == typeof(Media.Color)) { try { var hexString = value?.Trim() ?? ""; if (!hexString.StartsWith("#")) hexString = "#" + hexString; Value = Media.Color.Parse(hexString); } catch { // If parsing fails, ignore } } else { // For other types, try to convert the string to the property type try { if (!string.IsNullOrEmpty(value)) { Value = Convert.ChangeType(value, PropertyType, CultureInfo.InvariantCulture); } } catch { // If conversion fails, ignore } } } } /// /// The property type /// public Type PropertyType { get; set; } = typeof(object); /// /// Whether the property is read-only /// public bool IsReadOnly { get; set; } /// /// Whether the property is browsable (visible) /// public bool IsBrowsable { get; set; } = true; /// /// Whether the property is expandable (has sub-properties) /// public bool IsExpandable { get; set; } /// /// Whether the property is currently expanded /// public bool IsExpanded { get => _isExpanded; set { if (_isExpanded != value) { _isExpanded = value; OnPropertyChanged(nameof(IsExpanded)); } } } /// /// Child properties (for complex types) /// public List Children { get; set; } = new(); /// /// The PropertyInfo reflection object /// public PropertyInfo? PropertyInfo { get; set; } /// /// The object instance this property belongs to /// public object? Instance { get; set; } /// /// Custom editor type for this property /// public Type? EditorType { get; set; } /// /// The indentation level (for nested properties) /// public int IndentLevel { get; set; } /// /// Custom converter for this property /// public TypeConverter? Converter { get; set; } /// /// Order/priority for display /// public int Order { get; set; } /// /// Raises the PropertyChanged event. /// protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// /// Refreshes the value from the source instance /// public void RefreshValue() { if (PropertyInfo != null && Instance != null) { try { _value = PropertyInfo.GetValue(Instance); OnPropertyChanged(nameof(Value)); OnPropertyChanged(nameof(ValueString)); } catch { // Ignore errors during refresh } } } }