diff --git a/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject b/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/MobileSandbox.Browser.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject b/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/WindowsInteropTest.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject b/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/WindowsInteropTest.net6.0-windows.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
index 7f32536b11..9c30992624 100644
--- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
+++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
@@ -21,7 +21,7 @@ namespace ControlCatalog.ViewModels
public ListBoxPageViewModel()
{
Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
-
+
Selection = new SelectionModel();
Selection.Select(1);
@@ -34,7 +34,13 @@ namespace ControlCatalog.ViewModels
(t ? Avalonia.Controls.SelectionMode.Toggle : 0) |
(a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0));
- AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem()));
+ AddItemCommand = MiniCommand.Create(() =>
+ {
+ var item = GenerateItem();
+ Items.Add(item);
+ Selection.Clear();
+ Selection.Select(Items.Count - 1);
+ });
RemoveItemCommand = MiniCommand.Create(() =>
{
@@ -96,7 +102,7 @@ namespace ControlCatalog.ViewModels
public MiniCommand RemoveItemCommand { get; }
public MiniCommand SelectRandomItemCommand { get; }
- private ItemModel GenerateItem() => new ItemModel(_counter ++);
+ private ItemModel GenerateItem() => new ItemModel(_counter++);
}
///
diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
index 0c22213d33..b3f41eb420 100644
--- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs
+++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
@@ -334,7 +334,7 @@ namespace Avalonia
/// .
///
/// The type of the property change sender.
- /// /// The type of the property..
+ /// The type of the property.
/// The property changed observable.
///
/// The method to call. The parameters are the sender and the event args.
diff --git a/src/Avalonia.Base/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs
index 8211855324..84d74e888e 100644
--- a/src/Avalonia.Base/Media/EllipseGeometry.cs
+++ b/src/Avalonia.Base/Media/EllipseGeometry.cs
@@ -56,6 +56,10 @@ namespace Avalonia.Media
///
/// Gets or sets a rect that defines the bounds of the ellipse.
///
+ ///
+ /// When set, this takes priority over the other properties that define an
+ /// ellipse using a center point and X/Y-axis radii.
+ ///
public Rect Rect
{
get => GetValue(RectProperty);
@@ -65,6 +69,10 @@ namespace Avalonia.Media
///
/// Gets or sets a double that defines the radius in the X-axis of the ellipse.
///
+ ///
+ /// In order for this property to be used, must not be set
+ /// (equal to the default value).
+ ///
public double RadiusX
{
get => GetValue(RadiusXProperty);
@@ -74,6 +82,10 @@ namespace Avalonia.Media
///
/// Gets or sets a double that defines the radius in the Y-axis of the ellipse.
///
+ ///
+ /// In order for this property to be used, must not be set
+ /// (equal to the default value).
+ ///
public double RadiusY
{
get => GetValue(RadiusYProperty);
@@ -83,6 +95,10 @@ namespace Avalonia.Media
///
/// Gets or sets a point that defines the center of the ellipse.
///
+ ///
+ /// In order for this property to be used, must not be set
+ /// (equal to the default value).
+ ///
public Point Center
{
get => GetValue(CenterProperty);
@@ -92,7 +108,30 @@ namespace Avalonia.Media
///
public override Geometry Clone()
{
- return new EllipseGeometry(Rect);
+ // Note that the ellipse properties are used in two modes:
+ //
+ // 1. Rect-only Mode:
+ // Directly set the rectangle bounds the ellipse will fill
+ //
+ // 2. Center + Radii Mode:
+ // Set a center-point and then X/Y-axis radii that are used to
+ // calculate the rectangle bounds the ellipse will fill.
+ // This is the only mode supported by WPF.
+ //
+ // Rendering the ellipse will only ever use one of these two modes
+ // based on if the Rect property is set (not equal to default).
+ //
+ // This means it would normally be fine to copy ONLY the Rect property
+ // when it is set. However, while it would render the same, it isn't
+ // a true clone. We want to include all the properties here regardless
+ // of the rendering mode that will eventually be used.
+ return new EllipseGeometry()
+ {
+ Rect = Rect,
+ RadiusX = RadiusX,
+ RadiusY = RadiusY,
+ Center = Center,
+ };
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
index 3406432ce7..f418d4e14a 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
@@ -687,7 +687,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
///
/// This method resolves the sos and eos values for the run
/// and adds the run to the list
- /// ///
+ ///
/// The index of the start of the run (in x9 removed units)
/// The length of the run (in x9 removed units)
/// The level of the run
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index 6f62c3be1d..57fedb3d69 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Platform
/// Creates an ellipse geometry implementation.
///
/// The bounds of the ellipse.
- /// An ellipse geometry..
+ /// An ellipse geometry.
IGeometryImpl CreateEllipseGeometry(Rect rect);
///
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
index 5e76ee56cf..8f1aa1cb49 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
@@ -159,14 +159,15 @@ namespace Avalonia.Rendering.Composition.Server
_redrawRequested = false;
using (var targetContext = _renderTarget.CreateDrawingContext())
{
- var layerSize = Size * Scaling;
+ var size = Size;
+ var layerSize = size * Scaling;
if (layerSize != _layerSize || _layer == null || _layer.IsCorrupted)
{
_layer?.Dispose();
_layer = null;
- _layer = targetContext.CreateLayer(Size);
+ _layer = targetContext.CreateLayer(size);
_layerSize = layerSize;
- _dirtyRect = new Rect(0, 0, layerSize.Width, layerSize.Height);
+ _dirtyRect = new Rect(0, 0, size.Width, size.Height);
}
if (_dirtyRect.Width != 0 || _dirtyRect.Height != 0)
@@ -187,7 +188,7 @@ namespace Avalonia.Rendering.Composition.Server
else
targetContext.DrawBitmap(_layer, 1,
new Rect(_layerSize),
- new Rect(Size));
+ new Rect(size));
if (DebugOverlays != RendererDebugOverlays.None)
{
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index 3a82bf02e0..7dbb0872f5 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs
@@ -306,7 +306,7 @@ namespace Avalonia.Utilities
/// if the value could not be converted.
///
/// The value to convert.
- /// The type to convert to..
+ /// The type to convert to.
/// The culture to use.
/// A value of .
[RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)]
diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
index 0c9bb89caa..4029782772 100644
--- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
+++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
@@ -216,7 +216,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
///
/// If the adjusted position also ends up being constrained, the resulting position of the
/// FlipX adjustment will be the one before the adjustment.
- /// ///
+ ///
FlipX = 4,
///
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 0e627c2a37..e22d03273a 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -5,9 +5,7 @@ using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Xml.Linq;
using Avalonia.Controls.Selection;
-using Avalonia.Controls.Utils;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
@@ -171,7 +169,7 @@ namespace Avalonia.Controls.Primitives
///
public event EventHandler? SelectionChanged
{
- add => AddHandler(SelectionChangedEvent, value);
+ add => AddHandler(SelectionChangedEvent, value);
remove => RemoveHandler(SelectionChangedEvent, value);
}
@@ -369,7 +367,7 @@ namespace Avalonia.Controls.Primitives
///
public bool WrapSelection
{
- get => GetValue(WrapSelectionProperty);
+ get => GetValue(WrapSelectionProperty);
set => SetValue(WrapSelectionProperty, value);
}
@@ -382,7 +380,7 @@ namespace Avalonia.Controls.Primitives
///
protected SelectionMode SelectionMode
{
- get => GetValue(SelectionModeProperty);
+ get => GetValue(SelectionModeProperty);
set => SetValue(SelectionModeProperty, value);
}
@@ -465,7 +463,10 @@ namespace Avalonia.Controls.Primitives
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
- AutoScrollToSelectedItemIfNecessary();
+ if (Selection?.AnchorIndex is int index)
+ {
+ AutoScrollToSelectedItemIfNecessary(index);
+ }
}
///
@@ -476,7 +477,10 @@ namespace Avalonia.Controls.Primitives
void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e)
{
LayoutUpdated -= ExecuteScrollWhenLayoutUpdated;
- AutoScrollToSelectedItemIfNecessary();
+ if (Selection?.AnchorIndex is int index)
+ {
+ AutoScrollToSelectedItemIfNecessary(index);
+ }
}
if (AutoScrollToSelectedItem)
@@ -529,7 +533,16 @@ namespace Avalonia.Controls.Primitives
protected internal override void ClearContainerForItemOverride(Control element)
{
base.ClearContainerForItemOverride(element);
- element.ClearValue(IsSelectedProperty);
+
+ try
+ {
+ _ignoreContainerSelectionChanged = true;
+ element.ClearValue(IsSelectedProperty);
+ }
+ finally
+ {
+ _ignoreContainerSelectionChanged = false;
+ }
}
///
@@ -625,7 +638,10 @@ namespace Avalonia.Controls.Primitives
if (change.Property == AutoScrollToSelectedItemProperty)
{
- AutoScrollToSelectedItemIfNecessary();
+ if (Selection?.AnchorIndex is int index)
+ {
+ AutoScrollToSelectedItemIfNecessary(index);
+ }
}
else if (change.Property == SelectionModeProperty && _selection is object)
{
@@ -909,8 +925,11 @@ namespace Avalonia.Controls.Primitives
if (e.PropertyName == nameof(ISelectionModel.AnchorIndex))
{
_hasScrolledToSelectedItem = false;
- KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(Selection.AnchorIndex));
- AutoScrollToSelectedItemIfNecessary();
+ if (Selection?.AnchorIndex is int index)
+ {
+ KeyboardNavigation.SetTabOnceActiveElement(this, ContainerFromIndex(index));
+ AutoScrollToSelectedItemIfNecessary(index);
+ }
}
else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex) && _oldSelectedIndex != SelectedIndex)
{
@@ -1038,7 +1057,7 @@ namespace Avalonia.Controls.Primitives
return value;
}
else
- {
+ {
return AvaloniaProperty.UnsetValue;
}
}
@@ -1096,16 +1115,19 @@ namespace Avalonia.Controls.Primitives
}
}
- private void AutoScrollToSelectedItemIfNecessary()
+ private void AutoScrollToSelectedItemIfNecessary(int anchorIndex)
{
if (AutoScrollToSelectedItem &&
!_hasScrolledToSelectedItem &&
Presenter is object &&
- Selection.AnchorIndex >= 0 &&
+ anchorIndex >= 0 &&
IsAttachedToVisualTree)
{
- ScrollIntoView(Selection.AnchorIndex);
- _hasScrolledToSelectedItem = true;
+ Dispatcher.UIThread.Post(state =>
+ {
+ ScrollIntoView((int)state!);
+ _hasScrolledToSelectedItem = true;
+ }, anchorIndex);
}
}
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
index 49af6a71a0..74f12280bb 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
@@ -295,10 +295,7 @@ namespace Avalonia.Controls.Remote.Server
lock (_lock)
{
- // Ideally we should only send a frame if its status is Rendered: since the renderer might not be
- // initialized at the start, we're sending black frames in this case. However, this was the historical
- // behavior and some external programs are depending on receiving a frame asap.
- if (_lastReceivedFrame != _lastSentFrame || _framebuffer.GetStatus() == FrameStatus.CopiedToMessage)
+ if (_lastReceivedFrame != _lastSentFrame || _framebuffer.GetStatus() != FrameStatus.Rendered)
return;
framebuffer = _framebuffer;
diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs
index 68bad598d0..69bed2550e 100644
--- a/src/Avalonia.Controls/Selection/SelectionModel.cs
+++ b/src/Avalonia.Controls/Selection/SelectionModel.cs
@@ -277,7 +277,7 @@ namespace Avalonia.Controls.Selection
{
if (base.Source != value)
{
- if (_operation is not null)
+ if (_operation?.UpdateCount > 0)
{
throw new InvalidOperationException("Cannot change source while update is in progress.");
}
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 57d709ba94..ea420c7c45 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -668,17 +668,7 @@ namespace Avalonia.Controls
if (HasComplexContent)
{
- if (_textRuns != null)
- {
- foreach (var textRun in _textRuns)
- {
- if (textRun is EmbeddedControlRun controlRun &&
- controlRun.Control is Control control)
- {
- VisualChildren.Remove(control);
- }
- }
- }
+ VisualChildren.Clear();
var textRuns = new List();
diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs
index 21b9c9b765..bf540698f1 100644
--- a/src/Avalonia.Controls/TransitioningContentControl.cs
+++ b/src/Avalonia.Controls/TransitioningContentControl.cs
@@ -15,8 +15,9 @@ namespace Avalonia.Controls;
public class TransitioningContentControl : ContentControl
{
private CancellationTokenSource? _currentTransition;
- private ContentPresenter? _transitionPresenter;
- private Optional