initial commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.PropertyGrid.Models;
|
||||
|
||||
namespace Avalonia.PropertyGrid.Editors;
|
||||
|
||||
/// <summary>
|
||||
/// Registry for custom property editors
|
||||
/// </summary>
|
||||
public static class EditorRegistry
|
||||
{
|
||||
private static readonly Dictionary<Type, Type> _typeEditors = new();
|
||||
private static readonly Dictionary<string, Type> _propertyEditors = new();
|
||||
|
||||
/// <summary>
|
||||
/// Register a custom editor for a specific type
|
||||
/// </summary>
|
||||
public static void RegisterEditor(Type propertyType, Type editorType)
|
||||
{
|
||||
if (!typeof(PropertyEditor).IsAssignableFrom(editorType))
|
||||
{
|
||||
throw new ArgumentException($"Editor type must inherit from PropertyEditor", nameof(editorType));
|
||||
}
|
||||
|
||||
_typeEditors[propertyType] = editorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a custom editor for a specific property by name
|
||||
/// </summary>
|
||||
public static void RegisterEditor(string propertyName, Type editorType)
|
||||
{
|
||||
if (!typeof(PropertyEditor).IsAssignableFrom(editorType))
|
||||
{
|
||||
throw new ArgumentException($"Editor type must inherit from PropertyEditor", nameof(editorType));
|
||||
}
|
||||
|
||||
_propertyEditors[propertyName] = editorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the editor type for a property item
|
||||
/// </summary>
|
||||
public static Type? GetEditorType(PropertyItem item)
|
||||
{
|
||||
// Check for explicit editor type on the item
|
||||
if (item.EditorType != null)
|
||||
{
|
||||
return item.EditorType;
|
||||
}
|
||||
|
||||
// Check for property-specific editor
|
||||
if (_propertyEditors.TryGetValue(item.Name, out var propertyEditor))
|
||||
{
|
||||
return propertyEditor;
|
||||
}
|
||||
|
||||
// Check for type-specific editor
|
||||
if (_typeEditors.TryGetValue(item.PropertyType, out var typeEditor))
|
||||
{
|
||||
return typeEditor;
|
||||
}
|
||||
|
||||
// Return default editor based on type
|
||||
return GetDefaultEditorType(item.PropertyType);
|
||||
}
|
||||
|
||||
private static Type? GetDefaultEditorType(Type propertyType)
|
||||
{
|
||||
if (propertyType == typeof(string))
|
||||
return typeof(StringEditor);
|
||||
|
||||
if (propertyType == typeof(bool))
|
||||
return typeof(BooleanEditor);
|
||||
|
||||
if (propertyType == typeof(int) || propertyType == typeof(long) ||
|
||||
propertyType == typeof(short) || propertyType == typeof(byte) ||
|
||||
propertyType == typeof(uint) || propertyType == typeof(ulong) ||
|
||||
propertyType == typeof(ushort) || propertyType == typeof(sbyte) ||
|
||||
propertyType == typeof(float) || propertyType == typeof(double) ||
|
||||
propertyType == typeof(decimal))
|
||||
return typeof(NumericEditor);
|
||||
|
||||
if (propertyType.IsEnum)
|
||||
return typeof(EnumEditor);
|
||||
|
||||
if (propertyType == typeof(DateTime))
|
||||
return typeof(DateTimeEditor);
|
||||
|
||||
return null; // Will use default TextBox
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.PropertyGrid.Models;
|
||||
|
||||
namespace Avalonia.PropertyGrid.Editors;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for property editors
|
||||
/// </summary>
|
||||
public abstract class PropertyEditor : ContentControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the PropertyItem property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<PropertyItem?> PropertyItemProperty =
|
||||
AvaloniaProperty.Register<PropertyEditor, PropertyItem?>(nameof(PropertyItem));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property item being edited.
|
||||
/// </summary>
|
||||
public PropertyItem? PropertyItem
|
||||
{
|
||||
get => GetValue(PropertyItemProperty);
|
||||
set => SetValue(PropertyItemProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the PropertyItem changes.
|
||||
/// </summary>
|
||||
protected virtual void OnPropertyItemChanged(PropertyItem? oldValue, PropertyItem? newValue)
|
||||
{
|
||||
if (oldValue != null)
|
||||
{
|
||||
UnbindFromProperty(oldValue);
|
||||
}
|
||||
|
||||
if (newValue != null)
|
||||
{
|
||||
BindToProperty(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the editor to the property item.
|
||||
/// </summary>
|
||||
protected abstract void BindToProperty(PropertyItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Unbinds the editor from the property item.
|
||||
/// </summary>
|
||||
protected abstract void UnbindFromProperty(PropertyItem item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor for string properties
|
||||
/// </summary>
|
||||
public class StringEditor : PropertyEditor
|
||||
{
|
||||
private TextBox? _textBox;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
_textBox = new TextBox();
|
||||
Content = _textBox;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnbindFromProperty(PropertyItem item)
|
||||
{
|
||||
_textBox?.ClearValue(TextBox.TextProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor for numeric properties
|
||||
/// </summary>
|
||||
public class NumericEditor : PropertyEditor
|
||||
{
|
||||
private NumericUpDown? _numericUpDown;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
_numericUpDown = new NumericUpDown
|
||||
{
|
||||
Increment = 1,
|
||||
FormatString = "0.##"
|
||||
};
|
||||
Content = _numericUpDown;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnbindFromProperty(PropertyItem item)
|
||||
{
|
||||
_numericUpDown?.ClearValue(NumericUpDown.ValueProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor for boolean properties
|
||||
/// </summary>
|
||||
public class BooleanEditor : PropertyEditor
|
||||
{
|
||||
private CheckBox? _checkBox;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
_checkBox = new CheckBox
|
||||
{
|
||||
HorizontalAlignment = Layout.HorizontalAlignment.Left
|
||||
};
|
||||
Content = _checkBox;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnbindFromProperty(PropertyItem item)
|
||||
{
|
||||
_checkBox?.ClearValue(CheckBox.IsCheckedProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor for enum properties
|
||||
/// </summary>
|
||||
public class EnumEditor : PropertyEditor
|
||||
{
|
||||
private ComboBox? _comboBox;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
_comboBox = new ComboBox();
|
||||
Content = _comboBox;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnbindFromProperty(PropertyItem item)
|
||||
{
|
||||
_comboBox?.ClearValue(ComboBox.SelectedItemProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor for DateTime properties
|
||||
/// </summary>
|
||||
public class DateTimeEditor : PropertyEditor
|
||||
{
|
||||
private DatePicker? _datePicker;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
_datePicker = new DatePicker();
|
||||
Content = _datePicker;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnbindFromProperty(PropertyItem item)
|
||||
{
|
||||
_datePicker?.ClearValue(DatePicker.SelectedDateProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for DateTime to DateTimeOffset
|
||||
/// </summary>
|
||||
internal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user