Files

312 lines
8.5 KiB
C#
Raw Permalink Normal View History

2026-08-10 14:45:47 +02:00
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
namespace Avalonia.PropertyGrid.Models;
/// <summary>
/// Represents a single property item in the PropertyGrid
/// </summary>
public class PropertyItem : INotifyPropertyChanged
{
private object? _value;
private bool _isExpanded;
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// The property name displayed in the grid
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// The display name (from DisplayAttribute or DisplayNameAttribute)
/// </summary>
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// The property description
/// </summary>
public string? Description { get; set; }
/// <summary>
/// The category this property belongs to
/// </summary>
public string Category { get; set; } = "Misc";
/// <summary>
/// The property value
/// </summary>
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
}
}
}
}
}
/// <summary>
/// Numeric value for NumericUpDown binding (converts to/from decimal)
/// </summary>
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
}
}
}
/// <summary>
/// Color value for ColorPicker binding
/// </summary>
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;
}
}
/// <summary>
/// Boolean value for RadioButton binding
/// </summary>
public bool BooleanValue
{
get
{
if (Value is bool boolVal)
{
return boolVal;
}
return false;
}
set
{
Value = value;
}
}
/// <summary>
/// String representation of the value for display
/// </summary>
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('#'))
2026-08-10 14:45:47 +02:00
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
}
}
}
}
/// <summary>
/// The property type
/// </summary>
public Type PropertyType { get; set; } = typeof(object);
/// <summary>
/// Whether the property is read-only
/// </summary>
public bool IsReadOnly { get; set; }
/// <summary>
/// Whether the property is browsable (visible)
/// </summary>
public bool IsBrowsable { get; set; } = true;
/// <summary>
/// Whether the property is expandable (has sub-properties)
/// </summary>
public bool IsExpandable { get; set; }
/// <summary>
/// Whether the property is currently expanded
/// </summary>
public bool IsExpanded
{
get => _isExpanded;
set
{
if (_isExpanded != value)
{
_isExpanded = value;
OnPropertyChanged(nameof(IsExpanded));
}
}
}
/// <summary>
/// Child properties (for complex types)
/// </summary>
public List<PropertyItem> Children { get; set; } = new();
/// <summary>
/// The PropertyInfo reflection object
/// </summary>
public PropertyInfo? PropertyInfo { get; set; }
/// <summary>
/// The object instance this property belongs to
/// </summary>
public object? Instance { get; set; }
/// <summary>
/// Custom editor type for this property
/// </summary>
public Type? EditorType { get; set; }
/// <summary>
/// The indentation level (for nested properties)
/// </summary>
public int IndentLevel { get; set; }
/// <summary>
/// Custom converter for this property
/// </summary>
public TypeConverter? Converter { get; set; }
/// <summary>
/// Order/priority for display
/// </summary>
public int Order { get; set; }
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Refreshes the value from the source instance
/// </summary>
public void RefreshValue()
{
if (PropertyInfo != null && Instance != null)
{
try
{
_value = PropertyInfo.GetValue(Instance);
OnPropertyChanged(nameof(Value));
OnPropertyChanged(nameof(ValueString));
}
catch
{
// Ignore errors during refresh
}
}
}
}