From 9250ccb18afb5d538b6e300a05c606da2d38d3a6 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 8 Oct 2022 13:35:13 -0400 Subject: [PATCH 01/40] Add missing base constructor call --- .../ColorSpectrum/ColorSpectrum.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index bd44161a42..200a652d3b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -95,7 +95,7 @@ namespace Avalonia.Controls.Primitives /// /// Initializes a new instance of the class. /// - public ColorSpectrum() + public ColorSpectrum() : base() { _shapeFromLastBitmapCreation = Shape; _componentsFromLastBitmapCreation = Components; From 5c3a56a9533eab9060a216831df2a9a8c5e08e12 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 8 Oct 2022 14:25:02 -0400 Subject: [PATCH 02/40] Fix the color spectrum selection ellipse position Specifically, when updated and not part of the visual tree --- .../ColorSpectrum/ColorSpectrum.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 200a652d3b..9261e5892d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -171,6 +171,18 @@ namespace Avalonia.Controls.Primitives { base.OnAttachedToVisualTree(e); + // If the color was updated while this ColorSpectrum was not part of the visual tree, + // the selection ellipse may be in an incorrect position. This is because the spectrum + // renders based on layout scaling to avoid color banding; however, layout scale is only + // available when the control is attached to the visual tree. The ColorSpectrum's color + // may be updated from code-behind or from binding with another control when it's not + // part of the visual tree. + // + // See discussion: https://github.com/AvaloniaUI/Avalonia/discussions/9077 + // + // To work-around this issue the selection ellipse is refreshed here. + UpdateEllipse(); + // OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here } @@ -832,6 +844,8 @@ namespace Avalonia.Controls.Primitives } // Remember the bitmap size follows physical device pixels + // Warning: LayoutHelper.GetLayoutScale() doesn't work unless the control is visible + // This will not be true in all cases if the color is updated from another control or code-behind var scale = LayoutHelper.GetLayoutScale(this); Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2)); Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2)); From 55e27213f958304679d449ef4aeb25ca9e8743ea Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 8 Oct 2022 14:25:26 -0400 Subject: [PATCH 03/40] Comment additional ColorSpectrum methods --- .../ColorSpectrum/ColorSpectrum.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 9261e5892d..f272ce850e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -600,6 +600,10 @@ namespace Avalonia.Controls.Primitives RaiseColorChanged(); } + /// + /// Updates the selected and based on a point within the color spectrum. + /// + /// The point on the spectrum representing the color. private void UpdateColorFromPoint(PointerPoint point) { // If we haven't initialized our HSV value array yet, then we should just ignore any user input - @@ -676,6 +680,9 @@ namespace Avalonia.Controls.Primitives UpdateColor(hsvAtPoint); } + /// + /// Updates the position of the selection ellipse on the spectrum which indicates the selected color. + /// private void UpdateEllipse() { if (_selectionEllipsePanel == null) From 8b070919398878acda991efc0140f7a02cd2a8b7 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 9 Oct 2022 12:21:08 -0400 Subject: [PATCH 04/40] Fix formatting --- .../Themes/Fluent/ColorView.xaml | 30 +++++++++---------- .../Themes/Simple/ColorView.xaml | 30 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml index 16bc2acdd1..cf836aff4a 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml @@ -241,23 +241,21 @@ - + - + - + - + Date: Sun, 2 Oct 2022 11:51:25 -0400 Subject: [PATCH 05/40] Implement WriteableBitmap caching/reuse and disposal in ColorSlider --- .../ColorSlider/ColorSlider.cs | 42 ++++++++++++++++++- .../Helpers/ColorPickerHelpers.cs | 19 ++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index b662d20223..4e907f610d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -2,6 +2,7 @@ using Avalonia.Controls.Metadata; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Media.Imaging; using Avalonia.Utilities; namespace Avalonia.Controls.Primitives @@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives protected bool ignorePropertyChanged = false; + private WriteableBitmap? _backgroundBitmap; + /// /// Initializes a new instance of the class. /// @@ -38,6 +41,26 @@ namespace Avalonia.Controls.Primitives { } + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + // Bitmaps were released when detached from the visual tree so they must be re-built + UpdateBackground(); + } + + /// + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + // Clean-up all bitmaps + // https://github.com/AvaloniaUI/Avalonia/issues/9051 + _backgroundBitmap?.Dispose(); + _backgroundBitmap = null; + } + /// /// Updates the visual state of the control by applying latest PseudoClasses. /// @@ -110,7 +133,19 @@ namespace Avalonia.Controls.Primitives if (bitmap != null) { - Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight)); + if (_backgroundBitmap != null) + { + // Re-use the existing WriteableBitmap + // This assumes the height, width and byte counts are the same and must be set to null + // elsewhere if that assumption is ever not true. + ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bitmap); + } + else + { + _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight); + } + + Background = new ImageBrush(_backgroundBitmap); } } } @@ -399,6 +434,11 @@ namespace Avalonia.Controls.Primitives } else if (change.Property == BoundsProperty) { + // If the control's overall dimensions have changed the background bitmap size also needs to change. + // This means the existing bitmap must be released to be recreated correctly in UpdateBackground(). + _backgroundBitmap?.Dispose(); + _backgroundBitmap = null; + UpdateBackground(); } else if (change.Property == ValueProperty || diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs index c1904a3c30..f2683e4677 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs @@ -617,7 +617,6 @@ namespace Avalonia.Controls.Primitives PixelFormat.Bgra8888, AlphaFormat.Premul); - // Warning: This is highly questionable using (var frameBuffer = bitmap.Lock()) { Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); @@ -625,5 +624,23 @@ namespace Avalonia.Controls.Primitives return bitmap; } + + /// + /// Updates the given with new, raw BGRA pre-multiplied alpha pixel data. + /// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally. + /// + /// The existing to update. + /// The bitmap (in raw BGRA pre-multiplied alpha pixels). + public static void UpdateBitmapFromPixelData( + WriteableBitmap bitmap, + IList bgraPixelData) + { + using (var frameBuffer = bitmap.Lock()) + { + Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); + } + + return; + } } } From ca7543f1d613f9d7b9f2cfed978f8e23379d6684 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 2 Oct 2022 14:24:10 -0400 Subject: [PATCH 06/40] Add new ArrayList to avoid an extra copy when creating spectrum bitmaps --- .../ColorSlider/ColorSlider.cs | 8 +-- .../ColorSpectrum/ColorSpectrum.cs | 61 +++++++++------- .../Helpers/ArrayList.cs | 71 +++++++++++++++++++ .../Helpers/ColorPickerHelpers.cs | 18 ++--- 4 files changed, 118 insertions(+), 40 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 4e907f610d..8bcdef5558 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -121,7 +121,7 @@ namespace Avalonia.Controls.Primitives if (pixelWidth != 0 && pixelHeight != 0) { - var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync( + ArrayList bgraPixelData = await ColorPickerHelpers.CreateComponentBitmapAsync( pixelWidth, pixelHeight, Orientation, @@ -131,18 +131,18 @@ namespace Avalonia.Controls.Primitives IsAlphaMaxForced, IsSaturationValueMaxForced); - if (bitmap != null) + if (bgraPixelData != null) { if (_backgroundBitmap != null) { // Re-use the existing WriteableBitmap // This assumes the height, width and byte counts are the same and must be set to null // elsewhere if that assumption is ever not true. - ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bitmap); + ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData); } else { - _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight); + _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight); } Background = new ImageBrush(_backgroundBitmap); diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index f272ce850e..3604894833 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -994,13 +994,13 @@ namespace Avalonia.Controls.Primitives // The middle 4 are only needed and used in the case of hue as the third dimension. // Saturation and luminosity need only a min and max. - List bgraMinPixelData = new List(); - List bgraMiddle1PixelData = new List(); - List bgraMiddle2PixelData = new List(); - List bgraMiddle3PixelData = new List(); - List bgraMiddle4PixelData = new List(); - List bgraMaxPixelData = new List(); - List newHsvValues = new List(); + ArrayList bgraMinPixelData; + ArrayList bgraMiddle1PixelData; + ArrayList bgraMiddle2PixelData; + ArrayList bgraMiddle3PixelData; + ArrayList bgraMiddle4PixelData; + ArrayList bgraMaxPixelData; + List newHsvValues; // In Avalonia, Bounds returns the actual device-independent pixel size of a control. // However, this is not necessarily the size of the control rendered on a display. @@ -1011,20 +1011,27 @@ namespace Avalonia.Controls.Primitives int pixelDimension = (int)Math.Round(minDimension * scale); var pixelCount = pixelDimension * pixelDimension; var pixelDataSize = pixelCount * 4; - bgraMinPixelData.Capacity = pixelDataSize; + + bgraMinPixelData = new ArrayList(pixelDataSize); + bgraMaxPixelData = new ArrayList(pixelDataSize); + newHsvValues = new List(pixelCount); // We'll only save pixel data for the middle bitmaps if our third dimension is hue. if (components == ColorSpectrumComponents.ValueSaturation || components == ColorSpectrumComponents.SaturationValue) { - bgraMiddle1PixelData.Capacity = pixelDataSize; - bgraMiddle2PixelData.Capacity = pixelDataSize; - bgraMiddle3PixelData.Capacity = pixelDataSize; - bgraMiddle4PixelData.Capacity = pixelDataSize; + bgraMiddle1PixelData = new ArrayList(pixelDataSize); + bgraMiddle2PixelData = new ArrayList(pixelDataSize); + bgraMiddle3PixelData = new ArrayList(pixelDataSize); + bgraMiddle4PixelData = new ArrayList(pixelDataSize); + } + else + { + bgraMiddle1PixelData = new ArrayList(0); + bgraMiddle2PixelData = new ArrayList(0); + bgraMiddle3PixelData = new ArrayList(0); + bgraMiddle4PixelData = new ArrayList(0); } - - bgraMaxPixelData.Capacity = pixelDataSize; - newHsvValues.Capacity = pixelCount; await Task.Run(() => { @@ -1132,12 +1139,12 @@ namespace Avalonia.Controls.Primitives double maxSaturation, double minValue, double maxValue, - List bgraMinPixelData, - List bgraMiddle1PixelData, - List bgraMiddle2PixelData, - List bgraMiddle3PixelData, - List bgraMiddle4PixelData, - List bgraMaxPixelData, + ArrayList bgraMinPixelData, + ArrayList bgraMiddle1PixelData, + ArrayList bgraMiddle2PixelData, + ArrayList bgraMiddle3PixelData, + ArrayList bgraMiddle4PixelData, + ArrayList bgraMaxPixelData, List newHsvValues) { double hMin = minHue; @@ -1292,12 +1299,12 @@ namespace Avalonia.Controls.Primitives double maxSaturation, double minValue, double maxValue, - List bgraMinPixelData, - List bgraMiddle1PixelData, - List bgraMiddle2PixelData, - List bgraMiddle3PixelData, - List bgraMiddle4PixelData, - List bgraMaxPixelData, + ArrayList bgraMinPixelData, + ArrayList bgraMiddle1PixelData, + ArrayList bgraMiddle2PixelData, + ArrayList bgraMiddle3PixelData, + ArrayList bgraMiddle4PixelData, + ArrayList bgraMaxPixelData, List newHsvValues) { double hMin = minHue; diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs new file mode 100644 index 0000000000..0b4c2f8579 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs @@ -0,0 +1,71 @@ +namespace Avalonia.Controls.Primitives +{ + /// + /// A thin wrapper over an that allows some additional list-like functionality. + /// + /// + /// This is only for internal ColorPicker-related functionality and should not be used elsewhere. + /// It is added for performance to enjoy the simplicity of the IList.Add() method without requiring + /// an additional copy to turn a list into an array for bitmaps. + /// + /// The type of items in the array. + internal class ArrayList + { + private int _nextIndex = 0; + + /// + /// Initializes a new instance of the class. + /// + public ArrayList(int capacity) + { + Capacity = capacity; + Array = new T[capacity]; + } + + /// + /// Provides access to the underlying array by index. + /// This exists for simplification and the property + /// may also be used. + /// + /// The index of the item to get or set. + /// The item at the given index. + public T this[int i] + { + get => Array[i]; + set => Array[i] = value; + } + + /// + /// Gets the underlying array. + /// + public T[] Array { get; private set; } + + /// + /// Gets the fixed capacity/size of the array. + /// This must be set during construction. + /// + public int Capacity { get; private set; } + + /// + /// Adds the given item to the array at the next available index. + /// WARNING: This must be used carefully and only once, in sequence. + /// + /// The item to add. + public void Add(T item) + { + if (_nextIndex >= 0 && + _nextIndex < Capacity) + { + Array[_nextIndex] = item; + _nextIndex++; + } + else + { + // If necessary an exception could be thrown here + // throw new IndexOutOfRangeException(); + } + + return; + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs index f2683e4677..2378813f52 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs @@ -37,7 +37,7 @@ namespace Avalonia.Controls.Primitives /// during calculation with the HSVA color model. /// This will ensure colors are always discernible regardless of saturation/value. /// A new bitmap representing a gradient of color component values. - public static async Task CreateComponentBitmapAsync( + public static async Task> CreateComponentBitmapAsync( int width, int height, Orientation orientation, @@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives { if (width == 0 || height == 0) { - return new byte[0]; + return new ArrayList(0); } - var bitmap = await Task.Run(() => + var bitmap = await Task.Run>(() => { int pixelDataIndex = 0; double componentStep; - byte[] bgraPixelData; + ArrayList bgraPixelData; Color baseRgbColor = Colors.White; Color rgbColor; int bgraPixelDataHeight; @@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives // Allocate the buffer // BGRA formatted color components 1 byte each (4 bytes in a pixel) - bgraPixelData = new byte[width * height * 4]; + bgraPixelData = new ArrayList(width * height * 4); bgraPixelDataHeight = height * 4; bgraPixelDataWidth = width * 4; @@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives /// The pixel height of the bitmap. /// A new . public static WriteableBitmap CreateBitmapFromPixelData( - IList bgraPixelData, + ArrayList bgraPixelData, int pixelWidth, int pixelHeight) { @@ -619,7 +619,7 @@ namespace Avalonia.Controls.Primitives using (var frameBuffer = bitmap.Lock()) { - Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); + Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length); } return bitmap; @@ -633,11 +633,11 @@ namespace Avalonia.Controls.Primitives /// The bitmap (in raw BGRA pre-multiplied alpha pixels). public static void UpdateBitmapFromPixelData( WriteableBitmap bitmap, - IList bgraPixelData) + ArrayList bgraPixelData) { using (var frameBuffer = bitmap.Lock()) { - Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); + Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length); } return; From 98a58217ce22d55f9343216fd4fbc727618c7306 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 2 Oct 2022 14:25:24 -0400 Subject: [PATCH 07/40] Fix ColorSlider update/refresh for all property changes --- .../ColorSlider/ColorSlider.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 8bcdef5558..6f20238b70 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -385,11 +385,11 @@ namespace Avalonia.Controls.Primitives return; } - // Always keep the two color properties in sync if (change.Property == ColorProperty) { ignorePropertyChanged = true; + // Always keep the two color properties in sync HsvColor = Color.ToHsv(); SetColorToSliderValues(); @@ -402,7 +402,10 @@ namespace Avalonia.Controls.Primitives ignorePropertyChanged = false; } - else if (change.Property == ColorModelProperty) + else if (change.Property == ColorComponentProperty || + change.Property == ColorModelProperty || + change.Property == IsAlphaMaxForcedProperty || + change.Property == IsSaturationValueMaxForcedProperty) { ignorePropertyChanged = true; @@ -416,6 +419,7 @@ namespace Avalonia.Controls.Primitives { ignorePropertyChanged = true; + // Always keep the two color properties in sync Color = HsvColor.ToRgb(); SetColorToSliderValues(); @@ -440,6 +444,7 @@ namespace Avalonia.Controls.Primitives _backgroundBitmap = null; UpdateBackground(); + UpdatePseudoClasses(); } else if (change.Property == ValueProperty || change.Property == MinimumProperty || From 4f1d315b4b11cf84ca7c3b4ffc65c41d35e24436 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 2 Oct 2022 14:26:28 -0400 Subject: [PATCH 08/40] Make all ColorSpectrum bitmaps globally accessible by the control for future disposal --- .../ColorSpectrum/ColorSpectrum.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 3604894833..6b06e1eac1 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -73,6 +73,8 @@ namespace Avalonia.Controls.Primitives private WriteableBitmap? _saturationMaximumBitmap; private WriteableBitmap? _valueBitmap; + private WriteableBitmap? _minBitmap; + private WriteableBitmap? _maxBitmap; // Fields used by UpdateEllipse() to ensure that it's using the data // associated with the last call to CreateBitmapsAndColorMap(), @@ -1084,28 +1086,28 @@ namespace Avalonia.Controls.Primitives ColorSpectrumComponents components2 = Components; - WriteableBitmap minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight); - WriteableBitmap maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight); + _minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight); + _maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight); switch (components2) { case ColorSpectrumComponents.HueValue: case ColorSpectrumComponents.ValueHue: - _saturationMinimumBitmap = minBitmap; - _saturationMaximumBitmap = maxBitmap; + _saturationMinimumBitmap = _minBitmap; + _saturationMaximumBitmap = _maxBitmap; break; case ColorSpectrumComponents.HueSaturation: case ColorSpectrumComponents.SaturationHue: - _valueBitmap = maxBitmap; + _valueBitmap = _maxBitmap; break; case ColorSpectrumComponents.ValueSaturation: case ColorSpectrumComponents.SaturationValue: - _hueRedBitmap = minBitmap; + _hueRedBitmap = _minBitmap; _hueYellowBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle1PixelData, pixelWidth, pixelHeight); _hueGreenBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle2PixelData, pixelWidth, pixelHeight); _hueCyanBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle3PixelData, pixelWidth, pixelHeight); _hueBlueBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle4PixelData, pixelWidth, pixelHeight); - _huePurpleBitmap = maxBitmap; + _huePurpleBitmap = _maxBitmap; break; } From 667b02d2c0d6e713980f0ad05c843d96e0c5a6f4 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 2 Oct 2022 14:33:24 -0400 Subject: [PATCH 09/40] Disable WriteableBitmap re-use in ColorSlider due to crashes --- .../ColorSlider/ColorSlider.cs | 8 +++++++- .../Helpers/ColorPickerHelpers.cs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 6f20238b70..8b624958dd 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -135,10 +135,16 @@ namespace Avalonia.Controls.Primitives { if (_backgroundBitmap != null) { + // CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER + // // Re-use the existing WriteableBitmap // This assumes the height, width and byte counts are the same and must be set to null // elsewhere if that assumption is ever not true. - ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData); + // ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData); + + // ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES + //_backgroundBitmap?.Dispose(); + _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight); } else { diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs index 2378813f52..01e5a0868f 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs @@ -627,6 +627,7 @@ namespace Avalonia.Controls.Primitives /// /// Updates the given with new, raw BGRA pre-multiplied alpha pixel data. + /// WARNING: THIS METHOD IS CURRENTLY PROVIDED AS REFERENCE BUT CAUSES INTERMITTENT CRASHES IF USED. /// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally. /// /// The existing to update. From bc927d03129fa201248d2bcab78143f7a1c8f93f Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 9 Oct 2022 21:07:58 -0400 Subject: [PATCH 10/40] Remove ColorSlider background disposal on attach/detach from visual tree --- .../ColorSlider/ColorSlider.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 8b624958dd..0b9f7f9146 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -45,20 +45,12 @@ namespace Avalonia.Controls.Primitives protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - - // Bitmaps were released when detached from the visual tree so they must be re-built - UpdateBackground(); } /// protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); - - // Clean-up all bitmaps - // https://github.com/AvaloniaUI/Avalonia/issues/9051 - _backgroundBitmap?.Dispose(); - _backgroundBitmap = null; } /// From 75b3abbdd86720e2e688687283b49ef95e2ca63b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 10 Oct 2022 18:25:44 +0200 Subject: [PATCH 11/40] Properly arrange Embedded controls during ArrangeOverride --- .../Documents/IInlineHost.cs | 2 - src/Avalonia.Controls/Documents/InlineRun.cs | 42 +++++++++ .../Documents/InlineUIContainer.cs | 48 +--------- src/Avalonia.Controls/RichTextBlock.cs | 87 ++++++++++++------- .../RichTextBlockTests.cs | 36 ++++++++ 5 files changed, 137 insertions(+), 78 deletions(-) create mode 100644 src/Avalonia.Controls/Documents/InlineRun.cs diff --git a/src/Avalonia.Controls/Documents/IInlineHost.cs b/src/Avalonia.Controls/Documents/IInlineHost.cs index da72c207be..5d142952ab 100644 --- a/src/Avalonia.Controls/Documents/IInlineHost.cs +++ b/src/Avalonia.Controls/Documents/IInlineHost.cs @@ -4,8 +4,6 @@ namespace Avalonia.Controls.Documents { internal interface IInlineHost : ILogical { - void AddVisualChild(IControl child); - void Invalidate(); } } diff --git a/src/Avalonia.Controls/Documents/InlineRun.cs b/src/Avalonia.Controls/Documents/InlineRun.cs new file mode 100644 index 0000000000..68c61ca3fa --- /dev/null +++ b/src/Avalonia.Controls/Documents/InlineRun.cs @@ -0,0 +1,42 @@ +using Avalonia.Media; +using Avalonia.Media.TextFormatting; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Documents +{ + internal class EmbeddedControlRun : DrawableTextRun + { + public EmbeddedControlRun(IControl control, TextRunProperties properties) + { + Control = control; + Properties = properties; + } + + public IControl Control { get; } + + public override TextRunProperties? Properties { get; } + + public override Size Size => Control.DesiredSize; + + public override double Baseline + { + get + { + double baseline = Size.Height; + double baselineOffsetValue = Control.GetValue(TextBlock.BaselineOffsetProperty); + + if (!MathUtilities.IsZero(baselineOffsetValue)) + { + baseline = baselineOffsetValue; + } + + return -baseline; + } + } + + public override void Draw(DrawingContext drawingContext, Point origin) + { + //noop + } + } +} diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index d632e5fea7..7107fb7fed 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -3,7 +3,6 @@ using System.Text; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; -using Avalonia.Utilities; namespace Avalonia.Controls.Documents { @@ -59,56 +58,11 @@ namespace Avalonia.Controls.Documents internal override void BuildTextRun(IList textRuns) { - if(InlineHost == null) - { - return; - } - - ((ISetLogicalParent)Child).SetParent(InlineHost); - - InlineHost.AddVisualChild(Child); - - textRuns.Add(new InlineRun(Child, CreateTextRunProperties())); + textRuns.Add(new EmbeddedControlRun(Child, CreateTextRunProperties())); } internal override void AppendText(StringBuilder stringBuilder) { } - - private class InlineRun : DrawableTextRun - { - public InlineRun(IControl control, TextRunProperties properties) - { - Control = control; - Properties = properties; - } - - public IControl Control { get; } - - public override TextRunProperties? Properties { get; } - - public override Size Size => Control.DesiredSize; - - public override double Baseline - { - get - { - double baseline = Size.Height; - double baselineOffsetValue = Control.GetValue(TextBlock.BaselineOffsetProperty); - - if (!MathUtilities.IsZero(baselineOffsetValue)) - { - baseline = baselineOffsetValue; - } - - return -baseline; - } - } - - public override void Draw(DrawingContext drawingContext, Point origin) - { - //noop - } - } } } diff --git a/src/Avalonia.Controls/RichTextBlock.cs b/src/Avalonia.Controls/RichTextBlock.cs index 906a038ec3..a7eee504ec 100644 --- a/src/Avalonia.Controls/RichTextBlock.cs +++ b/src/Avalonia.Controls/RichTextBlock.cs @@ -61,6 +61,7 @@ namespace Avalonia.Controls private int _selectionStart; private int _selectionEnd; private int _wordSelectionStart = -1; + private IReadOnlyList? _textRuns; static RichTextBlock() { @@ -277,8 +278,8 @@ namespace Avalonia.Controls protected override void SetText(string? text) { var oldValue = GetText(); - - AddText(text); + + AddText(text); RaisePropertyChanged(TextProperty, oldValue, text); } @@ -301,18 +302,9 @@ namespace Avalonia.Controls ITextSource textSource; - if (HasComplexContent) + if (_textRuns != null) { - var inlines = Inlines!; - - var textRuns = new List(); - - foreach (var inline in inlines) - { - inline.BuildTextRun(textRuns); - } - - textSource = new InlinesTextSource(textRuns); + textSource = new InlinesTextSource(_textRuns); } else { @@ -546,27 +538,72 @@ namespace Avalonia.Controls protected override Size MeasureOverride(Size availableSize) { - foreach (var child in VisualChildren) + LogicalChildren.Clear(); + + VisualChildren.Clear(); + + if (Inlines != null && Inlines.Count > 0) { - if (child is Control control) + var inlines = Inlines; + + var textRuns = new List(); + + foreach (var inline in inlines) { - control.Measure(Size.Infinity); + inline.BuildTextRun(textRuns); + } + + foreach (var textRun in textRuns) + { + if (textRun is EmbeddedControlRun controlRun && + controlRun.Control is Control control) + { + ((ISetLogicalParent)control).SetParent(this); + + VisualChildren.Add(control); + + control.Measure(Size.Infinity); + } } + + _textRuns = textRuns; + } + else + { + _textRuns = null; } - + return base.MeasureOverride(availableSize); } protected override Size ArrangeOverride(Size finalSize) { - foreach (var child in VisualChildren) + if (HasComplexContent) { - if (child is Control control) + var currentY = 0.0; + + foreach (var textLine in TextLayout.TextLines) { - control.Arrange(new Rect(control.DesiredSize)); + var currentX = textLine.Start; + + foreach (var run in textLine.TextRuns) + { + if (run is DrawableTextRun drawable) + { + if (drawable is EmbeddedControlRun controlRun + && controlRun.Control is Control control) + { + control.Arrange(new Rect(new Point(currentX, currentY), control.DesiredSize)); + } + + currentX += drawable.Size.Width; + } + } + + currentY += textLine.Height; } } - + return base.ArrangeOverride(finalSize); } @@ -618,14 +655,6 @@ namespace Avalonia.Controls } } - void IInlineHost.AddVisualChild(IControl child) - { - if (child.VisualParent == null) - { - VisualChildren.Add(child); - } - } - void IInlineHost.Invalidate() { InvalidateTextLayout(); diff --git a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs index c74f13b808..05007e4f2e 100644 --- a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs @@ -1,4 +1,6 @@ using Avalonia.Controls.Documents; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -92,5 +94,39 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(target, run.Parent); } } + + [Fact] + public void InlineUIContainer_Child_Schould_Be_Arranged() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new RichTextBlock(); + + var button = new Button { Content = "12345678" }; + + button.Template = new FuncControlTemplate public abstract class AvaloniaProperty : IEquatable, IPropertyInfo { +#if NET6_0_OR_GREATER + internal const char Period = '.'; +#else + internal const string Period = "."; +#endif + /// /// Represents an unset property value. /// @@ -41,7 +47,7 @@ namespace Avalonia { _ = name ?? throw new ArgumentNullException(nameof(name)); - if (name.Contains(".")) + if (name.Contains(Period)) { throw new ArgumentException("'name' may not contain periods."); } diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index d86e723b38..01c7160129 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -228,7 +228,7 @@ namespace Avalonia _ = type ?? throw new ArgumentNullException(nameof(type)); _ = name ?? throw new ArgumentNullException(nameof(name)); - if (name.Contains(".")) + if (name.Contains(AvaloniaProperty.Period)) { throw new InvalidOperationException("Attached properties not supported."); } diff --git a/src/Avalonia.Base/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs index e2338b9b26..33f7f656ba 100644 --- a/src/Avalonia.Base/Media/UnicodeRange.cs +++ b/src/Avalonia.Base/Media/UnicodeRange.cs @@ -104,6 +104,11 @@ namespace Avalonia.Media public readonly struct UnicodeRangeSegment { +#if NET6_0_OR_GREATER + private const char Question = '?'; +#else + private const string Question = "?"; +#endif private static Regex s_regex = new Regex(@"^(?:[uU]\+)?(?:([0-9a-fA-F](?:[0-9a-fA-F?]{1,5})?))$"); public UnicodeRangeSegment(int start, int end) @@ -163,7 +168,7 @@ namespace Avalonia.Media throw new FormatException("Could not parse specified Unicode range segment."); } - if (!single.Value.Contains("?")) + if (!single.Value.Contains(Question)) { start = int.Parse(single.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); end = start; diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index 00ebcab70e..65ec4cc54c 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -597,7 +597,7 @@ namespace Avalonia.Controls.Primitives for (int i = presenter.Classes.Count - 1; i >= 0; i--) { if (!classes.Contains(presenter.Classes[i]) && - !presenter.Classes[i].Contains(":")) + !presenter.Classes[i].Contains(':')) { presenter.Classes.RemoveAt(i); } diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index b607ced10a..21373c6b76 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -1151,8 +1151,8 @@ namespace Avalonia.Controls if (PIndex >= 0) { //stringToTest contains a "P" between 2 "'", it's considered as text, not percent - var isText = stringToTest.Substring(0, PIndex).Contains("'") - && stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains("'"); + var isText = stringToTest.Substring(0, PIndex).Contains('\'') + && stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains('\''); return !isText; } diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs index 524ec09ca9..6abece6bf3 100644 --- a/src/Avalonia.Remote.Protocol/MetsysBson.cs +++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs @@ -1364,13 +1364,13 @@ namespace Metsys.Bson var optionsString = ReadName(); var options = RegexOptions.None; - if (optionsString.Contains("e")) options = options | RegexOptions.ECMAScript; - if (optionsString.Contains("i")) options = options | RegexOptions.IgnoreCase; - if (optionsString.Contains("l")) options = options | RegexOptions.CultureInvariant; - if (optionsString.Contains("m")) options = options | RegexOptions.Multiline; - if (optionsString.Contains("s")) options = options | RegexOptions.Singleline; - if (optionsString.Contains("w")) options = options | RegexOptions.IgnorePatternWhitespace; - if (optionsString.Contains("x")) options = options | RegexOptions.ExplicitCapture; + if (optionsString.Contains('e')) options = options | RegexOptions.ECMAScript; + if (optionsString.Contains('i')) options = options | RegexOptions.IgnoreCase; + if (optionsString.Contains('l')) options = options | RegexOptions.CultureInvariant; + if (optionsString.Contains('m')) options = options | RegexOptions.Multiline; + if (optionsString.Contains('s')) options = options | RegexOptions.Singleline; + if (optionsString.Contains('w')) options = options | RegexOptions.IgnorePatternWhitespace; + if (optionsString.Contains('x')) options = options | RegexOptions.ExplicitCapture; return new Regex(pattern, options); } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index d907bcbef9..48a7ba99e3 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -14,6 +14,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { class AvaloniaXamlIlLanguageParseIntrinsics { +#if NET6_0_OR_GREATER + private const string Colon = ":"; +#else + private const string Colon = ":"; +#endif + public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result) { if (type.FullName == "System.TimeSpan") @@ -23,7 +29,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) { // // shorthand seconds format (ie. "0.25") - if (!tsText.Contains(":") && double.TryParse(tsText, + if (!tsText.Contains(Colon) && double.TryParse(tsText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var seconds)) timeSpan = TimeSpan.FromSeconds(seconds); diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs index 1e42a46875..3fd653be55 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs @@ -7,6 +7,12 @@ namespace Avalonia.Markup.Xaml.Converters public class TimeSpanTypeConverter : System.ComponentModel.TimeSpanConverter { +#if NET6_0_OR_GREATER + private const char Colon = ':'; +#else + private const string Colon = ":"; +#endif + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return base.CanConvertFrom(context, sourceType); @@ -15,7 +21,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { var valueStr = (string)value; - if (!valueStr.Contains(":")) + if (!valueStr.Contains(Colon)) { // shorthand seconds format (ie. "0.25") var secs = double.Parse(valueStr, CultureInfo.InvariantCulture); @@ -25,4 +31,4 @@ namespace Avalonia.Markup.Xaml.Converters return base.ConvertFrom(context, culture, value); } } -} \ No newline at end of file +} From 056bbdd9bd7459efcdc36ec47740f26e0617e494 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:13:39 -0400 Subject: [PATCH 15/40] Switch AutoCompleteBox to use the same TextChanged event as TextBox --- src/Avalonia.Controls/AutoCompleteBox.cs | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 8a8c4ead86..3cf7a56c58 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -610,6 +610,13 @@ namespace Avalonia.Controls o => o.AsyncPopulator, (o, v) => o.AsyncPopulator = v); + /// + /// Defines the event. + /// + public static readonly RoutedEvent TextChangedEvent = + RoutedEvent.Register( + nameof(TextChanged), RoutingStrategies.Bubble); + private static bool IsValidMinimumPrefixLength(int value) => value >= -1; private static bool IsValidMinimumPopulateDelay(TimeSpan value) => value.TotalMilliseconds >= 0.0; @@ -1529,10 +1536,14 @@ namespace Avalonia.Controls } /// - /// Occurs when the text in the text box portion of the - /// changes. + /// Occurs asynchronously when the text in the portion of the + /// changes. /// - public event EventHandler? TextChanged; + public event EventHandler? TextChanged + { + add => AddHandler(TextChangedEvent, value); + remove => RemoveHandler(TextChangedEvent, value); + } /// /// Occurs when the @@ -1690,15 +1701,12 @@ namespace Avalonia.Controls } /// - /// Raises the - /// - /// event. + /// Raises the event. /// - /// A - /// that contains the event data. - protected virtual void OnTextChanged(RoutedEventArgs e) + /// A that contains the event data. + protected virtual void OnTextChanged(TextChangedEventArgs e) { - TextChanged?.Invoke(this, e); + RaiseEvent(e); } /// @@ -1985,7 +1993,7 @@ namespace Avalonia.Controls if (callTextChanged) { - OnTextChanged(new RoutedEventArgs()); + OnTextChanged(new TextChangedEventArgs(TextChangedEvent)); } } From b6698825fc2d1f7c353a4a53834e07d7b4e5e90e Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:21:47 -0400 Subject: [PATCH 16/40] Modernize some code formatting in AutoCompleteBox --- src/Avalonia.Controls/AutoCompleteBox.cs | 82 +++++++++++------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 3cf7a56c58..e7a14bf568 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -898,8 +898,8 @@ namespace Avalonia.Controls /// public int MinimumPrefixLength { - get { return GetValue(MinimumPrefixLengthProperty); } - set { SetValue(MinimumPrefixLengthProperty, value); } + get => GetValue(MinimumPrefixLengthProperty); + set => SetValue(MinimumPrefixLengthProperty, value); } /// @@ -914,8 +914,8 @@ namespace Avalonia.Controls /// public bool IsTextCompletionEnabled { - get { return GetValue(IsTextCompletionEnabledProperty); } - set { SetValue(IsTextCompletionEnabledProperty, value); } + get => GetValue(IsTextCompletionEnabledProperty); + set => SetValue(IsTextCompletionEnabledProperty, value); } /// @@ -934,8 +934,8 @@ namespace Avalonia.Controls /// public IDataTemplate ItemTemplate { - get { return GetValue(ItemTemplateProperty); } - set { SetValue(ItemTemplateProperty, value); } + get => GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); } /// @@ -950,8 +950,8 @@ namespace Avalonia.Controls /// the list of possible matches in the drop-down. The default is 0. public TimeSpan MinimumPopulateDelay { - get { return GetValue(MinimumPopulateDelayProperty); } - set { SetValue(MinimumPopulateDelayProperty, value); } + get => GetValue(MinimumPopulateDelayProperty); + set => SetValue(MinimumPopulateDelayProperty, value); } /// @@ -964,8 +964,8 @@ namespace Avalonia.Controls /// The specified value is less than 0. public double MaxDropDownHeight { - get { return GetValue(MaxDropDownHeightProperty); } - set { SetValue(MaxDropDownHeightProperty, value); } + get => GetValue(MaxDropDownHeightProperty); + set => SetValue(MaxDropDownHeightProperty, value); } /// @@ -978,8 +978,8 @@ namespace Avalonia.Controls /// public bool IsDropDownOpen { - get { return _isDropDownOpen; } - set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); } + get => _isDropDownOpen; + set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); } /// @@ -993,7 +993,7 @@ namespace Avalonia.Controls [AssignBinding] public IBinding? ValueMemberBinding { - get { return _valueBindingEvaluator?.ValueBinding; } + get => _valueBindingEvaluator?.ValueBinding; set { if (ValueMemberBinding != value) @@ -1016,8 +1016,8 @@ namespace Avalonia.Controls /// public object? SelectedItem { - get { return _selectedItem; } - set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); } + get => _selectedItem; + set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value); } /// @@ -1028,8 +1028,8 @@ namespace Avalonia.Controls /// control. public string? Text { - get { return _text; } - set { SetAndRaise(TextProperty, ref _text, value); } + get => _text; + set => SetAndRaise(TextProperty, ref _text, value); } /// @@ -1047,7 +1047,7 @@ namespace Avalonia.Controls /// public string? SearchText { - get { return _searchText; } + get => _searchText; private set { try @@ -1083,14 +1083,14 @@ namespace Avalonia.Controls /// public AutoCompleteFilterMode FilterMode { - get { return GetValue(FilterModeProperty); } - set { SetValue(FilterModeProperty, value); } + get => GetValue(FilterModeProperty); + set => SetValue(FilterModeProperty, value); } public string? Watermark { - get { return GetValue(WatermarkProperty); } - set { SetValue(WatermarkProperty, value); } + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); } /// @@ -1109,8 +1109,8 @@ namespace Avalonia.Controls /// public AutoCompleteFilterPredicate? ItemFilter { - get { return _itemFilter; } - set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); } + get => _itemFilter; + set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value); } /// @@ -1129,8 +1129,8 @@ namespace Avalonia.Controls /// public AutoCompleteFilterPredicate? TextFilter { - get { return _textFilter; } - set { SetAndRaise(TextFilterProperty, ref _textFilter, value); } + get => _textFilter; + set => SetAndRaise(TextFilterProperty, ref _textFilter, value); } /// @@ -1145,8 +1145,8 @@ namespace Avalonia.Controls /// public AutoCompleteSelector? ItemSelector { - get { return _itemSelector; } - set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); } + get => _itemSelector; + set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); } /// @@ -1163,14 +1163,14 @@ namespace Avalonia.Controls /// public AutoCompleteSelector? TextSelector { - get { return _textSelector; } - set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); } + get => _textSelector; + set => SetAndRaise(TextSelectorProperty, ref _textSelector, value); } public Func>>? AsyncPopulator { - get { return _asyncPopulator; } - set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); } + get => _asyncPopulator; + set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); } /// @@ -1183,8 +1183,8 @@ namespace Avalonia.Controls /// control. public IEnumerable? Items { - get { return _itemsEnumerable; } - set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); } + get => _itemsEnumerable; + set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); } /// @@ -1197,7 +1197,7 @@ namespace Avalonia.Controls /// private TextBox? TextBox { - get { return _textBox; } + get => _textBox; set { _textBoxSubscriptions?.Dispose(); @@ -1261,7 +1261,7 @@ namespace Avalonia.Controls /// protected ISelectionAdapter? SelectionAdapter { - get { return _adapter; } + get => _adapter; set { if (_adapter != null) @@ -2748,8 +2748,6 @@ namespace Avalonia.Controls /// private IBinding? _binding; - #region public T Value - /// /// Identifies the Value dependency property. /// @@ -2761,18 +2759,16 @@ namespace Avalonia.Controls /// public T Value { - get { return GetValue(ValueProperty); } - set { SetValue(ValueProperty, value); } + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); } - #endregion public string Value - /// /// Gets or sets the value binding. /// public IBinding? ValueBinding { - get { return _binding; } + get => _binding; set { _binding = value; From bbb49ab1591920d4d7d45b500be7062048c0c06d Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:22:39 -0400 Subject: [PATCH 17/40] Move AutoCompleteBox into its own directory --- src/Avalonia.Controls/{ => AutoCompleteBox}/AutoCompleteBox.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls/{ => AutoCompleteBox}/AutoCompleteBox.cs (100%) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs similarity index 100% rename from src/Avalonia.Controls/AutoCompleteBox.cs rename to src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs From a28bf0f5af438319910182a94b8736514e528db9 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:32:12 -0400 Subject: [PATCH 18/40] Split apart AutoCompleteBox into multiple files --- .../AutoCompleteBox/AutoCompleteBox.cs | 185 ------------------ .../AutoCompleteBox/AutoCompleteFilterMode.cs | 133 +++++++++++++ .../AutoCompleteBox/PopulatedEventArgs.cs | 39 ++++ .../AutoCompleteBox/PopulatingEventArgs.cs | 39 ++++ 4 files changed, 211 insertions(+), 185 deletions(-) create mode 100644 src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs create mode 100644 src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs create mode 100644 src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index e7a14bf568..608d18d0d3 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -26,65 +26,6 @@ using Avalonia.VisualTree; namespace Avalonia.Controls { - /// - /// Provides data for the - /// - /// event. - /// - public class PopulatedEventArgs : EventArgs - { - /// - /// Gets the list of possible matches added to the drop-down portion of - /// the - /// control. - /// - /// The list of possible matches added to the - /// . - public IEnumerable Data { get; private set; } - - /// - /// Initializes a new instance of the - /// . - /// - /// The list of possible matches added to the - /// drop-down portion of the - /// control. - public PopulatedEventArgs(IEnumerable data) - { - Data = data; - } - } - - /// - /// Provides data for the - /// - /// event. - /// - public class PopulatingEventArgs : CancelEventArgs - { - /// - /// Gets the text that is used to determine which items to display in - /// the - /// control. - /// - /// The text that is used to determine which items to display in - /// the . - public string? Parameter { get; private set; } - - /// - /// Initializes a new instance of the - /// . - /// - /// The value of the - /// - /// property, which is used to filter items for the - /// control. - public PopulatingEventArgs(string? parameter) - { - Parameter = parameter; - } - } - /// /// Represents the filter used by the /// control to @@ -100,132 +41,6 @@ namespace Avalonia.Controls /// be either a string or an object. public delegate bool AutoCompleteFilterPredicate(string? search, T item); - /// - /// Specifies how text in the text box portion of the - /// control is used - /// to filter items specified by the - /// - /// property for display in the drop-down. - /// - public enum AutoCompleteFilterMode - { - /// - /// Specifies that no filter is used. All items are returned. - /// - None = 0, - - /// - /// Specifies a culture-sensitive, case-insensitive filter where the - /// returned items start with the specified text. The filter uses the - /// - /// method, specifying - /// as - /// the string comparison criteria. - /// - StartsWith = 1, - - /// - /// Specifies a culture-sensitive, case-sensitive filter where the - /// returned items start with the specified text. The filter uses the - /// - /// method, specifying - /// as the string - /// comparison criteria. - /// - StartsWithCaseSensitive = 2, - - /// - /// Specifies an ordinal, case-insensitive filter where the returned - /// items start with the specified text. The filter uses the - /// - /// method, specifying - /// as the - /// string comparison criteria. - /// - StartsWithOrdinal = 3, - - /// - /// Specifies an ordinal, case-sensitive filter where the returned items - /// start with the specified text. The filter uses the - /// - /// method, specifying as - /// the string comparison criteria. - /// - StartsWithOrdinalCaseSensitive = 4, - - /// - /// Specifies a culture-sensitive, case-insensitive filter where the - /// returned items contain the specified text. - /// - Contains = 5, - - /// - /// Specifies a culture-sensitive, case-sensitive filter where the - /// returned items contain the specified text. - /// - ContainsCaseSensitive = 6, - - /// - /// Specifies an ordinal, case-insensitive filter where the returned - /// items contain the specified text. - /// - ContainsOrdinal = 7, - - /// - /// Specifies an ordinal, case-sensitive filter where the returned items - /// contain the specified text. - /// - ContainsOrdinalCaseSensitive = 8, - - /// - /// Specifies a culture-sensitive, case-insensitive filter where the - /// returned items equal the specified text. The filter uses the - /// - /// method, specifying - /// as - /// the search comparison criteria. - /// - Equals = 9, - - /// - /// Specifies a culture-sensitive, case-sensitive filter where the - /// returned items equal the specified text. The filter uses the - /// - /// method, specifying - /// as the string - /// comparison criteria. - /// - EqualsCaseSensitive = 10, - - /// - /// Specifies an ordinal, case-insensitive filter where the returned - /// items equal the specified text. The filter uses the - /// - /// method, specifying - /// as the - /// string comparison criteria. - /// - EqualsOrdinal = 11, - - /// - /// Specifies an ordinal, case-sensitive filter where the returned items - /// equal the specified text. The filter uses the - /// - /// method, specifying as - /// the string comparison criteria. - /// - EqualsOrdinalCaseSensitive = 12, - - /// - /// Specifies that a custom filter is used. This mode is used when the - /// - /// or - /// - /// properties are set. - /// - Custom = 13, - } - /// /// Represents the selector used by the /// control to diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs new file mode 100644 index 0000000000..9aea49aef3 --- /dev/null +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs @@ -0,0 +1,133 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +namespace Avalonia.Controls +{ + /// + /// Specifies how text in the text box portion of the + /// control is used + /// to filter items specified by the + /// + /// property for display in the drop-down. + /// + public enum AutoCompleteFilterMode + { + /// + /// Specifies that no filter is used. All items are returned. + /// + None = 0, + + /// + /// Specifies a culture-sensitive, case-insensitive filter where the + /// returned items start with the specified text. The filter uses the + /// + /// method, specifying + /// as + /// the string comparison criteria. + /// + StartsWith = 1, + + /// + /// Specifies a culture-sensitive, case-sensitive filter where the + /// returned items start with the specified text. The filter uses the + /// + /// method, specifying + /// as the string + /// comparison criteria. + /// + StartsWithCaseSensitive = 2, + + /// + /// Specifies an ordinal, case-insensitive filter where the returned + /// items start with the specified text. The filter uses the + /// + /// method, specifying + /// as the + /// string comparison criteria. + /// + StartsWithOrdinal = 3, + + /// + /// Specifies an ordinal, case-sensitive filter where the returned items + /// start with the specified text. The filter uses the + /// + /// method, specifying as + /// the string comparison criteria. + /// + StartsWithOrdinalCaseSensitive = 4, + + /// + /// Specifies a culture-sensitive, case-insensitive filter where the + /// returned items contain the specified text. + /// + Contains = 5, + + /// + /// Specifies a culture-sensitive, case-sensitive filter where the + /// returned items contain the specified text. + /// + ContainsCaseSensitive = 6, + + /// + /// Specifies an ordinal, case-insensitive filter where the returned + /// items contain the specified text. + /// + ContainsOrdinal = 7, + + /// + /// Specifies an ordinal, case-sensitive filter where the returned items + /// contain the specified text. + /// + ContainsOrdinalCaseSensitive = 8, + + /// + /// Specifies a culture-sensitive, case-insensitive filter where the + /// returned items equal the specified text. The filter uses the + /// + /// method, specifying + /// as + /// the search comparison criteria. + /// + Equals = 9, + + /// + /// Specifies a culture-sensitive, case-sensitive filter where the + /// returned items equal the specified text. The filter uses the + /// + /// method, specifying + /// as the string + /// comparison criteria. + /// + EqualsCaseSensitive = 10, + + /// + /// Specifies an ordinal, case-insensitive filter where the returned + /// items equal the specified text. The filter uses the + /// + /// method, specifying + /// as the + /// string comparison criteria. + /// + EqualsOrdinal = 11, + + /// + /// Specifies an ordinal, case-sensitive filter where the returned items + /// equal the specified text. The filter uses the + /// + /// method, specifying as + /// the string comparison criteria. + /// + EqualsOrdinalCaseSensitive = 12, + + /// + /// Specifies that a custom filter is used. This mode is used when the + /// + /// or + /// + /// properties are set. + /// + Custom = 13, + } +} diff --git a/src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs b/src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs new file mode 100644 index 0000000000..22bc1d3cab --- /dev/null +++ b/src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs @@ -0,0 +1,39 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +using System; +using System.Collections; + +namespace Avalonia.Controls +{ + /// + /// Provides data for the + /// + /// event. + /// + public class PopulatedEventArgs : EventArgs + { + /// + /// Gets the list of possible matches added to the drop-down portion of + /// the + /// control. + /// + /// The list of possible matches added to the + /// . + public IEnumerable Data { get; private set; } + + /// + /// Initializes a new instance of the + /// . + /// + /// The list of possible matches added to the + /// drop-down portion of the + /// control. + public PopulatedEventArgs(IEnumerable data) + { + Data = data; + } + } +} diff --git a/src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs b/src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs new file mode 100644 index 0000000000..c4941ad6fe --- /dev/null +++ b/src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs @@ -0,0 +1,39 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +using System.ComponentModel; + +namespace Avalonia.Controls +{ + /// + /// Provides data for the + /// + /// event. + /// + public class PopulatingEventArgs : CancelEventArgs + { + /// + /// Gets the text that is used to determine which items to display in + /// the + /// control. + /// + /// The text that is used to determine which items to display in + /// the . + public string? Parameter { get; private set; } + + /// + /// Initializes a new instance of the + /// . + /// + /// The value of the + /// + /// property, which is used to filter items for the + /// control. + public PopulatingEventArgs(string? parameter) + { + Parameter = parameter; + } + } +} From d86cb35d3b815b90c5f3410f97576ed5f5e2ef2d Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:38:31 -0400 Subject: [PATCH 19/40] Separate out public AutoCompleteBox properties --- .../AutoCompleteBox.Properties.cs | 540 ++++++++++++++++++ .../AutoCompleteBox/AutoCompleteBox.cs | 533 +---------------- 2 files changed, 549 insertions(+), 524 deletions(-) create mode 100644 src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs new file mode 100644 index 0000000000..4f70f8271e --- /dev/null +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs @@ -0,0 +1,540 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls.Templates; +using Avalonia.Data; + +namespace Avalonia.Controls +{ + public partial class AutoCompleteBox + { + public static readonly StyledProperty WatermarkProperty = + TextBox.WatermarkProperty.AddOwner(); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly StyledProperty MinimumPrefixLengthProperty = + AvaloniaProperty.Register( + nameof(MinimumPrefixLength), 1, + validate: IsValidMinimumPrefixLength); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly StyledProperty MinimumPopulateDelayProperty = + AvaloniaProperty.Register( + nameof(MinimumPopulateDelay), + TimeSpan.Zero, + validate: IsValidMinimumPopulateDelay); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly StyledProperty MaxDropDownHeightProperty = + AvaloniaProperty.Register( + nameof(MaxDropDownHeight), + double.PositiveInfinity, + validate: IsValidMaxDropDownHeight); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly StyledProperty IsTextCompletionEnabledProperty = + AvaloniaProperty.Register(nameof(IsTextCompletionEnabled)); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly StyledProperty ItemTemplateProperty = + AvaloniaProperty.Register(nameof(ItemTemplate)); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty IsDropDownOpenProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsDropDownOpen), + o => o.IsDropDownOpen, + (o, v) => o.IsDropDownOpen = v); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier the + /// + /// dependency property. + public static readonly DirectProperty SelectedItemProperty = + AvaloniaProperty.RegisterDirect( + nameof(SelectedItem), + o => o.SelectedItem, + (o, v) => o.SelectedItem = v, + defaultBindingMode: BindingMode.TwoWay, + enableDataValidation: true); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty TextProperty = + TextBlock.TextProperty.AddOwnerWithDataValidation( + o => o.Text, + (o, v) => o.Text = v, + defaultBindingMode: BindingMode.TwoWay, + enableDataValidation: true); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty SearchTextProperty = + AvaloniaProperty.RegisterDirect( + nameof(SearchText), + o => o.SearchText, + unsetValue: string.Empty); + + /// + /// Gets the identifier for the + /// + /// dependency property. + /// + public static readonly StyledProperty FilterModeProperty = + AvaloniaProperty.Register( + nameof(FilterMode), + defaultValue: AutoCompleteFilterMode.StartsWith, + validate: IsValidFilterMode); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty?> ItemFilterProperty = + AvaloniaProperty.RegisterDirect?>( + nameof(ItemFilter), + o => o.ItemFilter, + (o, v) => o.ItemFilter = v); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty?> TextFilterProperty = + AvaloniaProperty.RegisterDirect?>( + nameof(TextFilter), + o => o.TextFilter, + (o, v) => o.TextFilter = v, + unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty?> ItemSelectorProperty = + AvaloniaProperty.RegisterDirect?>( + nameof(ItemSelector), + o => o.ItemSelector, + (o, v) => o.ItemSelector = v); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty?> TextSelectorProperty = + AvaloniaProperty.RegisterDirect?>( + nameof(TextSelector), + o => o.TextSelector, + (o, v) => o.TextSelector = v); + + /// + /// Identifies the + /// + /// dependency property. + /// + /// The identifier for the + /// + /// dependency property. + public static readonly DirectProperty ItemsProperty = + AvaloniaProperty.RegisterDirect( + nameof(Items), + o => o.Items, + (o, v) => o.Items = v); + + public static readonly DirectProperty>>?> AsyncPopulatorProperty = + AvaloniaProperty.RegisterDirect>>?>( + nameof(AsyncPopulator), + o => o.AsyncPopulator, + (o, v) => o.AsyncPopulator = v); + + /// + /// Gets or sets the minimum number of characters required to be entered + /// in the text box before the + /// displays + /// possible matches. + /// matches. + /// + /// + /// The minimum number of characters to be entered in the text box + /// before the + /// displays possible matches. The default is 1. + /// + /// + /// If you set MinimumPrefixLength to -1, the AutoCompleteBox will + /// not provide possible matches. There is no maximum value, but + /// setting MinimumPrefixLength to value that is too large will + /// prevent the AutoCompleteBox from providing possible matches as well. + /// + public int MinimumPrefixLength + { + get => GetValue(MinimumPrefixLengthProperty); + set => SetValue(MinimumPrefixLengthProperty, value); + } + + /// + /// Gets or sets a value indicating whether the first possible match + /// found during the filtering process will be displayed automatically + /// in the text box. + /// + /// + /// True if the first possible match found will be displayed + /// automatically in the text box; otherwise, false. The default is + /// false. + /// + public bool IsTextCompletionEnabled + { + get => GetValue(IsTextCompletionEnabledProperty); + set => SetValue(IsTextCompletionEnabledProperty, value); + } + + /// + /// Gets or sets the used + /// to display each item in the drop-down portion of the control. + /// + /// The used to + /// display each item in the drop-down. The default is null. + /// + /// You use the ItemTemplate property to specify the visualization + /// of the data objects in the drop-down portion of the AutoCompleteBox + /// control. If your AutoCompleteBox is bound to a collection and you + /// do not provide specific display instructions by using a + /// DataTemplate, the resulting UI of each item is a string + /// representation of each object in the underlying collection. + /// + public IDataTemplate ItemTemplate + { + get => GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); + } + + /// + /// Gets or sets the minimum delay, after text is typed + /// in the text box before the + /// control + /// populates the list of possible matches in the drop-down. + /// + /// The minimum delay, after text is typed in + /// the text box, but before the + /// populates + /// the list of possible matches in the drop-down. The default is 0. + public TimeSpan MinimumPopulateDelay + { + get => GetValue(MinimumPopulateDelayProperty); + set => SetValue(MinimumPopulateDelayProperty, value); + } + + /// + /// Gets or sets the maximum height of the drop-down portion of the + /// control. + /// + /// The maximum height of the drop-down portion of the + /// control. + /// The default is . + /// The specified value is less than 0. + public double MaxDropDownHeight + { + get => GetValue(MaxDropDownHeightProperty); + set => SetValue(MaxDropDownHeightProperty, value); + } + + /// + /// Gets or sets a value indicating whether the drop-down portion of + /// the control is open. + /// + /// + /// True if the drop-down is open; otherwise, false. The default is + /// false. + /// + public bool IsDropDownOpen + { + get => _isDropDownOpen; + set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); + } + + /// + /// Gets or sets the that + /// is used to get the values for display in the text portion of + /// the + /// control. + /// + /// The object used + /// when binding to a collection property. + [AssignBinding] + public IBinding? ValueMemberBinding + { + get => _valueBindingEvaluator?.ValueBinding; + set + { + if (ValueMemberBinding != value) + { + _valueBindingEvaluator = new BindingEvaluator(value); + OnValueMemberBindingChanged(value); + } + } + } + + /// + /// Gets or sets the selected item in the drop-down. + /// + /// The selected item in the drop-down. + /// + /// If the IsTextCompletionEnabled property is true and text typed by + /// the user matches an item in the ItemsSource collection, which is + /// then displayed in the text box, the SelectedItem property will be + /// a null reference. + /// + public object? SelectedItem + { + get => _selectedItem; + set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value); + } + + /// + /// Gets or sets the text in the text box portion of the + /// control. + /// + /// The text in the text box portion of the + /// control. + public string? Text + { + get => _text; + set => SetAndRaise(TextProperty, ref _text, value); + } + + /// + /// Gets the text that is used to filter items in the + /// + /// item collection. + /// + /// The text that is used to filter items in the + /// + /// item collection. + /// + /// The SearchText value is typically the same as the + /// Text property, but is set after the TextChanged event occurs + /// and before the Populating event. + /// + public string? SearchText + { + get => _searchText; + private set + { + try + { + _allowWrite = true; + SetAndRaise(SearchTextProperty, ref _searchText, value); + } + finally + { + _allowWrite = false; + } + } + } + + /// + /// Gets or sets how the text in the text box is used to filter items + /// specified by the + /// + /// property for display in the drop-down. + /// + /// One of the + /// + /// values The default is + /// . + /// The specified value is + /// not a valid + /// . + /// + /// Use the FilterMode property to specify how possible matches are + /// filtered. For example, possible matches can be filtered in a + /// predefined or custom way. The search mode is automatically set to + /// Custom if you set the ItemFilter property. + /// + public AutoCompleteFilterMode FilterMode + { + get => GetValue(FilterModeProperty); + set => SetValue(FilterModeProperty, value); + } + + public string? Watermark + { + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); + } + + /// + /// Gets or sets the custom method that uses user-entered text to filter + /// the items specified by the + /// + /// property for display in the drop-down. + /// + /// The custom method that uses the user-entered text to filter + /// the items specified by the + /// + /// property. The default is null. + /// + /// The filter mode is automatically set to Custom if you set the + /// ItemFilter property. + /// + public AutoCompleteFilterPredicate? ItemFilter + { + get => _itemFilter; + set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value); + } + + /// + /// Gets or sets the custom method that uses the user-entered text to + /// filter items specified by the + /// + /// property in a text-based way for display in the drop-down. + /// + /// The custom method that uses the user-entered text to filter + /// items specified by the + /// + /// property in a text-based way for display in the drop-down. + /// + /// The search mode is automatically set to Custom if you set the + /// TextFilter property. + /// + public AutoCompleteFilterPredicate? TextFilter + { + get => _textFilter; + set => SetAndRaise(TextFilterProperty, ref _textFilter, value); + } + + /// + /// Gets or sets the custom method that combines the user-entered + /// text and one of the items specified by the + /// . + /// + /// + /// The custom method that combines the user-entered + /// text and one of the items specified by the + /// . + /// + public AutoCompleteSelector? ItemSelector + { + get => _itemSelector; + set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); + } + + /// + /// Gets or sets the custom method that combines the user-entered + /// text and one of the items specified by the + /// + /// in a text-based way. + /// + /// + /// The custom method that combines the user-entered + /// text and one of the items specified by the + /// + /// in a text-based way. + /// + public AutoCompleteSelector? TextSelector + { + get => _textSelector; + set => SetAndRaise(TextSelectorProperty, ref _textSelector, value); + } + + public Func>>? AsyncPopulator + { + get => _asyncPopulator; + set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); + } + + /// + /// Gets or sets a collection that is used to generate the items for the + /// drop-down portion of the + /// control. + /// + /// The collection that is used to generate the items of the + /// drop-down portion of the + /// control. + public IEnumerable? Items + { + get => _itemsEnumerable; + set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); + } + } +} diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index 608d18d0d3..a027b8b650 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls [TemplatePart(ElementSelectionAdapter, typeof(ISelectionAdapter))] [TemplatePart(ElementTextBox, typeof(TextBox))] [PseudoClasses(":dropdownopen")] - public class AutoCompleteBox : TemplatedControl + public partial class AutoCompleteBox : TemplatedControl { /// /// Specifies the name of the selection adapter TemplatePart. @@ -209,228 +209,22 @@ namespace Avalonia.Controls private readonly EventHandler _populateDropDownHandler; - public static readonly RoutedEvent SelectionChangedEvent = - RoutedEvent.Register(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); - - public static readonly StyledProperty WatermarkProperty = - TextBox.WatermarkProperty.AddOwner(); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly StyledProperty MinimumPrefixLengthProperty = - AvaloniaProperty.Register( - nameof(MinimumPrefixLength), 1, - validate: IsValidMinimumPrefixLength); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly StyledProperty MinimumPopulateDelayProperty = - AvaloniaProperty.Register( - nameof(MinimumPopulateDelay), - TimeSpan.Zero, - validate: IsValidMinimumPopulateDelay); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly StyledProperty MaxDropDownHeightProperty = - AvaloniaProperty.Register( - nameof(MaxDropDownHeight), - double.PositiveInfinity, - validate: IsValidMaxDropDownHeight); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly StyledProperty IsTextCompletionEnabledProperty = - AvaloniaProperty.Register(nameof(IsTextCompletionEnabled)); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly StyledProperty ItemTemplateProperty = - AvaloniaProperty.Register(nameof(ItemTemplate)); - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty IsDropDownOpenProperty = - AvaloniaProperty.RegisterDirect( - nameof(IsDropDownOpen), - o => o.IsDropDownOpen, - (o, v) => o.IsDropDownOpen = v); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier the - /// - /// dependency property. - public static readonly DirectProperty SelectedItemProperty = - AvaloniaProperty.RegisterDirect( - nameof(SelectedItem), - o => o.SelectedItem, - (o, v) => o.SelectedItem = v, - defaultBindingMode: BindingMode.TwoWay, - enableDataValidation: true); - - /// - /// Identifies the - /// - /// dependency property. + /// /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty TextProperty = - TextBlock.TextProperty.AddOwnerWithDataValidation( - o => o.Text, - (o, v) => o.Text = v, - defaultBindingMode: BindingMode.TwoWay, - enableDataValidation: true); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty SearchTextProperty = - AvaloniaProperty.RegisterDirect( - nameof(SearchText), - o => o.SearchText, - unsetValue: string.Empty); - - /// - /// Gets the identifier for the - /// - /// dependency property. - /// - public static readonly StyledProperty FilterModeProperty = - AvaloniaProperty.Register( - nameof(FilterMode), - defaultValue: AutoCompleteFilterMode.StartsWith, - validate: IsValidFilterMode); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty?> ItemFilterProperty = - AvaloniaProperty.RegisterDirect?>( - nameof(ItemFilter), - o => o.ItemFilter, - (o, v) => o.ItemFilter = v); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty?> TextFilterProperty = - AvaloniaProperty.RegisterDirect?>( - nameof(TextFilter), - o => o.TextFilter, - (o, v) => o.TextFilter = v, - unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty?> ItemSelectorProperty = - AvaloniaProperty.RegisterDirect?>( - nameof(ItemSelector), - o => o.ItemSelector, - (o, v) => o.ItemSelector = v); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty?> TextSelectorProperty = - AvaloniaProperty.RegisterDirect?>( - nameof(TextSelector), - o => o.TextSelector, - (o, v) => o.TextSelector = v); - - /// - /// Identifies the - /// - /// dependency property. - /// - /// The identifier for the - /// - /// dependency property. - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( - nameof(Items), - o => o.Items, - (o, v) => o.Items = v); - - public static readonly DirectProperty>>?> AsyncPopulatorProperty = - AvaloniaProperty.RegisterDirect>>?>( - nameof(AsyncPopulator), - o => o.AsyncPopulator, - (o, v) => o.AsyncPopulator = v); + public static readonly RoutedEvent SelectionChangedEvent = + RoutedEvent.Register( + nameof(SelectionChanged), + RoutingStrategies.Bubble, + typeof(AutoCompleteBox)); /// /// Defines the event. /// public static readonly RoutedEvent TextChangedEvent = RoutedEvent.Register( - nameof(TextChanged), RoutingStrategies.Bubble); + nameof(TextChanged), + RoutingStrategies.Bubble); private static bool IsValidMinimumPrefixLength(int value) => value >= -1; @@ -693,315 +487,6 @@ namespace Avalonia.Controls ClearView(); } - /// - /// Gets or sets the minimum number of characters required to be entered - /// in the text box before the - /// displays - /// possible matches. - /// matches. - /// - /// - /// The minimum number of characters to be entered in the text box - /// before the - /// displays possible matches. The default is 1. - /// - /// - /// If you set MinimumPrefixLength to -1, the AutoCompleteBox will - /// not provide possible matches. There is no maximum value, but - /// setting MinimumPrefixLength to value that is too large will - /// prevent the AutoCompleteBox from providing possible matches as well. - /// - public int MinimumPrefixLength - { - get => GetValue(MinimumPrefixLengthProperty); - set => SetValue(MinimumPrefixLengthProperty, value); - } - - /// - /// Gets or sets a value indicating whether the first possible match - /// found during the filtering process will be displayed automatically - /// in the text box. - /// - /// - /// True if the first possible match found will be displayed - /// automatically in the text box; otherwise, false. The default is - /// false. - /// - public bool IsTextCompletionEnabled - { - get => GetValue(IsTextCompletionEnabledProperty); - set => SetValue(IsTextCompletionEnabledProperty, value); - } - - /// - /// Gets or sets the used - /// to display each item in the drop-down portion of the control. - /// - /// The used to - /// display each item in the drop-down. The default is null. - /// - /// You use the ItemTemplate property to specify the visualization - /// of the data objects in the drop-down portion of the AutoCompleteBox - /// control. If your AutoCompleteBox is bound to a collection and you - /// do not provide specific display instructions by using a - /// DataTemplate, the resulting UI of each item is a string - /// representation of each object in the underlying collection. - /// - public IDataTemplate ItemTemplate - { - get => GetValue(ItemTemplateProperty); - set => SetValue(ItemTemplateProperty, value); - } - - /// - /// Gets or sets the minimum delay, after text is typed - /// in the text box before the - /// control - /// populates the list of possible matches in the drop-down. - /// - /// The minimum delay, after text is typed in - /// the text box, but before the - /// populates - /// the list of possible matches in the drop-down. The default is 0. - public TimeSpan MinimumPopulateDelay - { - get => GetValue(MinimumPopulateDelayProperty); - set => SetValue(MinimumPopulateDelayProperty, value); - } - - /// - /// Gets or sets the maximum height of the drop-down portion of the - /// control. - /// - /// The maximum height of the drop-down portion of the - /// control. - /// The default is . - /// The specified value is less than 0. - public double MaxDropDownHeight - { - get => GetValue(MaxDropDownHeightProperty); - set => SetValue(MaxDropDownHeightProperty, value); - } - - /// - /// Gets or sets a value indicating whether the drop-down portion of - /// the control is open. - /// - /// - /// True if the drop-down is open; otherwise, false. The default is - /// false. - /// - public bool IsDropDownOpen - { - get => _isDropDownOpen; - set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); - } - - /// - /// Gets or sets the that - /// is used to get the values for display in the text portion of - /// the - /// control. - /// - /// The object used - /// when binding to a collection property. - [AssignBinding] - public IBinding? ValueMemberBinding - { - get => _valueBindingEvaluator?.ValueBinding; - set - { - if (ValueMemberBinding != value) - { - _valueBindingEvaluator = new BindingEvaluator(value); - OnValueMemberBindingChanged(value); - } - } - } - - /// - /// Gets or sets the selected item in the drop-down. - /// - /// The selected item in the drop-down. - /// - /// If the IsTextCompletionEnabled property is true and text typed by - /// the user matches an item in the ItemsSource collection, which is - /// then displayed in the text box, the SelectedItem property will be - /// a null reference. - /// - public object? SelectedItem - { - get => _selectedItem; - set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value); - } - - /// - /// Gets or sets the text in the text box portion of the - /// control. - /// - /// The text in the text box portion of the - /// control. - public string? Text - { - get => _text; - set => SetAndRaise(TextProperty, ref _text, value); - } - - /// - /// Gets the text that is used to filter items in the - /// - /// item collection. - /// - /// The text that is used to filter items in the - /// - /// item collection. - /// - /// The SearchText value is typically the same as the - /// Text property, but is set after the TextChanged event occurs - /// and before the Populating event. - /// - public string? SearchText - { - get => _searchText; - private set - { - try - { - _allowWrite = true; - SetAndRaise(SearchTextProperty, ref _searchText, value); - } - finally - { - _allowWrite = false; - } - } - } - - /// - /// Gets or sets how the text in the text box is used to filter items - /// specified by the - /// - /// property for display in the drop-down. - /// - /// One of the - /// - /// values The default is - /// . - /// The specified value is - /// not a valid - /// . - /// - /// Use the FilterMode property to specify how possible matches are - /// filtered. For example, possible matches can be filtered in a - /// predefined or custom way. The search mode is automatically set to - /// Custom if you set the ItemFilter property. - /// - public AutoCompleteFilterMode FilterMode - { - get => GetValue(FilterModeProperty); - set => SetValue(FilterModeProperty, value); - } - - public string? Watermark - { - get => GetValue(WatermarkProperty); - set => SetValue(WatermarkProperty, value); - } - - /// - /// Gets or sets the custom method that uses user-entered text to filter - /// the items specified by the - /// - /// property for display in the drop-down. - /// - /// The custom method that uses the user-entered text to filter - /// the items specified by the - /// - /// property. The default is null. - /// - /// The filter mode is automatically set to Custom if you set the - /// ItemFilter property. - /// - public AutoCompleteFilterPredicate? ItemFilter - { - get => _itemFilter; - set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value); - } - - /// - /// Gets or sets the custom method that uses the user-entered text to - /// filter items specified by the - /// - /// property in a text-based way for display in the drop-down. - /// - /// The custom method that uses the user-entered text to filter - /// items specified by the - /// - /// property in a text-based way for display in the drop-down. - /// - /// The search mode is automatically set to Custom if you set the - /// TextFilter property. - /// - public AutoCompleteFilterPredicate? TextFilter - { - get => _textFilter; - set => SetAndRaise(TextFilterProperty, ref _textFilter, value); - } - - /// - /// Gets or sets the custom method that combines the user-entered - /// text and one of the items specified by the - /// . - /// - /// - /// The custom method that combines the user-entered - /// text and one of the items specified by the - /// . - /// - public AutoCompleteSelector? ItemSelector - { - get => _itemSelector; - set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); - } - - /// - /// Gets or sets the custom method that combines the user-entered - /// text and one of the items specified by the - /// - /// in a text-based way. - /// - /// - /// The custom method that combines the user-entered - /// text and one of the items specified by the - /// - /// in a text-based way. - /// - public AutoCompleteSelector? TextSelector - { - get => _textSelector; - set => SetAndRaise(TextSelectorProperty, ref _textSelector, value); - } - - public Func>>? AsyncPopulator - { - get => _asyncPopulator; - set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); - } - - /// - /// Gets or sets a collection that is used to generate the items for the - /// drop-down portion of the - /// control. - /// - /// The collection that is used to generate the items of the - /// drop-down portion of the - /// control. - public IEnumerable? Items - { - get => _itemsEnumerable; - set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); - } - /// /// Gets or sets the drop down popup control. /// From 62b1eea00c3880bb324207ad3b6dec60da624ead Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 20:48:02 -0400 Subject: [PATCH 20/40] Cleanup some AutoCompleteBox property comments --- .../AutoCompleteBox.Properties.cs | 187 ++++++------------ 1 file changed, 55 insertions(+), 132 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs index 4f70f8271e..ecbe01d8b7 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs @@ -19,26 +19,18 @@ namespace Avalonia.Controls TextBox.WatermarkProperty.AddOwner(); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly StyledProperty MinimumPrefixLengthProperty = AvaloniaProperty.Register( nameof(MinimumPrefixLength), 1, validate: IsValidMinimumPrefixLength); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly StyledProperty MinimumPopulateDelayProperty = AvaloniaProperty.Register( nameof(MinimumPopulateDelay), @@ -46,13 +38,9 @@ namespace Avalonia.Controls validate: IsValidMinimumPopulateDelay); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly StyledProperty MaxDropDownHeightProperty = AvaloniaProperty.Register( nameof(MaxDropDownHeight), @@ -60,35 +48,23 @@ namespace Avalonia.Controls validate: IsValidMaxDropDownHeight); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly StyledProperty IsTextCompletionEnabledProperty = AvaloniaProperty.Register(nameof(IsTextCompletionEnabled)); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly StyledProperty ItemTemplateProperty = AvaloniaProperty.Register(nameof(ItemTemplate)); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty IsDropDownOpenProperty = AvaloniaProperty.RegisterDirect( nameof(IsDropDownOpen), @@ -96,13 +72,9 @@ namespace Avalonia.Controls (o, v) => o.IsDropDownOpen = v); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier the - /// - /// dependency property. + /// The identifier the property. public static readonly DirectProperty SelectedItemProperty = AvaloniaProperty.RegisterDirect( nameof(SelectedItem), @@ -112,13 +84,9 @@ namespace Avalonia.Controls enableDataValidation: true); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty TextProperty = TextBlock.TextProperty.AddOwnerWithDataValidation( o => o.Text, @@ -127,13 +95,9 @@ namespace Avalonia.Controls enableDataValidation: true); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty SearchTextProperty = AvaloniaProperty.RegisterDirect( nameof(SearchText), @@ -141,9 +105,7 @@ namespace Avalonia.Controls unsetValue: string.Empty); /// - /// Gets the identifier for the - /// - /// dependency property. + /// Gets the identifier for the property. /// public static readonly StyledProperty FilterModeProperty = AvaloniaProperty.Register( @@ -152,13 +114,9 @@ namespace Avalonia.Controls validate: IsValidFilterMode); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty?> ItemFilterProperty = AvaloniaProperty.RegisterDirect?>( nameof(ItemFilter), @@ -166,13 +124,9 @@ namespace Avalonia.Controls (o, v) => o.ItemFilter = v); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty?> TextFilterProperty = AvaloniaProperty.RegisterDirect?>( nameof(TextFilter), @@ -181,13 +135,9 @@ namespace Avalonia.Controls unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty?> ItemSelectorProperty = AvaloniaProperty.RegisterDirect?>( nameof(ItemSelector), @@ -195,13 +145,9 @@ namespace Avalonia.Controls (o, v) => o.ItemSelector = v); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty?> TextSelectorProperty = AvaloniaProperty.RegisterDirect?>( nameof(TextSelector), @@ -209,13 +155,9 @@ namespace Avalonia.Controls (o, v) => o.TextSelector = v); /// - /// Identifies the - /// - /// dependency property. + /// Identifies the property. /// - /// The identifier for the - /// - /// dependency property. + /// The identifier for the property. public static readonly DirectProperty ItemsProperty = AvaloniaProperty.RegisterDirect( nameof(Items), @@ -230,14 +172,11 @@ namespace Avalonia.Controls /// /// Gets or sets the minimum number of characters required to be entered - /// in the text box before the - /// displays - /// possible matches. - /// matches. + /// in the text box before the displays possible matches. /// /// /// The minimum number of characters to be entered in the text box - /// before the + /// before the /// displays possible matches. The default is 1. /// /// @@ -291,12 +230,12 @@ namespace Avalonia.Controls /// /// Gets or sets the minimum delay, after text is typed /// in the text box before the - /// control + /// control /// populates the list of possible matches in the drop-down. /// /// The minimum delay, after text is typed in /// the text box, but before the - /// populates + /// populates /// the list of possible matches in the drop-down. The default is 0. public TimeSpan MinimumPopulateDelay { @@ -306,10 +245,10 @@ namespace Avalonia.Controls /// /// Gets or sets the maximum height of the drop-down portion of the - /// control. + /// control. /// /// The maximum height of the drop-down portion of the - /// control. + /// control. /// The default is . /// The specified value is less than 0. public double MaxDropDownHeight @@ -335,7 +274,7 @@ namespace Avalonia.Controls /// /// Gets or sets the that /// is used to get the values for display in the text portion of - /// the + /// the /// control. /// /// The object used @@ -372,10 +311,10 @@ namespace Avalonia.Controls /// /// Gets or sets the text in the text box portion of the - /// control. + /// control. /// /// The text in the text box portion of the - /// control. + /// control. public string? Text { get => _text; @@ -384,12 +323,10 @@ namespace Avalonia.Controls /// /// Gets the text that is used to filter items in the - /// - /// item collection. + /// item collection. /// /// The text that is used to filter items in the - /// - /// item collection. + /// item collection. /// /// The SearchText value is typically the same as the /// Text property, but is set after the TextChanged event occurs @@ -414,17 +351,13 @@ namespace Avalonia.Controls /// /// Gets or sets how the text in the text box is used to filter items - /// specified by the - /// + /// specified by the /// property for display in the drop-down. /// - /// One of the - /// - /// values The default is - /// . - /// The specified value is - /// not a valid - /// . + /// One of the + /// values The default is . + /// The specified value is not a valid + /// . /// /// Use the FilterMode property to specify how possible matches are /// filtered. For example, possible matches can be filtered in a @@ -445,13 +378,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that uses user-entered text to filter - /// the items specified by the - /// + /// the items specified by the /// property for display in the drop-down. /// /// The custom method that uses the user-entered text to filter - /// the items specified by the - /// + /// the items specified by the /// property. The default is null. /// /// The filter mode is automatically set to Custom if you set the @@ -465,13 +396,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that uses the user-entered text to - /// filter items specified by the - /// + /// filter items specified by the /// property in a text-based way for display in the drop-down. /// /// The custom method that uses the user-entered text to filter - /// items specified by the - /// + /// items specified by the /// property in a text-based way for display in the drop-down. /// /// The search mode is automatically set to Custom if you set the @@ -485,13 +414,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that combines the user-entered - /// text and one of the items specified by the - /// . + /// text and one of the items specified by the . /// /// /// The custom method that combines the user-entered - /// text and one of the items specified by the - /// . + /// text and one of the items specified by the . /// public AutoCompleteSelector? ItemSelector { @@ -502,13 +429,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that combines the user-entered /// text and one of the items specified by the - /// - /// in a text-based way. + /// in a text-based way. /// /// /// The custom method that combines the user-entered - /// text and one of the items specified by the - /// + /// text and one of the items specified by the /// in a text-based way. /// public AutoCompleteSelector? TextSelector @@ -525,12 +450,10 @@ namespace Avalonia.Controls /// /// Gets or sets a collection that is used to generate the items for the - /// drop-down portion of the - /// control. + /// drop-down portion of the control. /// /// The collection that is used to generate the items of the - /// drop-down portion of the - /// control. + /// drop-down portion of the control. public IEnumerable? Items { get => _itemsEnumerable; From 44e416e2cd2d6a4c75ae6ac3b5b1da6edc330b01 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 14 Oct 2022 21:19:28 -0400 Subject: [PATCH 21/40] AutoCompleteFilterMode comment fixes/improvements --- .../AutoCompleteBox/AutoCompleteFilterMode.cs | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs index 9aea49aef3..c17f5a19ab 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs @@ -3,13 +3,13 @@ // Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using System; + namespace Avalonia.Controls { /// - /// Specifies how text in the text box portion of the - /// control is used - /// to filter items specified by the - /// + /// Specifies how text in the text box portion of the + /// control is used to filter items specified by the /// property for display in the drop-down. /// public enum AutoCompleteFilterMode @@ -22,9 +22,9 @@ namespace Avalonia.Controls /// /// Specifies a culture-sensitive, case-insensitive filter where the /// returned items start with the specified text. The filter uses the - /// + /// /// method, specifying - /// as + /// as /// the string comparison criteria. /// StartsWith = 1, @@ -32,9 +32,9 @@ namespace Avalonia.Controls /// /// Specifies a culture-sensitive, case-sensitive filter where the /// returned items start with the specified text. The filter uses the - /// + /// /// method, specifying - /// as the string + /// as the string /// comparison criteria. /// StartsWithCaseSensitive = 2, @@ -42,9 +42,9 @@ namespace Avalonia.Controls /// /// Specifies an ordinal, case-insensitive filter where the returned /// items start with the specified text. The filter uses the - /// + /// /// method, specifying - /// as the + /// as the /// string comparison criteria. /// StartsWithOrdinal = 3, @@ -52,8 +52,8 @@ namespace Avalonia.Controls /// /// Specifies an ordinal, case-sensitive filter where the returned items /// start with the specified text. The filter uses the - /// - /// method, specifying as + /// + /// method, specifying as /// the string comparison criteria. /// StartsWithOrdinalCaseSensitive = 4, @@ -85,9 +85,9 @@ namespace Avalonia.Controls /// /// Specifies a culture-sensitive, case-insensitive filter where the /// returned items equal the specified text. The filter uses the - /// + /// /// method, specifying - /// as + /// as /// the search comparison criteria. /// Equals = 9, @@ -95,9 +95,9 @@ namespace Avalonia.Controls /// /// Specifies a culture-sensitive, case-sensitive filter where the /// returned items equal the specified text. The filter uses the - /// + /// /// method, specifying - /// as the string + /// as the string /// comparison criteria. /// EqualsCaseSensitive = 10, @@ -105,9 +105,9 @@ namespace Avalonia.Controls /// /// Specifies an ordinal, case-insensitive filter where the returned /// items equal the specified text. The filter uses the - /// + /// /// method, specifying - /// as the + /// as the /// string comparison criteria. /// EqualsOrdinal = 11, @@ -115,17 +115,15 @@ namespace Avalonia.Controls /// /// Specifies an ordinal, case-sensitive filter where the returned items /// equal the specified text. The filter uses the - /// - /// method, specifying as + /// + /// method, specifying as /// the string comparison criteria. /// EqualsOrdinalCaseSensitive = 12, /// /// Specifies that a custom filter is used. This mode is used when the - /// - /// or - /// + /// or /// properties are set. /// Custom = 13, From 949cebed1a71cef4de7fedd4ebd6b73749f12c12 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 15 Oct 2022 20:52:11 +0800 Subject: [PATCH 22/40] fix: fix toggleswitch knob part name in fluent template. --- src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml index 1ba8c237ee..90993fcc64 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml @@ -135,7 +135,7 @@ - - - diff --git a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml index 379b92ea17..1badffef33 100644 --- a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml @@ -47,7 +47,7 @@ Theme="{StaticResource SimpleCaptionButton}"> - @@ -69,7 +69,7 @@ - @@ -89,17 +89,17 @@ - - - From 3184252633e0c7828c340eacf57920ecbda230ad Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 18 Oct 2022 01:24:35 +0800 Subject: [PATCH 39/40] fix: revert FullScreenButtonPath. --- src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml | 4 ++-- src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml index e06fa0c8d4..3b1071b36a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml @@ -41,7 +41,7 @@ Theme="{StaticResource FluentCaptionButton}" IsVisible="False"> - @@ -83,7 +83,7 @@ - diff --git a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml index 1badffef33..8c263d0231 100644 --- a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml @@ -47,7 +47,7 @@ Theme="{StaticResource SimpleCaptionButton}"> - @@ -92,7 +92,7 @@ - From 2f124a9a480e8a20373531d4e605d096572fc669 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 18 Oct 2022 01:31:08 +0800 Subject: [PATCH 40/40] fix: align with naming convention. --- src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml | 4 ++-- src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml index 3b1071b36a..71ae012289 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml @@ -61,7 +61,7 @@ - @@ -80,7 +80,7 @@ -