in src/Avalonia.Diagnostics/Diagnostics/Views/PropertyValueEditorView.cs [53:302]
private Control? UpdateControl()
{
_cleanup.Clear();
if (Property?.PropertyType is not { } propertyType)
return null;
if (propertyType == typeof(bool))
return CreateControl<CheckBox>(ToggleButton.IsCheckedProperty);
//TODO: Infinity, NaN not working with NumericUpDown
if (IsValidNumeric(propertyType))
return CreateControl<NumericUpDown>(
NumericUpDown.ValueProperty,
new ValueToDecimalConverter(),
init: n =>
{
n.Increment = 1;
n.NumberFormat = new NumberFormatInfo { NumberDecimalDigits = 0 };
n.ParsingNumberStyle = NumberStyles.Integer;
},
readonlyProperty: NumericUpDown.IsReadOnlyProperty);
if (propertyType == typeof(Color))
{
var el = new Ellipse { Width = 12, Height = 12, VerticalAlignment = VerticalAlignment.Center };
el.Bind(
Shape.FillProperty,
new Binding(nameof(Property.Value)) { Source = Property, Converter = Color2Brush })
.DisposeWith(_cleanup);
var tbl = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
tbl.Bind(
TextBlock.TextProperty,
new Binding(nameof(Property.Value)) { Source = Property })
.DisposeWith(_cleanup);
var sp = new StackPanel
{
Orientation = Orientation.Horizontal,
Spacing = 2,
Children = { el, tbl },
Background = Brushes.Transparent,
Cursor = new Cursor(StandardCursorType.Hand),
IsEnabled = !Property.IsReadonly
};
var cv = new ColorView
{
HexInputAlphaPosition = AlphaComponentPosition.Leading, // Always match XAML
};
cv.Bind(
ColorView.ColorProperty,
new Binding(nameof(Property.Value), BindingMode.TwoWay)
{
Source = Property,
Converter = Color2Brush
})
.DisposeWith(_cleanup);
FlyoutBase.SetAttachedFlyout(sp, new Flyout { Content = cv });
sp.PointerPressed += (_, _) => FlyoutBase.ShowAttachedFlyout(sp);
return sp;
}
if (ImplementsInterface<IBrush>(propertyType))
return CreateControl<BrushEditor>(BrushEditor.BrushProperty);
var isImage = ImplementsInterface<IImage>(propertyType);
var isGeometry = propertyType == typeof(Geometry);
if (isImage || isGeometry)
{
var valueObservable = Property.GetObservable(x => x.Value);
var tbl = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
tbl.Bind(TextBlock.TextProperty,
valueObservable.Select(
value => value switch
{
IImage img => $"{img.Size.Width} x {img.Size.Height}",
Geometry geom => $"{geom.Bounds.Width} x {geom.Bounds.Height}",
_ => "(null)"
}))
.DisposeWith(_cleanup);
var sp = new StackPanel
{
Background = Brushes.Transparent,
Orientation = Orientation.Horizontal,
Spacing = 2,
Children =
{
new Path
{
Data = isImage ? ImageIcon : GeometryIcon,
Fill = Brushes.Gray,
Width = 12,
Height = 12,
Stretch = Stretch.Uniform,
VerticalAlignment = VerticalAlignment.Center
},
tbl
}
};
if (isImage)
{
var previewImage = new Image { Stretch = Stretch.Uniform, Width = 300, Height = 300 };
previewImage
.Bind(Image.SourceProperty, valueObservable)
.DisposeWith(_cleanup);
ToolTip.SetTip(sp, previewImage);
}
else
{
var previewShape = new Path
{
Stretch = Stretch.Uniform,
Fill = Brushes.White,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
previewShape
.Bind(Path.DataProperty, valueObservable)
.DisposeWith(_cleanup);
ToolTip.SetTip(sp, new Border { Child = previewShape, Width = 300, Height = 300 });
}
return sp;
}
if (propertyType.IsEnum)
return CreateControl<ComboBox>(
SelectingItemsControl.SelectedItemProperty, init: c =>
{
c.ItemsSource = Enum.GetValues(propertyType);
});
var tb = CreateControl<CommitTextBox>(
CommitTextBox.CommittedTextProperty,
new TextToValueConverter(),
t =>
{
t.Watermark = "(null)";
},
readonlyProperty: TextBox.IsReadOnlyProperty);
tb.IsReadOnly |= propertyType == typeof(object) ||
!StringConversionHelper.CanConvertFromString(propertyType);
if (!tb.IsReadOnly)
{
tb.GetObservable(TextBox.TextProperty).Subscribe(t =>
{
try
{
if (t != null)
{
StringConversionHelper.FromString(t, propertyType);
}
DataValidationErrors.ClearErrors(tb);
}
catch (Exception ex)
{
DataValidationErrors.SetError(tb, ex.GetBaseException());
}
}).DisposeWith(_cleanup);
}
return tb;
TControl CreateControl<TControl>(AvaloniaProperty valueProperty,
IValueConverter? converter = null,
Action<TControl>? init = null,
AvaloniaProperty? readonlyProperty = null)
where TControl : Control, new()
{
var control = new TControl();
var bindingMode = Property.IsReadonly ? BindingMode.OneWay : BindingMode.TwoWay;
init?.Invoke(control);
control.Bind(valueProperty,
new Binding(nameof(Property.Value), bindingMode)
{
Source = Property,
Converter = converter ?? new ValueConverter(),
ConverterParameter = propertyType
}).DisposeWith(_cleanup);
if (readonlyProperty != null)
{
control[readonlyProperty] = Property.IsReadonly;
}
else
{
control.IsEnabled = !Property.IsReadonly;
}
return control;
}
static bool IsValidNumeric(Type? type)
{
if (type == null || type.IsEnum == true)
{
return false;
}
var typeCode = Type.GetTypeCode(type);
if (typeCode == TypeCode.Object)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
typeCode = Type.GetTypeCode(Nullable.GetUnderlyingType(type));
}
else
{
return false;
}
}
switch (typeCode)
{
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
default:
return false;
}
}
}