diff --git a/build/ApiCompatAttributeExcludeList.txt b/build/ApiCompatAttributeExcludeList.txt
new file mode 100644
index 0000000000..1df5a30ec3
--- /dev/null
+++ b/build/ApiCompatAttributeExcludeList.txt
@@ -0,0 +1,2 @@
+T:Avalonia.Metadata.NotClientImplementableAttribute
+T:Avalonia.Metadata.UnstableAttribute
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index 314d38190a..9448a31d73 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -3,8 +3,6 @@
-
-
diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
index 314c579b76..0fa4540726 100644
--- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
+++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
@@ -85,7 +85,7 @@
_settingSize = true;
[super setFrameSize:newSize];
- auto window = static_cast>([self window]);
+ auto window = (id ) [self window];
// TODO get actual titlebar size
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
index 6ff19ead68..54bfe6e38a 100644
--- a/native/Avalonia.Native/src/OSX/AvnWindow.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -174,6 +174,11 @@
[self setBackgroundColor: [NSColor clearColor]];
_isExtended = false;
+
+#ifdef IS_NSPANEL
+ [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary];
+#endif
+
return self;
}
@@ -194,6 +199,8 @@
[self backingScaleFactor];
}
+
+
- (void)windowWillClose:(NSNotification *)notification
{
_closed = true;
@@ -212,11 +219,14 @@
// If the window has a child window being shown as a dialog then don't allow it to become the key window.
for(NSWindow* uch in [self childWindows])
{
- auto ch = static_cast>(uch);
- if(ch == nil)
+ if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)])
+ {
continue;
- if (ch.isDialog)
- return false;
+ }
+
+ id ch = (id ) uch;
+
+ return !ch.isDialog;
}
return true;
@@ -372,6 +382,11 @@
if(cparent != nullptr)
{
+ if(!cparent->IsShown())
+ {
+ return;
+ }
+
if(cparent->WindowState() == Maximized)
{
cparent->SetWindowState(Normal);
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm
index 64a8780158..cf3ecefb3c 100644
--- a/native/Avalonia.Native/src/OSX/PopupImpl.mm
+++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm
@@ -50,11 +50,17 @@ protected:
return S_OK;
}
}
+
public:
virtual bool ShouldTakeFocusOnShow() override
{
return false;
}
+
+ virtual HRESULT Show(bool activate, bool isDialog) override
+ {
+ return WindowBaseImpl::Show(activate, true);
+ }
};
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
index eff13bcb23..0e482f9f30 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
@@ -60,6 +60,8 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT Show(bool activate, bool isDialog) override;
+ virtual bool IsShown ();
+
virtual bool ShouldTakeFocusOnShow();
virtual HRESULT Hide() override;
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
index db5eb54e3f..b84e999825 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -30,8 +30,8 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl)
View = [[AvnView alloc] initWithParent:this];
StandardContainer = [[AutoFitContentView new] initWithContent:View];
- lastPositionSet.X = 100;
- lastPositionSet.Y = 100;
+ lastPositionSet.X = -1;
+ lastPositionSet.Y = -1;
lastSize = NSSize { 100, 100 };
lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
lastMinSize = NSSize { 0, 0 };
@@ -92,7 +92,11 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
CreateNSWindow(isDialog);
InitialiseNSWindow();
- SetPosition(lastPositionSet);
+ if(lastPositionSet.X >= 0 && lastPositionSet.Y >= 0)
+ {
+ SetPosition(lastPositionSet);
+ }
+
UpdateStyle();
[Window setTitle:_lastTitle];
@@ -112,6 +116,11 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
}
}
+bool WindowBaseImpl::IsShown ()
+{
+ return _shown;
+}
+
bool WindowBaseImpl::ShouldTakeFocusOnShow() {
return true;
}
@@ -191,9 +200,8 @@ HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
if (ret == nullptr)
return E_POINTER;
- auto frame = [View frame];
- ret->Width = frame.size.width;
- ret->Height = frame.size.height;
+ ret->Width = lastSize.width;
+ ret->Height = lastSize.height;
return S_OK;
}
@@ -206,9 +214,11 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
if (ret == nullptr)
return E_POINTER;
- auto frame = [Window frame];
- ret->Width = frame.size.width;
- ret->Height = frame.size.height;
+ if(Window != nullptr){
+ auto frame = [Window frame];
+ ret->Width = frame.size.width;
+ ret->Height = frame.size.height;
+ }
return S_OK;
}
@@ -354,12 +364,17 @@ HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) {
return E_POINTER;
}
- auto frame = [Window frame];
+ if(Window != nullptr) {
+ auto frame = [Window frame];
- ret->X = frame.origin.x;
- ret->Y = frame.origin.y + frame.size.height;
+ ret->X = frame.origin.x;
+ ret->Y = frame.origin.y + frame.size.height;
- *ret = ConvertPointY(*ret);
+ *ret = ConvertPointY(*ret);
+ } else
+ {
+ *ret = lastPositionSet;
+ }
return S_OK;
}
@@ -370,7 +385,10 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
@autoreleasepool {
lastPositionSet = point;
- [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+
+ if(Window != nullptr) {
+ [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+ }
return S_OK;
}
@@ -559,6 +577,7 @@ void WindowBaseImpl::InitialiseNSWindow() {
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
+ [Window center];
if (lastMenu != nullptr) {
[GetWindowProtocol() applyMenu:lastMenu];
@@ -576,7 +595,7 @@ id WindowBaseImpl::GetWindowProtocol() {
return nullptr;
}
- return static_cast>(Window);
+ return (id ) Window;
}
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index 7ab2b2b5fc..5b15b4cdfc 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -430,6 +430,9 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
START_COM_CALL;
@autoreleasepool {
+ auto currentState = _actualWindowState;
+ _lastWindowState = state;
+
if (Window == nullptr) {
return S_OK;
}
@@ -440,9 +443,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
_inSetWindowState = true;
- auto currentState = _actualWindowState;
- _lastWindowState = state;
-
if (currentState == Normal) {
_preZoomSize = [Window frame];
}
diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj
index 2c6ff74e5e..bd6054327f 100644
--- a/samples/BindingDemo/BindingDemo.csproj
+++ b/samples/BindingDemo/BindingDemo.csproj
@@ -5,6 +5,7 @@
+
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index 7cbd8a3f9c..e5f07c90c3 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -25,6 +25,8 @@
+
+
diff --git a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
index 2fe16ba8e3..aef96802f4 100644
--- a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
+++ b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
@@ -10,8 +10,7 @@
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
-
+
+
diff --git a/samples/IntegrationTestApp/IntegrationTestApp.csproj b/samples/IntegrationTestApp/IntegrationTestApp.csproj
index e8338adae6..4284399357 100644
--- a/samples/IntegrationTestApp/IntegrationTestApp.csproj
+++ b/samples/IntegrationTestApp/IntegrationTestApp.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
net6.0
@@ -18,6 +18,7 @@
+
diff --git a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
index 9660d2a90d..4f7f06b529 100644
--- a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
+++ b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
@@ -7,6 +7,7 @@
+
diff --git a/samples/Previewer/Previewer.csproj b/samples/Previewer/Previewer.csproj
index c1d14cba26..98560e9ab0 100644
--- a/samples/Previewer/Previewer.csproj
+++ b/samples/Previewer/Previewer.csproj
@@ -9,6 +9,9 @@
+
+
+
diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj
index 18a4ee5662..3c62af1eaf 100644
--- a/samples/RenderDemo/RenderDemo.csproj
+++ b/samples/RenderDemo/RenderDemo.csproj
@@ -12,6 +12,7 @@
+
diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj
index f3c38cd96e..eab654acb6 100644
--- a/samples/Sandbox/Sandbox.csproj
+++ b/samples/Sandbox/Sandbox.csproj
@@ -10,6 +10,7 @@
+
diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj
index 2c6ff74e5e..bd6054327f 100644
--- a/samples/VirtualizationDemo/VirtualizationDemo.csproj
+++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj
@@ -5,6 +5,7 @@
+
diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
index cd9963a2e5..81b94b4d09 100644
--- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
+++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
@@ -22,6 +22,7 @@
+
diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
index c25442b52c..2f3ea85e46 100644
--- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
+++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Avalonia.Base/Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs
index 436a765d27..eda86ed106 100644
--- a/src/Avalonia.Base/Animation/IAnimation.cs
+++ b/src/Avalonia.Base/Animation/IAnimation.cs
@@ -1,12 +1,14 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using Avalonia.Metadata;
namespace Avalonia.Animation
{
///
/// Interface for Animation objects
///
+ [NotClientImplementable]
public interface IAnimation
{
///
diff --git a/src/Avalonia.Base/Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs
index 6a1d3539e2..8c4ba95517 100644
--- a/src/Avalonia.Base/Animation/IAnimationSetter.cs
+++ b/src/Avalonia.Base/Animation/IAnimationSetter.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Animation
{
+ [NotClientImplementable]
public interface IAnimationSetter
{
AvaloniaProperty? Property { get; set; }
diff --git a/src/Avalonia.Base/Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs
index f64ac9f913..06ba8dc329 100644
--- a/src/Avalonia.Base/Animation/IAnimator.cs
+++ b/src/Avalonia.Base/Animation/IAnimator.cs
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
+using Avalonia.Metadata;
namespace Avalonia.Animation
{
///
/// Interface for Animator objects
///
+ [NotClientImplementable]
public interface IAnimator : IList
{
///
diff --git a/src/Avalonia.Base/Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs
index ae44102077..7b3b7b3924 100644
--- a/src/Avalonia.Base/Animation/IClock.cs
+++ b/src/Avalonia.Base/Animation/IClock.cs
@@ -1,9 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.Text;
+using Avalonia.Metadata;
namespace Avalonia.Animation
{
+ [NotClientImplementable]
public interface IClock : IObservable
{
PlayState PlayState { get; set; }
diff --git a/src/Avalonia.Base/Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs
index b0455e2c80..138dc07539 100644
--- a/src/Avalonia.Base/Animation/IGlobalClock.cs
+++ b/src/Avalonia.Base/Animation/IGlobalClock.cs
@@ -1,9 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using Avalonia.Metadata;
namespace Avalonia.Animation
{
+ [NotClientImplementable]
public interface IGlobalClock : IClock
{
}
diff --git a/src/Avalonia.Base/Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs
index 241ca208d1..639b92ce35 100644
--- a/src/Avalonia.Base/Animation/ITransition.cs
+++ b/src/Avalonia.Base/Animation/ITransition.cs
@@ -1,10 +1,12 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Animation
{
///
/// Interface for Transition objects.
///
+ [NotClientImplementable]
public interface ITransition
{
///
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index 9972e72dd4..a05c9c5da3 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaList.cs
@@ -394,7 +394,13 @@ namespace Avalonia.Collections
} while (en.MoveNext());
if (notificationItems is not null)
+ {
NotifyAdd(notificationItems, index);
+ }
+ else
+ {
+ NotifyCountChanged();
+ }
}
}
}
diff --git a/src/Avalonia.Base/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs
index 1ca7db2f37..a12e88c055 100644
--- a/src/Avalonia.Base/Controls/INameScope.cs
+++ b/src/Avalonia.Base/Controls/INameScope.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Threading.Tasks;
+using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls
@@ -7,6 +6,7 @@ namespace Avalonia.Controls
///
/// Defines a name scope.
///
+ [NotClientImplementable]
public interface INameScope
{
///
diff --git a/src/Avalonia.Base/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs
index 27290716d0..eda521727f 100644
--- a/src/Avalonia.Base/Controls/IPseudoClasses.cs
+++ b/src/Avalonia.Base/Controls/IPseudoClasses.cs
@@ -1,9 +1,11 @@
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
///
/// Exposes an interface for setting pseudoclasses on a collection.
///
+ [NotClientImplementable]
public interface IPseudoClasses
{
///
diff --git a/src/Avalonia.Base/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs
index ea34a8b39a..286f0e36ef 100644
--- a/src/Avalonia.Base/Controls/IResourceHost.cs
+++ b/src/Avalonia.Base/Controls/IResourceHost.cs
@@ -1,6 +1,5 @@
using System;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
@@ -10,6 +9,7 @@ namespace Avalonia.Controls
///
/// This interface is implemented by and `Application`.
///
+ [NotClientImplementable]
public interface IResourceHost : IResourceNode
{
///
diff --git a/src/Avalonia.Base/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs
index 73bfeaf161..d6c900f97f 100644
--- a/src/Avalonia.Base/Controls/IResourceNode.cs
+++ b/src/Avalonia.Base/Controls/IResourceNode.cs
@@ -1,6 +1,5 @@
using System;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
@@ -12,6 +11,7 @@ namespace Avalonia.Controls
/// () and resource providers such as
/// (see ).
///
+ [NotClientImplementable]
public interface IResourceNode
{
///
diff --git a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs
index dbf8c68892..e85e025005 100644
--- a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs
+++ b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs
@@ -1,4 +1,4 @@
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
@@ -10,6 +10,7 @@ namespace Avalonia.Controls
/// Additionally, also sets the inheritance parent; this
/// interface is only needed where the logical and inheritance parents differ.
///
+ [NotClientImplementable]
public interface ISetInheritanceParent
{
///
diff --git a/src/Avalonia.Base/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs
index 85bda05961..7f1b4b5d87 100644
--- a/src/Avalonia.Base/Controls/ISetLogicalParent.cs
+++ b/src/Avalonia.Base/Controls/ISetLogicalParent.cs
@@ -1,6 +1,5 @@
using Avalonia.LogicalTree;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
@@ -10,6 +9,7 @@ namespace Avalonia.Controls
///
/// You should not usually need to use this interface - it is for advanced scenarios only.
///
+ [NotClientImplementable]
public interface ISetLogicalParent
{
///
diff --git a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs
index 4d80feb4ba..0cb8a937cc 100644
--- a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs
+++ b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Data.Core
{
+ [NotClientImplementable]
public interface IPropertyInfo
{
string Name { get; }
diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
index 33cecd10a7..b93bf87fdf 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Data.Core.Plugins
new Dictionary<(Type, string), PropertyInfo?>();
///
- public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null;
+ public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null;
///
/// Starts monitoring the value of a property on an object.
@@ -36,7 +36,7 @@ namespace Avalonia.Data.Core.Plugins
if (!reference.TryGetTarget(out var instance) || instance is null)
return null;
- var p = GetFirstPropertyWithName(instance.GetType(), propertyName);
+ var p = GetFirstPropertyWithName(instance, propertyName);
if (p != null)
{
@@ -50,8 +50,16 @@ namespace Avalonia.Data.Core.Plugins
}
}
- private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName)
+ private const BindingFlags PropertyBindingFlags =
+ BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
+
+ private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName)
{
+ if (instance is IReflectableType reflectableType)
+ return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags);
+
+ var type = instance.GetType();
+
var key = (type, propertyName);
if (!_propertyLookup.TryGetValue(key, out var propertyInfo))
@@ -66,10 +74,7 @@ namespace Avalonia.Data.Core.Plugins
{
PropertyInfo? found = null;
- const BindingFlags bindingFlags =
- BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
-
- var properties = type.GetProperties(bindingFlags);
+ var properties = type.GetProperties(PropertyBindingFlags);
foreach (PropertyInfo propertyInfo in properties)
{
diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs
index 9535cf608e..7d44bf09b5 100644
--- a/src/Avalonia.Base/Data/IBinding.cs
+++ b/src/Avalonia.Base/Data/IBinding.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Data
{
///
/// Holds a binding that can be applied to a property on an object.
///
+ [NotClientImplementable]
public interface IBinding
{
///
diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs
index 9c1ffce24c..efcb7dfecb 100644
--- a/src/Avalonia.Base/DirectPropertyBase.cs
+++ b/src/Avalonia.Base/DirectPropertyBase.cs
@@ -2,7 +2,6 @@
using Avalonia.Data;
using Avalonia.Reactive;
using Avalonia.Styling;
-using Avalonia.Utilities;
namespace Avalonia
{
@@ -188,10 +187,10 @@ namespace Avalonia
}
else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType))
{
- return new PropertySetterLazyInstance(
+ return new PropertySetterTemplateInstance(
target,
this,
- () => (TValue)template.Build());
+ template);
}
else
{
diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs
index 00f5062f9e..3b0016903b 100644
--- a/src/Avalonia.Base/IAvaloniaObject.cs
+++ b/src/Avalonia.Base/IAvaloniaObject.cs
@@ -1,11 +1,13 @@
using System;
using Avalonia.Data;
+using Avalonia.Metadata;
namespace Avalonia
{
///
/// Interface for getting/setting values on an object.
///
+ [NotClientImplementable]
public interface IAvaloniaObject
{
///
diff --git a/src/Avalonia.Base/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs
index 1172adcaa4..1d381cf4a5 100644
--- a/src/Avalonia.Base/IDataContextProvider.cs
+++ b/src/Avalonia.Base/IDataContextProvider.cs
@@ -1,10 +1,11 @@
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia
{
///
/// Defines an element with a data context that can be used for binding.
///
+ [NotClientImplementable]
public interface IDataContextProvider : IAvaloniaObject
{
///
diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs
index e34483fa7b..476a36f372 100644
--- a/src/Avalonia.Base/IDirectPropertyAccessor.cs
+++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia
{
diff --git a/src/Avalonia.Base/IDirectPropertyMetadata.cs b/src/Avalonia.Base/IDirectPropertyMetadata.cs
index 4fd4a3a6b7..7d74470a13 100644
--- a/src/Avalonia.Base/IDirectPropertyMetadata.cs
+++ b/src/Avalonia.Base/IDirectPropertyMetadata.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia
{
///
/// Untyped interface to
///
+ [NotClientImplementable]
public interface IDirectPropertyMetadata
{
///
diff --git a/src/Avalonia.Base/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs
index a068d4a5bf..4eed54de45 100644
--- a/src/Avalonia.Base/IStyledElement.cs
+++ b/src/Avalonia.Base/IStyledElement.cs
@@ -2,12 +2,12 @@
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.LogicalTree;
+using Avalonia.Metadata;
using Avalonia.Styling;
-#nullable enable
-
namespace Avalonia
{
+ [NotClientImplementable]
public interface IStyledElement :
IStyleable,
IStyleHost,
diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs
index 6b29b5f977..89990338e0 100644
--- a/src/Avalonia.Base/IStyledPropertyMetadata.cs
+++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia
{
///
/// Untyped interface to
///
+ [NotClientImplementable]
public interface IStyledPropertyMetadata
{
///
diff --git a/src/Avalonia.Base/Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs
index e484d003c7..93a50968e2 100644
--- a/src/Avalonia.Base/Input/IAccessKeyHandler.cs
+++ b/src/Avalonia.Base/Input/IAccessKeyHandler.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
///
/// Defines the interface for classes that handle access keys for a window.
///
+ [Unstable]
public interface IAccessKeyHandler
{
///
diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs
index 2510479a8e..0c85cad2f7 100644
--- a/src/Avalonia.Base/Input/IFocusManager.cs
+++ b/src/Avalonia.Base/Input/IFocusManager.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
///
/// Manages focus for the application.
///
+ [NotClientImplementable]
public interface IFocusManager
{
///
diff --git a/src/Avalonia.Base/Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs
index ab0fae65df..fe4f584f18 100644
--- a/src/Avalonia.Base/Input/IInputDevice.cs
+++ b/src/Avalonia.Base/Input/IInputDevice.cs
@@ -1,7 +1,9 @@
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
+ [NotClientImplementable]
public interface IInputDevice
{
///
diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs
index d1552a3a2a..78001143d7 100644
--- a/src/Avalonia.Base/Input/IInputElement.cs
+++ b/src/Avalonia.Base/Input/IInputElement.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Avalonia.Interactivity;
+using Avalonia.Metadata;
using Avalonia.VisualTree;
#nullable enable
@@ -10,6 +11,7 @@ namespace Avalonia.Input
///
/// Defines input-related functionality for a control.
///
+ [NotClientImplementable]
public interface IInputElement : IInteractive, IVisual
{
///
diff --git a/src/Avalonia.Base/Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs
index 80b71d3e47..658226a519 100644
--- a/src/Avalonia.Base/Input/IInputManager.cs
+++ b/src/Avalonia.Base/Input/IInputManager.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
@@ -7,6 +8,7 @@ namespace Avalonia.Input
/// Receives input from the windowing subsystem and dispatches it to interested parties
/// for processing.
///
+ [NotClientImplementable]
public interface IInputManager
{
///
diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs
index 98e8699573..7edc69df52 100644
--- a/src/Avalonia.Base/Input/IInputRoot.cs
+++ b/src/Avalonia.Base/Input/IInputRoot.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
///
/// Defines the interface for top-level input elements.
///
+ [NotClientImplementable]
public interface IInputRoot : IInputElement
{
///
diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs
index d0e84e5ad0..c8db8bf16f 100644
--- a/src/Avalonia.Base/Input/IKeyboardDevice.cs
+++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
@@ -50,6 +51,7 @@ namespace Avalonia.Input
KeyboardMask = Alt | Control | Shift | Meta
}
+ [NotClientImplementable]
public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged
{
IInputElement? FocusedElement { get; }
diff --git a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs
index 88d00b3b50..3bcd6d6206 100644
--- a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs
+++ b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
///
/// Defines the interface for classes that handle keyboard navigation for a window.
///
+ [Unstable]
public interface IKeyboardNavigationHandler
{
///
diff --git a/src/Avalonia.Base/Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs
index 67b58c0ffc..213a979c28 100644
--- a/src/Avalonia.Base/Input/IMainMenu.cs
+++ b/src/Avalonia.Base/Input/IMainMenu.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Interactivity;
+using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Input
@@ -7,6 +8,7 @@ namespace Avalonia.Input
///
/// Defines the interface for a window's main menu.
///
+ [NotClientImplementable]
public interface IMainMenu : IVisual
{
///
diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs
index 6b7f0e76e5..2d66397d63 100644
--- a/src/Avalonia.Base/Input/IMouseDevice.cs
+++ b/src/Avalonia.Base/Input/IMouseDevice.cs
@@ -1,10 +1,12 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
///
/// Represents a mouse device.
///
+ [NotClientImplementable]
public interface IMouseDevice : IPointerDevice
{
///
diff --git a/src/Avalonia.Base/Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs
index 7af48cef82..66aeacadc9 100644
--- a/src/Avalonia.Base/Input/IPointer.cs
+++ b/src/Avalonia.Base/Input/IPointer.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
+ [NotClientImplementable]
public interface IPointer
{
int Id { get; }
diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs
index 0096bb77bf..0993835feb 100644
--- a/src/Avalonia.Base/Input/IPointerDevice.cs
+++ b/src/Avalonia.Base/Input/IPointerDevice.cs
@@ -1,9 +1,11 @@
using System;
using Avalonia.VisualTree;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
+ [NotClientImplementable]
public interface IPointerDevice : IInputDevice
{
///
diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs
index 9fe07f62dd..f4e25ebada 100644
--- a/src/Avalonia.Base/Input/InputElement.cs
+++ b/src/Avalonia.Base/Input/InputElement.cs
@@ -94,7 +94,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent KeyDownEvent =
RoutedEvent.Register(
- "KeyDown",
+ nameof(KeyDown),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -102,7 +102,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent KeyUpEvent =
RoutedEvent.Register(
- "KeyUp",
+ nameof(KeyUp),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -116,7 +116,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent TextInputEvent =
RoutedEvent.Register(
- "TextInput",
+ nameof(TextInput),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -124,7 +124,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent TextInputMethodClientRequestedEvent =
RoutedEvent.Register(
- "TextInputMethodClientRequested",
+ nameof(TextInputMethodClientRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -144,7 +144,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent PointerMovedEvent =
RoutedEvent.Register(
- "PointerMove",
+ nameof(PointerMoved),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -152,7 +152,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent PointerPressedEvent =
RoutedEvent.Register(
- "PointerPressed",
+ nameof(PointerPressed),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -160,7 +160,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent PointerReleasedEvent =
RoutedEvent.Register(
- "PointerReleased",
+ nameof(PointerReleased),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -168,7 +168,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent PointerCaptureLostEvent =
RoutedEvent.Register(
- "PointerCaptureLost",
+ nameof(PointerCaptureLost),
RoutingStrategies.Direct);
///
@@ -176,7 +176,7 @@ namespace Avalonia.Input
///
public static readonly RoutedEvent PointerWheelChangedEvent =
RoutedEvent.Register(
- "PointerWheelChanged",
+ nameof(PointerWheelChanged),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
diff --git a/src/Avalonia.Base/Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs
index eb880904eb..bf2a5a8602 100644
--- a/src/Avalonia.Base/Input/Platform/IClipboard.cs
+++ b/src/Avalonia.Base/Input/Platform/IClipboard.cs
@@ -1,7 +1,9 @@
using System.Threading.Tasks;
+using Avalonia.Metadata;
namespace Avalonia.Input.Platform
{
+ [NotClientImplementable]
public interface IClipboard
{
Task GetTextAsync();
diff --git a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs
index 30d8ee5337..8d1b93087e 100644
--- a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs
+++ b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs
@@ -1,7 +1,9 @@
using System.Threading.Tasks;
+using Avalonia.Metadata;
namespace Avalonia.Input.Platform
{
+ [Unstable]
public interface IPlatformDragSource
{
Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects);
diff --git a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs
index f7b7914bd1..3bcc9fadd3 100644
--- a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs
+++ b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs
@@ -1,5 +1,8 @@
-namespace Avalonia.Input.Raw
+using Avalonia.Metadata;
+
+namespace Avalonia.Input.Raw
{
+ [NotClientImplementable]
public interface IDragDropDevice : IInputDevice
{
}
diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs
index 4404c903b7..be7ad81f81 100644
--- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs
+++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input.TextInput
{
+ [Unstable]
public interface ITextInputMethodImpl
{
void SetClient(ITextInputMethodClient? client);
@@ -8,6 +11,7 @@ namespace Avalonia.Input.TextInput
void Reset();
}
+ [NotClientImplementable]
public interface ITextInputMethodRoot : IInputRoot
{
ITextInputMethodImpl? InputMethod { get; }
diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs
index 6d7dcd64f4..980bf54f1f 100644
--- a/src/Avalonia.Base/Interactivity/IInteractive.cs
+++ b/src/Avalonia.Base/Interactivity/IInteractive.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Metadata;
#nullable enable
@@ -7,6 +8,7 @@ namespace Avalonia.Interactivity
///
/// Interface for objects that raise routed events.
///
+ [NotClientImplementable]
public interface IInteractive
{
///
diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs
index 614670a53b..143ce13a1b 100644
--- a/src/Avalonia.Base/Layout/ILayoutManager.cs
+++ b/src/Avalonia.Base/Layout/ILayoutManager.cs
@@ -1,12 +1,12 @@
using System;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Layout
{
///
/// Manages measuring and arranging of controls.
///
+ [NotClientImplementable]
public interface ILayoutManager : IDisposable
{
///
diff --git a/src/Avalonia.Base/Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs
index e2f16b338a..df15fc1a1d 100644
--- a/src/Avalonia.Base/Layout/ILayoutRoot.cs
+++ b/src/Avalonia.Base/Layout/ILayoutRoot.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Layout
{
///
/// Defines the root of a layoutable tree.
///
+ [NotClientImplementable]
public interface ILayoutRoot : ILayoutable
{
///
diff --git a/src/Avalonia.Base/Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs
index 54d3ba6a11..d8b546b04a 100644
--- a/src/Avalonia.Base/Layout/ILayoutable.cs
+++ b/src/Avalonia.Base/Layout/ILayoutable.cs
@@ -1,12 +1,12 @@
+using Avalonia.Metadata;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Layout
{
///
/// Defines layout-related functionality for a control.
///
+ [NotClientImplementable]
public interface ILayoutable : IVisual
{
///
diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs
index df7aa937a0..f30925f489 100644
--- a/src/Avalonia.Base/Layout/Layoutable.cs
+++ b/src/Avalonia.Base/Layout/Layoutable.cs
@@ -141,7 +141,6 @@ namespace Avalonia.Layout
static Layoutable()
{
AffectsMeasure(
- IsVisibleProperty,
WidthProperty,
HeightProperty,
MinWidthProperty,
@@ -791,6 +790,25 @@ namespace Avalonia.Layout
{
}
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == IsVisibleProperty)
+ {
+ DesiredSize = default;
+
+ // All changes to visibility cause the parent element to be notified.
+ this.GetVisualParent()?.ChildDesiredSizeChanged(this);
+
+ // We only invalidate outselves when visibility is changed to true.
+ if (change.GetNewValue())
+ {
+ InvalidateMeasure();
+ }
+ }
+ }
+
///
protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent)
{
diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs
index 418cd55e41..47c994a350 100644
--- a/src/Avalonia.Base/Layout/UniformGridLayout.cs
+++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs
@@ -116,7 +116,7 @@ namespace Avalonia.Layout
/// Defines the property.
///
public static readonly StyledProperty MaximumRowsOrColumnsProperty =
- AvaloniaProperty.Register(nameof(MinItemWidth));
+ AvaloniaProperty.Register(nameof(MaximumRowsOrColumns));
///
/// Defines the property.
diff --git a/src/Avalonia.Base/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs
index caff3d8150..9cc2edff86 100644
--- a/src/Avalonia.Base/LogicalTree/ILogical.cs
+++ b/src/Avalonia.Base/LogicalTree/ILogical.cs
@@ -1,12 +1,14 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls;
+using Avalonia.Metadata;
namespace Avalonia.LogicalTree
{
///
/// Represents a node in the logical tree.
///
+ [NotClientImplementable]
public interface ILogical
{
///
diff --git a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs
index 4a61544a6f..ea0f554e96 100644
--- a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs
+++ b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.LogicalTree
+using Avalonia.Metadata;
+
+namespace Avalonia.LogicalTree
{
///
/// Represents a root of a logical tree.
///
+ [NotClientImplementable]
public interface ILogicalRoot : ILogical
{
}
diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs
index 7c1266fa17..4b50019ddc 100644
--- a/src/Avalonia.Base/Media/ConicGradientBrush.cs
+++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Media
/// Defines the property.
///
public static readonly StyledProperty CenterProperty =
- AvaloniaProperty.Register(
+ AvaloniaProperty.Register(
nameof(Center),
RelativePoint.Center);
@@ -19,7 +19,7 @@ namespace Avalonia.Media
/// Defines the property.
///
public static readonly StyledProperty AngleProperty =
- AvaloniaProperty.Register(
+ AvaloniaProperty.Register(
nameof(Angle),
0);
diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs
index 830c066182..10700492d1 100644
--- a/src/Avalonia.Base/Media/IBrush.cs
+++ b/src/Avalonia.Base/Media/IBrush.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
@@ -6,6 +7,7 @@ namespace Avalonia.Media
/// Describes how an area is painted.
///
[TypeConverter(typeof(BrushConverter))]
+ [NotClientImplementable]
public interface IBrush
{
///
diff --git a/src/Avalonia.Base/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs
index 5368dd1851..6a397b86d4 100644
--- a/src/Avalonia.Base/Media/IConicGradientBrush.cs
+++ b/src/Avalonia.Base/Media/IConicGradientBrush.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Paints an area with a conic gradient.
///
+ [NotClientImplementable]
public interface IConicGradientBrush : IGradientBrush
{
///
diff --git a/src/Avalonia.Base/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs
index 7835c7a1e9..7208216603 100644
--- a/src/Avalonia.Base/Media/IDashStyle.cs
+++ b/src/Avalonia.Base/Media/IDashStyle.cs
@@ -1,10 +1,12 @@
using System.Collections.Generic;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
///
/// Represents the sequence of dashes and gaps that will be applied by a .
///
+ [NotClientImplementable]
public interface IDashStyle
{
///
diff --git a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs
index e71584258a..38048fb255 100644
--- a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs
+++ b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Experimental Interface for producing Acrylic-like materials.
///
+ [NotClientImplementable]
public interface IExperimentalAcrylicMaterial
{
///
diff --git a/src/Avalonia.Base/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs
index 18db0af660..9b78a4af78 100644
--- a/src/Avalonia.Base/Media/IGradientBrush.cs
+++ b/src/Avalonia.Base/Media/IGradientBrush.cs
@@ -1,10 +1,12 @@
using System.Collections.Generic;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
///
/// A brush that draws with a gradient.
///
+ [NotClientImplementable]
public interface IGradientBrush : IBrush
{
///
diff --git a/src/Avalonia.Base/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs
index 22eb9df60d..64b952f3bc 100644
--- a/src/Avalonia.Base/Media/IGradientStop.cs
+++ b/src/Avalonia.Base/Media/IGradientStop.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Describes the location and color of a transition point in a gradient.
///
+ [NotClientImplementable]
public interface IGradientStop
{
///
diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs
index aaa481bd28..732f1957d0 100644
--- a/src/Avalonia.Base/Media/IImageBrush.cs
+++ b/src/Avalonia.Base/Media/IImageBrush.cs
@@ -1,10 +1,12 @@
using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
///
/// Paints an area with an .
///
+ [NotClientImplementable]
public interface IImageBrush : ITileBrush
{
///
@@ -12,4 +14,4 @@ namespace Avalonia.Media
///
IBitmap Source { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs
index 3e2a5a0e22..4f4a55db36 100644
--- a/src/Avalonia.Base/Media/ILinearGradientBrush.cs
+++ b/src/Avalonia.Base/Media/ILinearGradientBrush.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// A brush that draws with a linear gradient.
///
+ [NotClientImplementable]
public interface ILinearGradientBrush : IGradientBrush
{
///
@@ -15,4 +18,4 @@
///
RelativePoint EndPoint { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs
index 415db61d68..fef124ba36 100644
--- a/src/Avalonia.Base/Media/IMutableBrush.cs
+++ b/src/Avalonia.Base/Media/IMutableBrush.cs
@@ -1,10 +1,12 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
///
/// Represents a mutable brush which can return an immutable clone of itself.
///
+ [NotClientImplementable]
public interface IMutableBrush : IBrush, IAffectsRender
{
///
diff --git a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs
index fcfe4631a6..f954a8c52a 100644
--- a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs
+++ b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Represents a mutable brush which can return an immutable clone of itself.
///
+ [NotClientImplementable]
public interface IMutableExperimentalAcrylicMaterial : IExperimentalAcrylicMaterial, IAffectsRender
{
///
diff --git a/src/Avalonia.Base/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs
index 2033c434c0..22526eed17 100644
--- a/src/Avalonia.Base/Media/IMutableTransform.cs
+++ b/src/Avalonia.Base/Media/IMutableTransform.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Media
{
diff --git a/src/Avalonia.Base/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs
index 1cad9948b4..eca1cb507a 100644
--- a/src/Avalonia.Base/Media/IPen.cs
+++ b/src/Avalonia.Base/Media/IPen.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Describes how a stroke is drawn.
///
+ [NotClientImplementable]
public interface IPen
{
///
diff --git a/src/Avalonia.Base/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs
index cadf53cc18..0f025e98bc 100644
--- a/src/Avalonia.Base/Media/IRadialGradientBrush.cs
+++ b/src/Avalonia.Base/Media/IRadialGradientBrush.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
{
///
/// Paints an area with a radial gradient.
///
+ [NotClientImplementable]
public interface IRadialGradientBrush : IGradientBrush
{
///
@@ -21,4 +24,4 @@
///
double Radius { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs
index d0ab00599b..29e11210f1 100644
--- a/src/Avalonia.Base/Media/ISolidColorBrush.cs
+++ b/src/Avalonia.Base/Media/ISolidColorBrush.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Media
{
///
/// Fills an area with a solid color.
///
+ [NotClientImplementable]
public interface ISolidColorBrush : IBrush
{
///
@@ -10,4 +13,4 @@ namespace Avalonia.Media
///
Color Color { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs
index 991857eec9..cb5a591003 100644
--- a/src/Avalonia.Base/Media/ITileBrush.cs
+++ b/src/Avalonia.Base/Media/ITileBrush.cs
@@ -1,10 +1,12 @@
using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
namespace Avalonia.Media
-{
+{
///
/// A brush which displays a repeating image.
///
+ [NotClientImplementable]
public interface ITileBrush : IBrush
{
///
diff --git a/src/Avalonia.Base/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs
index e74892b218..b8900bbd73 100644
--- a/src/Avalonia.Base/Media/IVisualBrush.cs
+++ b/src/Avalonia.Base/Media/IVisualBrush.cs
@@ -1,10 +1,12 @@
-using Avalonia.VisualTree;
+using Avalonia.Metadata;
+using Avalonia.VisualTree;
namespace Avalonia.Media
{
///
/// Paints an area with an .
///
+ [NotClientImplementable]
public interface IVisualBrush : ITileBrush
{
///
@@ -12,4 +14,4 @@ namespace Avalonia.Media
///
IVisual Visual { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs
index 134bebc002..bd04d5ce86 100644
--- a/src/Avalonia.Base/Media/Imaging/IBitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/IBitmap.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -8,6 +9,7 @@ namespace Avalonia.Media.Imaging
///
/// Represents a bitmap image.
///
+ [NotClientImplementable]
public interface IBitmap : IImage, IDisposable
{
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs
index 32012ab8e9..26966b37bc 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Media.TextFormatting
+using Avalonia.Metadata;
+
+namespace Avalonia.Media.TextFormatting
{
///
/// Produces objects that are used by the .
diff --git a/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs
new file mode 100644
index 0000000000..348c983c03
--- /dev/null
+++ b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Avalonia.Metadata
+{
+ ///
+ /// This interface is not intended to be implemented outside of the core Avalonia framework as
+ /// its API may change without warning.
+ ///
+ ///
+ /// This interface is stable for consumption by a client, but should not be implemented as members
+ /// may be added to its API.
+ ///
+ [AttributeUsage(AttributeTargets.Interface)]
+ public class NotClientImplementableAttribute : Attribute
+ {
+ }
+}
diff --git a/src/Avalonia.Base/Metadata/UnstableAttribute.cs b/src/Avalonia.Base/Metadata/UnstableAttribute.cs
new file mode 100644
index 0000000000..3b6fa5168a
--- /dev/null
+++ b/src/Avalonia.Base/Metadata/UnstableAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Avalonia.Metadata
+{
+ ///
+ /// This API is unstable and is not covered by API compatibility guarantees between minor and
+ /// patch releases.
+ ///
+ public class UnstableAttribute : Attribute
+ {
+ }
+}
diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs
index e3899784ad..b65d61803f 100644
--- a/src/Avalonia.Base/Platform/IAssetLoader.cs
+++ b/src/Avalonia.Base/Platform/IAssetLoader.cs
@@ -2,12 +2,14 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Loads assets compiled into the application binary.
///
+ [Unstable]
public interface IAssetLoader
{
///
diff --git a/src/Avalonia.Base/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs
index 1e68bc477d..8f11f68e7c 100644
--- a/src/Avalonia.Base/Platform/IBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IBitmapImpl.cs
@@ -1,11 +1,13 @@
using System;
using System.IO;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Defines the platform-specific interface for a .
///
+ [Unstable]
public interface IBitmapImpl : IDisposable
{
///
diff --git a/src/Avalonia.Base/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs
index 14235869f7..74e0ba2e5c 100644
--- a/src/Avalonia.Base/Platform/ICursorImpl.cs
+++ b/src/Avalonia.Base/Platform/ICursorImpl.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Input;
+using Avalonia.Metadata;
#nullable enable
@@ -8,6 +9,7 @@ namespace Avalonia.Platform
///
/// Represents a platform implementation of a .
///
+ [Unstable]
public interface ICursorImpl : IDisposable
{
}
diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
index 4e6612e908..d84a509234 100644
--- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
@@ -3,12 +3,14 @@ using Avalonia.Media;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Defines the interface through which drawing occurs.
///
+ [Unstable]
public interface IDrawingContextImpl : IDisposable
{
///
diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
index 0110287afd..932249bd52 100644
--- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
@@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Media;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
+ [Unstable]
public interface IFontManagerImpl
{
///
diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs
index ed6de1b5c7..c80f8923ef 100644
--- a/src/Avalonia.Base/Platform/IGeometryImpl.cs
+++ b/src/Avalonia.Base/Platform/IGeometryImpl.cs
@@ -1,10 +1,12 @@
using Avalonia.Media;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Defines the platform-specific interface for a .
///
+ [Unstable]
public interface IGeometryImpl
{
///
diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
index 08786d9689..7801bdd50f 100644
--- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
+++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs
@@ -1,9 +1,11 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Actual implementation of a glyph run that stores platform dependent resources.
///
+ [Unstable]
public interface IGlyphRunImpl : IDisposable { }
}
diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
index 6afd79d29c..415f34fb29 100644
--- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
+++ b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
+ [Unstable]
public interface IGlyphTypefaceImpl : IDisposable
{
///
diff --git a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs
index e399976bbe..b087724079 100644
--- a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs
+++ b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
+ [Unstable]
public interface IMacOSTopLevelPlatformHandle
{
IntPtr NSView { get; }
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index c46efd46c3..0eeefddf0b 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.IO;
using Avalonia.Media;
using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
///
/// Defines the main platform-specific interface for the rendering subsystem.
///
+ [Unstable]
public interface IPlatformRenderInterface
{
///
diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs
index e4b28e6575..78d1817312 100644
--- a/src/Avalonia.Base/Platform/IPlatformSettings.cs
+++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
+ [Unstable]
public interface IPlatformSettings
{
Size DoubleClickSize { get; }
diff --git a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
index 2137f965cc..bf18a7da5b 100644
--- a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading;
+using Avalonia.Metadata;
using Avalonia.Threading;
namespace Avalonia.Platform
@@ -7,6 +8,7 @@ namespace Avalonia.Platform
///
/// Provides platform-specific services relating to threading.
///
+ [Unstable]
public interface IPlatformThreadingInterface
{
void RunLoop(CancellationToken cancellationToken);
diff --git a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs
index 9add07afe3..d33c503650 100644
--- a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs
@@ -1,3 +1,4 @@
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
@@ -5,6 +6,7 @@ namespace Avalonia.Platform
/// Defines the platform-specific interface for a
/// .
///
+ [Unstable]
public interface IRenderTargetBitmapImpl : IBitmapImpl, IRenderTarget
{
}
diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
index 850757a1ee..8ab04f5995 100644
--- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs
+++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform
{
+ [Unstable]
public interface IRuntimePlatform
{
IDisposable StartSystemTimer(TimeSpan interval, Action tick);
@@ -9,6 +11,7 @@ namespace Avalonia.Platform
IUnmanagedBlob AllocBlob(int size);
}
+ [Unstable]
public interface IUnmanagedBlob : IDisposable
{
IntPtr Address { get; }
@@ -17,6 +20,7 @@ namespace Avalonia.Platform
}
+ [Unstable]
public struct RuntimePlatformInfo
{
public OperatingSystemType OperatingSystem { get; set; }
@@ -29,6 +33,7 @@ namespace Avalonia.Platform
public bool IsUnix { get; set; }
}
+ [Unstable]
public enum OperatingSystemType
{
Unknown,
diff --git a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs
index 4587979308..3d5ee36608 100644
--- a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Platform
{
///
/// Describes a geometry using drawing commands.
///
+ [Unstable]
public interface IStreamGeometryContextImpl : IGeometryContext
{
}
diff --git a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs
index 5b070fde02..bd4411f3a4 100644
--- a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs
+++ b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Platform
{
///
/// Defines the platform-specific interface for a .
///
+ [Unstable]
public interface IStreamGeometryImpl : IGeometryImpl
{
///
diff --git a/src/Avalonia.Base/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs
index 11be9e3f09..10e58b7d0b 100644
--- a/src/Avalonia.Base/Platform/ITextShaperImpl.cs
+++ b/src/Avalonia.Base/Platform/ITextShaperImpl.cs
@@ -1,4 +1,5 @@
using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Platform
@@ -6,6 +7,7 @@ namespace Avalonia.Platform
///
/// An abstraction that is used produce shaped text.
///
+ [Unstable]
public interface ITextShaperImpl
{
///
diff --git a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs
index 1ed025b571..2754414cd1 100644
--- a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs
+++ b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Platform
+using Avalonia.Metadata;
+
+namespace Avalonia.Platform
{
///
/// Represents a geometry with a transform applied.
@@ -7,6 +9,7 @@
/// An transforms a geometry without transforming its
/// stroke thickness.
///
+ [Unstable]
public interface ITransformedGeometryImpl : IGeometryImpl
{
///
diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
index c4e2e4915f..fa1e1862b7 100644
--- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Platform
+using Avalonia.Metadata;
+
+namespace Avalonia.Platform
{
///
/// Defines the platform-specific interface for a .
///
+ [Unstable]
public interface IWriteableBitmapImpl : IBitmapImpl
{
ILockedFramebuffer Lock();
diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
index 8124ce6bc4..9389ebc703 100644
--- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
+++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Platform.Interop
{
+ [Unstable]
public interface IDynamicLibraryLoader
{
IntPtr LoadLibrary(string dll);
diff --git a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs
index eab3dca58e..1c6bd69158 100644
--- a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs
+++ b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Rendering
{
+ [Unstable]
public interface IDeferredRendererLock
{
IDisposable? TryLock();
diff --git a/src/Avalonia.Base/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs
index dd7442e7f8..9838967261 100644
--- a/src/Avalonia.Base/Rendering/IRenderLoop.cs
+++ b/src/Avalonia.Base/Rendering/IRenderLoop.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Rendering
+using Avalonia.Metadata;
+
+namespace Avalonia.Rendering
{
///
/// The application render loop.
@@ -7,6 +9,7 @@
/// The render loop is responsible for advancing the animation timer and updating the scene
/// graph for visible windows.
///
+ [NotClientImplementable]
public interface IRenderLoop
{
///
diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs
index 54e58bf39c..1aa44158b2 100644
--- a/src/Avalonia.Base/Rendering/IRenderRoot.cs
+++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs
@@ -1,3 +1,4 @@
+using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.VisualTree;
@@ -6,6 +7,7 @@ namespace Avalonia.Rendering
///
/// Represents the root of a renderable tree.
///
+ [NotClientImplementable]
public interface IRenderRoot : IVisual
{
///
diff --git a/src/Avalonia.Base/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs
index d333e928a0..ee74c345be 100644
--- a/src/Avalonia.Base/Rendering/IRenderTimer.cs
+++ b/src/Avalonia.Base/Rendering/IRenderTimer.cs
@@ -1,11 +1,13 @@
using System;
using System.Threading.Tasks;
+using Avalonia.Metadata;
namespace Avalonia.Rendering
{
///
/// Defines the interface implemented by an application render timer.
///
+ [NotClientImplementable]
public interface IRenderTimer
{
///
diff --git a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs
index 00449c5344..b5ab8ed0bd 100644
--- a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs
+++ b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs
@@ -1,4 +1,5 @@
using Avalonia.Media;
+using Avalonia.Metadata;
namespace Avalonia.Rendering
{
@@ -6,6 +7,7 @@ namespace Avalonia.Rendering
/// Internal interface for initializing controls that are to be used as the visual in a
/// .
///
+ [Unstable]
public interface IVisualBrushInitialize
{
///
diff --git a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs
index 1cd6515635..f5312ad39b 100644
--- a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs
+++ b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs
@@ -1,4 +1,5 @@
using Avalonia.Media;
+using Avalonia.Metadata;
using Avalonia.Platform;
namespace Avalonia.Rendering
@@ -6,6 +7,7 @@ namespace Avalonia.Rendering
///
/// Defines a renderer used to render a visual brush to a bitmap.
///
+ [Unstable]
public interface IVisualBrushRenderer
{
///
diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs
index dd5eb703ea..da607720ff 100644
--- a/src/Avalonia.Base/StyledPropertyBase.cs
+++ b/src/Avalonia.Base/StyledPropertyBase.cs
@@ -1,9 +1,7 @@
using System;
-using System.Diagnostics;
using Avalonia.Data;
using Avalonia.Reactive;
using Avalonia.Styling;
-using Avalonia.Utilities;
namespace Avalonia
{
@@ -12,7 +10,7 @@ namespace Avalonia
///
public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor
{
- private bool _inherits;
+ private readonly bool _inherits;
///
/// Initializes a new instance of the class.
@@ -243,10 +241,10 @@ namespace Avalonia
}
else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType))
{
- return new PropertySetterLazyInstance(
+ return new PropertySetterTemplateInstance(
target,
this,
- () => (TValue)template.Build());
+ template);
}
else
{
diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs
index 479100ed8a..ac7b8b3ef1 100644
--- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs
+++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs
@@ -1,6 +1,5 @@
-#nullable enable
-
-using System;
+using System;
+using Avalonia.Metadata;
namespace Avalonia.Styling.Activators
{
@@ -16,6 +15,7 @@ namespace Avalonia.Styling.Activators
/// - The subscription can have a tag associated with it, allowing a subscriber to index
/// into a list of subscriptions without having to allocate additional objects.
///
+ [Unstable]
public interface IStyleActivator : IDisposable
{
///
diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs
index a1a6ef5c28..fbb18dc304 100644
--- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs
+++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs
@@ -1,10 +1,11 @@
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Styling.Activators
{
///
/// Receives notifications from an .
///
+ [Unstable]
public interface IStyleActivatorSink
{
///
diff --git a/src/Avalonia.Base/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs
index ab24e3138c..f9667a8895 100644
--- a/src/Avalonia.Base/Styling/IGlobalStyles.cs
+++ b/src/Avalonia.Base/Styling/IGlobalStyles.cs
@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
///
/// Defines the style host that provides styles global to the application.
///
+ [NotClientImplementable]
public interface IGlobalStyles : IStyleHost
{
///
diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs
index d588817be8..71ae5d84c0 100644
--- a/src/Avalonia.Base/Styling/ISetter.cs
+++ b/src/Avalonia.Base/Styling/ISetter.cs
@@ -1,12 +1,12 @@
using System;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
///
/// Represents a setter for a .
///
+ [NotClientImplementable]
public interface ISetter
{
///
diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs
index a299a87b64..e0d3137619 100644
--- a/src/Avalonia.Base/Styling/ISetterInstance.cs
+++ b/src/Avalonia.Base/Styling/ISetterInstance.cs
@@ -1,12 +1,12 @@
-#nullable enable
-
-using System;
+using System;
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
///
/// Represents a setter that has been instanced on a control.
///
+ [Unstable]
public interface ISetterInstance : IDisposable
{
///
diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs
index 738a69cb88..e9faf82c07 100644
--- a/src/Avalonia.Base/Styling/IStyle.cs
+++ b/src/Avalonia.Base/Styling/IStyle.cs
@@ -1,13 +1,13 @@
using System.Collections.Generic;
using Avalonia.Controls;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
///
/// Defines the interface for styles.
///
+ [NotClientImplementable]
public interface IStyle : IResourceNode
{
///
diff --git a/src/Avalonia.Base/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs
index 360b40d9a1..2ae488825c 100644
--- a/src/Avalonia.Base/Styling/IStyleHost.cs
+++ b/src/Avalonia.Base/Styling/IStyleHost.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
+using Avalonia.Metadata;
#nullable enable
@@ -8,6 +9,7 @@ namespace Avalonia.Styling
///
/// Defines an element that has a collection.
///
+ [NotClientImplementable]
public interface IStyleHost
{
///
diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs
index 8ddb989bc0..262f336e05 100644
--- a/src/Avalonia.Base/Styling/IStyleInstance.cs
+++ b/src/Avalonia.Base/Styling/IStyleInstance.cs
@@ -1,12 +1,12 @@
using System;
-
-#nullable enable
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
///
/// Represents a style that has been instanced on a control.
///
+ [Unstable]
public interface IStyleInstance : IDisposable
{
///
@@ -14,6 +14,11 @@ namespace Avalonia.Styling
///
IStyle Source { get; }
+ ///
+ /// Gets a value indicating whether this style has an activator.
+ ///
+ bool HasActivator { get; }
+
///
/// Gets a value indicating whether this style is active.
///
diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs
index a3df779057..5bc972e7ab 100644
--- a/src/Avalonia.Base/Styling/IStyleable.cs
+++ b/src/Avalonia.Base/Styling/IStyleable.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Avalonia.Collections;
+using Avalonia.Metadata;
#nullable enable
@@ -9,6 +10,7 @@ namespace Avalonia.Styling
///
/// Interface for styleable elements.
///
+ [NotClientImplementable]
public interface IStyleable : IAvaloniaObject, INamed
{
///
diff --git a/src/Avalonia.Base/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs
index 5485babb62..68989c8a5e 100644
--- a/src/Avalonia.Base/Styling/ITemplatedControl.cs
+++ b/src/Avalonia.Base/Styling/ITemplatedControl.cs
@@ -1,6 +1,8 @@
+using Avalonia.Metadata;
namespace Avalonia.Styling
{
+ [NotClientImplementable]
public interface ITemplatedControl : IAvaloniaObject
{
}
diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs
index 48f462d006..c4e8f47e67 100644
--- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs
+++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Styling
{
if (hasActivator)
{
- if (_styledProperty is object)
+ if (_styledProperty is not null)
{
_subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger);
}
@@ -55,13 +55,15 @@ namespace Avalonia.Styling
}
else
{
- if (_styledProperty is object)
+ var target = (AvaloniaObject) _target;
+
+ if (_styledProperty is not null)
{
- _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style);
+ _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style);
}
else
{
- _target.SetValue(_directProperty!, _value);
+ target.SetValue(_directProperty!, _value);
}
}
}
diff --git a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
similarity index 79%
rename from src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs
rename to src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
index 92653d0064..0f6efef1be 100644
--- a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs
+++ b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
@@ -11,42 +11,42 @@ namespace Avalonia.Styling
/// evaluated.
///
/// The target property type.
- internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>,
+ internal class PropertySetterTemplateInstance : SingleSubscriberObservableBase>,
ISetterInstance
{
private readonly IStyleable _target;
private readonly StyledPropertyBase? _styledProperty;
private readonly DirectPropertyBase? _directProperty;
- private readonly Func _valueFactory;
+ private readonly ITemplate _template;
private BindingValue _value;
private IDisposable? _subscription;
private bool _isActive;
- public PropertySetterLazyInstance(
+ public PropertySetterTemplateInstance(
IStyleable target,
StyledPropertyBase property,
- Func valueFactory)
+ ITemplate template)
{
_target = target;
_styledProperty = property;
- _valueFactory = valueFactory;
+ _template = template;
}
- public PropertySetterLazyInstance(
+ public PropertySetterTemplateInstance(
IStyleable target,
DirectPropertyBase property,
- Func valueFactory)
+ ITemplate template)
{
_target = target;
_directProperty = property;
- _valueFactory = valueFactory;
+ _template = template;
}
public void Start(bool hasActivator)
{
_isActive = !hasActivator;
- if (_styledProperty is object)
+ if (_styledProperty is not null)
{
var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style;
_subscription = _target.Bind(_styledProperty, this, priority);
@@ -77,7 +77,7 @@ namespace Avalonia.Styling
public override void Dispose()
{
- if (_subscription is object)
+ if (_subscription is not null)
{
var sub = _subscription;
_subscription = null;
@@ -85,7 +85,7 @@ namespace Avalonia.Styling
}
else if (_isActive)
{
- if (_styledProperty is object)
+ if (_styledProperty is not null)
{
_target.ClearValue(_styledProperty);
}
@@ -101,22 +101,21 @@ namespace Avalonia.Styling
protected override void Subscribed() => PublishNext();
protected override void Unsubscribed() { }
- private T GetValue()
+ private void EnsureTemplate()
{
if (_value.HasValue)
{
- return _value.Value;
+ return;
}
- _value = _valueFactory();
- return _value.Value;
+ _value = (T) _template.Build();
}
private void PublishNext()
{
if (_isActive)
{
- GetValue();
+ EnsureTemplate();
PublishNext(_value);
}
else
diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs
index b4b3399022..d989bb0706 100644
--- a/src/Avalonia.Base/Styling/Setter.cs
+++ b/src/Avalonia.Base/Styling/Setter.cs
@@ -1,9 +1,7 @@
using System;
using Avalonia.Animation;
using Avalonia.Data;
-using Avalonia.Data.Core;
using Avalonia.Metadata;
-using Avalonia.Utilities;
#nullable enable
@@ -70,12 +68,5 @@ namespace Avalonia.Styling
return Property.CreateSetterInstance(target, Value);
}
-
- private struct SetterVisitorData
- {
- public IStyleable target;
- public object? value;
- public ISetterInstance? result;
- }
}
}
diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs
index 830cf49a0d..db96da6821 100644
--- a/src/Avalonia.Base/Styling/StyleInstance.cs
+++ b/src/Avalonia.Base/Styling/StyleInstance.cs
@@ -11,10 +11,10 @@ namespace Avalonia.Styling
///
/// A which has been instanced on a control.
///
- internal class StyleInstance : IStyleInstance, IStyleActivatorSink
+ internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink
{
- private readonly List? _setters;
- private readonly List? _animations;
+ private readonly ISetterInstance[]? _setters;
+ private readonly IDisposable[]? _animations;
private readonly IStyleActivator? _activator;
private readonly Subject? _animationTrigger;
@@ -30,41 +30,42 @@ namespace Avalonia.Styling
_activator = activator;
IsActive = _activator is null;
- if (setters is object)
+ if (setters is not null)
{
var setterCount = setters.Count;
- _setters = new List(setterCount);
+ _setters = new ISetterInstance[setterCount];
for (var i = 0; i < setterCount; ++i)
{
- _setters.Add(setters[i].Instance(Target));
+ _setters[i] = setters[i].Instance(Target);
}
}
- if (animations is object && target is Animatable animatable)
+ if (animations is not null && target is Animatable animatable)
{
var animationsCount = animations.Count;
- _animations = new List(animationsCount);
+ _animations = new IDisposable[animationsCount];
_animationTrigger = new Subject();
for (var i = 0; i < animationsCount; ++i)
{
- _animations.Add(animations[i].Apply(animatable, null, _animationTrigger));
+ _animations[i] = animations[i].Apply(animatable, null, _animationTrigger);
}
}
}
+ public bool HasActivator => _activator is not null;
public bool IsActive { get; private set; }
public IStyle Source { get; }
public IStyleable Target { get; }
public void Start()
{
- var hasActivator = _activator is object;
+ var hasActivator = HasActivator;
- if (_setters is object)
+ if (_setters is not null)
{
foreach (var setter in _setters)
{
@@ -76,7 +77,7 @@ namespace Avalonia.Styling
{
_activator!.Subscribe(this, 0);
}
- else if (_animationTrigger != null)
+ else if (_animationTrigger is not null)
{
_animationTrigger.OnNext(true);
}
@@ -84,7 +85,7 @@ namespace Avalonia.Styling
public void Dispose()
{
- if (_setters is object)
+ if (_setters is not null)
{
foreach (var setter in _setters)
{
@@ -92,11 +93,11 @@ namespace Avalonia.Styling
}
}
- if (_animations is object)
+ if (_animations is not null)
{
- foreach (var subscripion in _animations)
+ foreach (var subscription in _animations)
{
- subscripion.Dispose();
+ subscription.Dispose();
}
}
@@ -111,7 +112,7 @@ namespace Avalonia.Styling
_animationTrigger?.OnNext(value);
- if (_setters is object)
+ if (_setters is not null)
{
if (IsActive)
{
diff --git a/src/Avalonia.Base/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs
index b1251618c4..3b053fab38 100644
--- a/src/Avalonia.Base/VisualTree/IVisual.cs
+++ b/src/Avalonia.Base/VisualTree/IVisual.cs
@@ -1,10 +1,9 @@
using System;
using Avalonia.Collections;
using Avalonia.Media;
+using Avalonia.Metadata;
using Avalonia.Rendering;
-#nullable enable
-
namespace Avalonia.VisualTree
{
///
@@ -18,6 +17,7 @@ namespace Avalonia.VisualTree
/// implemented by . It should not be necessary to implement it
/// anywhere else.
///
+ [NotClientImplementable]
public interface IVisual
{
///
diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
index e07c933039..38d559a031 100644
--- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
+++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
@@ -218,6 +218,8 @@ namespace Avalonia.Controls.Primitives
{
// No explicit height values were set so we can autosize
autoSizeHeight = true;
+ // We need to invalidate desired height in order to grow or shrink as needed
+ InvalidateDesiredHeight();
measureHeight = double.PositiveInfinity;
}
else
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs
index 9860d0cb38..b38a539d4a 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Controls.ApplicationLifetimes
{
+ [NotClientImplementable]
public interface IApplicationLifetime
{
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
index 2bd5c1238d..4b88f6b537 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using Avalonia.Metadata;
namespace Avalonia.Controls.ApplicationLifetimes
{
///
/// Controls application lifetime in classic desktop style
///
+ [NotClientImplementable]
public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime
{
///
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs
index 3f61aeb536..d7eda790df 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs
@@ -1,7 +1,9 @@
using System;
+using Avalonia.Metadata;
namespace Avalonia.Controls.ApplicationLifetimes
{
+ [NotClientImplementable]
public interface IControlledApplicationLifetime : IApplicationLifetime
{
///
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
index e25815602e..480c65e5ad 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Controls.ApplicationLifetimes
{
+ [NotClientImplementable]
public interface ISingleViewApplicationLifetime : IApplicationLifetime
{
Control? MainView { get; set; }
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index 0ef1ba4c8c..db4ca6bc43 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -309,6 +309,8 @@ namespace Avalonia.Controls
IsPressed = false;
e.Handled = true;
}
+
+ base.OnKeyUp(e);
}
///
@@ -393,6 +395,8 @@ namespace Avalonia.Controls
///
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
+ base.OnPointerCaptureLost(e);
+
IsPressed = false;
}
@@ -407,6 +411,8 @@ namespace Avalonia.Controls
///
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
+ base.OnApplyTemplate(e);
+
UnregisterFlyoutEvents(Flyout);
RegisterFlyoutEvents(Flyout);
UpdatePseudoClasses();
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index c44994f92f..32fdaceacb 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
+using Avalonia.Collections.Pooled;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
using Avalonia.Input;
@@ -172,13 +173,13 @@ namespace Avalonia.Controls.Primitives
if (MonthView != null)
{
var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
- var children = new List(childCount);
+ using var children = new PooledList(childCount);
for (int i = 0; i < Calendar.RowsPerMonth; i++)
{
if (_dayTitleTemplate != null)
{
- var cell = _dayTitleTemplate.Build();
+ var cell = (Control) _dayTitleTemplate.Build();
cell.DataContext = string.Empty;
cell.SetValue(Grid.RowProperty, 0);
cell.SetValue(Grid.ColumnProperty, i);
@@ -186,11 +187,16 @@ namespace Avalonia.Controls.Primitives
}
}
+ EventHandler cellMouseLeftButtonDown = Cell_MouseLeftButtonDown;
+ EventHandler cellMouseLeftButtonUp = Cell_MouseLeftButtonUp;
+ EventHandler cellMouseEnter = Cell_MouseEnter;
+ EventHandler cellClick = Cell_Click;
+
for (int i = 1; i < Calendar.RowsPerMonth; i++)
{
for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
{
- CalendarDayButton cell = new CalendarDayButton();
+ var cell = new CalendarDayButton();
if (Owner != null)
{
@@ -198,10 +204,10 @@ namespace Avalonia.Controls.Primitives
}
cell.SetValue(Grid.RowProperty, i);
cell.SetValue(Grid.ColumnProperty, j);
- cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
- cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
- cell.PointerEnter += Cell_MouseEnter;
- cell.Click += Cell_Click;
+ cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
+ cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
+ cell.PointerEnter += cellMouseEnter;
+ cell.Click += cellClick;
children.Add(cell);
}
}
@@ -214,12 +220,15 @@ namespace Avalonia.Controls.Primitives
var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear;
var children = new List(childCount);
- CalendarButton month;
+ EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
+ EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;
+ EventHandler monthMouseEnter = Month_MouseEnter;
+
for (int i = 0; i < Calendar.RowsPerYear; i++)
{
for (int j = 0; j < Calendar.ColumnsPerYear; j++)
{
- month = new CalendarButton();
+ var month = new CalendarButton();
if (Owner != null)
{
@@ -227,9 +236,9 @@ namespace Avalonia.Controls.Primitives
}
month.SetValue(Grid.RowProperty, i);
month.SetValue(Grid.ColumnProperty, j);
- month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
- month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
- month.PointerEnter += Month_MouseEnter;
+ month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown;
+ month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp;
+ month.PointerEnter += monthMouseEnter;
children.Add(month);
}
}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
new file mode 100644
index 0000000000..6c2356b411
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
@@ -0,0 +1,303 @@
+using System;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Layout;
+
+namespace Avalonia.Controls
+{
+ ///
+ public partial class CalendarDatePicker
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDate),
+ o => o.DisplayDate,
+ (o, v) => o.DisplayDate = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateStartProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDateStart),
+ o => o.DisplayDateStart,
+ (o, v) => o.DisplayDateStart = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateEndProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDateEnd),
+ o => o.DisplayDateEnd,
+ (o, v) => o.DisplayDateEnd = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty FirstDayOfWeekProperty =
+ AvaloniaProperty.Register(nameof(FirstDayOfWeek));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty IsDropDownOpenProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(IsDropDownOpen),
+ o => o.IsDropDownOpen,
+ (o, v) => o.IsDropDownOpen = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsTodayHighlightedProperty =
+ AvaloniaProperty.Register(nameof(IsTodayHighlighted));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty SelectedDateProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SelectedDate),
+ o => o.SelectedDate,
+ (o, v) => o.SelectedDate = v,
+ enableDataValidation: true,
+ defaultBindingMode:BindingMode.TwoWay);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty SelectedDateFormatProperty =
+ AvaloniaProperty.Register(
+ nameof(SelectedDateFormat),
+ defaultValue: CalendarDatePickerFormat.Short,
+ validate: IsValidSelectedDateFormat);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty CustomDateFormatStringProperty =
+ AvaloniaProperty.Register(
+ nameof(CustomDateFormatString),
+ defaultValue: "d",
+ validate: IsValidDateFormatString);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Text),
+ o => o.Text,
+ (o, v) => o.Text = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WatermarkProperty =
+ TextBox.WatermarkProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty UseFloatingWatermarkProperty =
+ TextBox.UseFloatingWatermarkProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty HorizontalContentAlignmentProperty =
+ ContentControl.HorizontalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty VerticalContentAlignmentProperty =
+ ContentControl.VerticalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Gets a collection of dates that are marked as not selectable.
+ ///
+ ///
+ /// A collection of dates that cannot be selected. The default value is
+ /// an empty collection.
+ ///
+ public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
+
+ ///
+ /// Gets or sets the date to display.
+ ///
+ ///
+ /// The date to display. The default is .
+ ///
+ ///
+ /// The specified date is not in the range defined by
+ ///
+ /// and
+ /// .
+ ///
+ public DateTime DisplayDate
+ {
+ get => _displayDate;
+ set => SetAndRaise(DisplayDateProperty, ref _displayDate, value);
+ }
+
+ ///
+ /// Gets or sets the first date to be displayed.
+ ///
+ /// The first date to display.
+ public DateTime? DisplayDateStart
+ {
+ get => _displayDateStart;
+ set => SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value);
+ }
+
+ ///
+ /// Gets or sets the last date to be displayed.
+ ///
+ /// The last date to display.
+ public DateTime? DisplayDateEnd
+ {
+ get => _displayDateEnd;
+ set => SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value);
+ }
+
+ ///
+ /// Gets or sets the day that is considered the beginning of the week.
+ ///
+ ///
+ /// A representing the beginning of
+ /// the week. The default is .
+ ///
+ public DayOfWeek FirstDayOfWeek
+ {
+ get => GetValue(FirstDayOfWeekProperty);
+ set => SetValue(FirstDayOfWeekProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the drop-down
+ /// is open or closed.
+ ///
+ ///
+ /// True if the is
+ /// open; otherwise, false. The default is false.
+ ///
+ public bool IsDropDownOpen
+ {
+ get => _isDropDownOpen;
+ set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the current date will be
+ /// highlighted.
+ ///
+ ///
+ /// True if the current date is highlighted; otherwise, false. The
+ /// default is true.
+ ///
+ public bool IsTodayHighlighted
+ {
+ get => GetValue(IsTodayHighlightedProperty);
+ set => SetValue(IsTodayHighlightedProperty, value);
+ }
+
+ ///
+ /// Gets or sets the currently selected date.
+ ///
+ ///
+ /// The date currently selected. The default is null.
+ ///
+ ///
+ /// The specified date is not in the range defined by
+ ///
+ /// and
+ /// ,
+ /// or the specified date is in the
+ ///
+ /// collection.
+ ///
+ public DateTime? SelectedDate
+ {
+ get => _selectedDate;
+ set => SetAndRaise(SelectedDateProperty, ref _selectedDate, value);
+ }
+
+ ///
+ /// Gets or sets the format that is used to display the selected date.
+ ///
+ ///
+ /// The format that is used to display the selected date. The default is
+ /// .
+ ///
+ ///
+ /// An specified format is not valid.
+ ///
+ public CalendarDatePickerFormat SelectedDateFormat
+ {
+ get => GetValue(SelectedDateFormatProperty);
+ set => SetValue(SelectedDateFormatProperty, value);
+ }
+
+ public string CustomDateFormatString
+ {
+ get => GetValue(CustomDateFormatStringProperty);
+ set => SetValue(CustomDateFormatStringProperty, value);
+ }
+
+ ///
+ /// Gets or sets the text that is displayed by the .
+ ///
+ ///
+ /// The text displayed by the .
+ ///
+ ///
+ /// The text entered cannot be parsed to a valid date, and the exception
+ /// is not suppressed.
+ ///
+ ///
+ /// The text entered parses to a date that is not selectable.
+ ///
+ public string? Text
+ {
+ get => _text;
+ set => SetAndRaise(TextProperty, ref _text, value);
+ }
+
+ ///
+ public string? Watermark
+ {
+ get => GetValue(WatermarkProperty);
+ set => SetValue(WatermarkProperty, value);
+ }
+
+ ///
+ public bool UseFloatingWatermark
+ {
+ get => GetValue(UseFloatingWatermarkProperty);
+ set => SetValue(UseFloatingWatermarkProperty, value);
+ }
+
+ ///
+ /// Gets or sets the horizontal alignment of the content within the control.
+ ///
+ public HorizontalAlignment HorizontalContentAlignment
+ {
+ get => GetValue(HorizontalContentAlignmentProperty);
+ set => SetValue(HorizontalContentAlignmentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the vertical alignment of the content within the control.
+ ///
+ public VerticalAlignment VerticalContentAlignment
+ {
+ get => GetValue(VerticalContentAlignmentProperty);
+ set => SetValue(VerticalContentAlignmentProperty, value);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
similarity index 55%
rename from src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
rename to src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
index 0409eb30aa..3d592e9ab5 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
@@ -7,122 +7,28 @@ using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
+using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.Layout;
namespace Avalonia.Controls
{
///
- /// Provides data for the
- ///
- /// event.
+ /// A date selection control that allows the user to select dates from a drop down calendar.
///
- public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
- {
- private bool _throwException;
-
- ///
- /// Initializes a new instance of the
- ///
- /// class.
- ///
- ///
- /// The initial exception from the
- ///
- /// event.
- ///
- ///
- /// The text that caused the
- ///
- /// event.
- ///
- public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text)
- {
- this.Text = text;
- this.Exception = exception;
- }
-
- ///
- /// Gets the initial exception associated with the
- ///
- /// event.
- ///
- ///
- /// The exception associated with the validation failure.
- ///
- public Exception Exception { get; private set; }
-
- ///
- /// Gets the text that caused the
- ///
- /// event.
- ///
- ///
- /// The text that caused the validation failure.
- ///
- public string Text { get; private set; }
-
- ///
- /// Gets or sets a value indicating whether
- ///
- /// should be thrown.
- ///
- ///
- /// True if the exception should be thrown; otherwise, false.
- ///
- ///
- /// If set to true and
- ///
- /// is null.
- ///
- public bool ThrowException
- {
- get { return this._throwException; }
- set
- {
- if (value && this.Exception == null)
- {
- throw new ArgumentException("Cannot Throw Null Exception");
- }
- this._throwException = value;
- }
- }
- }
-
- ///
- /// Specifies date formats for a
- /// .
- ///
- public enum CalendarDatePickerFormat
- {
- ///
- /// Specifies that the date should be displayed using unabbreviated days
- /// of the week and month names.
- ///
- Long = 0,
-
- ///
- /// Specifies that the date should be displayed using abbreviated days
- /// of the week and month names.
- ///
- Short = 1,
-
- ///
- /// Specifies that the date should be displayed using a custom format string.
- ///
- Custom = 2
- }
-
[TemplatePart(ElementButton, typeof(Button))]
[TemplatePart(ElementCalendar, typeof(Calendar))]
[TemplatePart(ElementPopup, typeof(Popup))]
[TemplatePart(ElementTextBox, typeof(TextBox))]
- public class CalendarDatePicker : TemplatedControl
+ [PseudoClasses(pcFlyoutOpen, pcPressed)]
+ public partial class CalendarDatePicker : TemplatedControl
{
+ protected const string pcPressed = ":pressed";
+ protected const string pcFlyoutOpen = ":flyout-open";
+
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
private const string ElementPopup = "PART_Popup";
@@ -131,8 +37,6 @@ namespace Avalonia.Controls
private Calendar? _calendar;
private string _defaultText;
private Button? _dropDownButton;
- //private Canvas _outsideCanvas;
- //private Canvas _outsidePopupCanvas;
private Popup? _popUp;
private TextBox? _textBox;
private IDisposable? _textBoxTextChangedSubscription;
@@ -150,258 +54,8 @@ namespace Avalonia.Controls
private bool _suspendTextChangeHandler = false;
private bool _isPopupClosing = false;
private bool _ignoreButtonClick = false;
-
- ///
- /// Gets a collection of dates that are marked as not selectable.
- ///
- ///
- /// A collection of dates that cannot be selected. The default value is
- /// an empty collection.
- ///
- public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
-
- public static readonly DirectProperty DisplayDateProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDate),
- o => o.DisplayDate,
- (o, v) => o.DisplayDate = v);
- public static readonly DirectProperty DisplayDateStartProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDateStart),
- o => o.DisplayDateStart,
- (o, v) => o.DisplayDateStart = v);
- public static readonly DirectProperty DisplayDateEndProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDateEnd),
- o => o.DisplayDateEnd,
- (o, v) => o.DisplayDateEnd = v);
- public static readonly StyledProperty FirstDayOfWeekProperty =
- AvaloniaProperty.Register(nameof(FirstDayOfWeek));
-
- public static readonly DirectProperty IsDropDownOpenProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(IsDropDownOpen),
- o => o.IsDropDownOpen,
- (o, v) => o.IsDropDownOpen = v);
-
- public static readonly StyledProperty IsTodayHighlightedProperty =
- AvaloniaProperty.Register(nameof(IsTodayHighlighted));
- public static readonly DirectProperty SelectedDateProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(SelectedDate),
- o => o.SelectedDate,
- (o, v) => o.SelectedDate = v,
- enableDataValidation: true,
- defaultBindingMode:BindingMode.TwoWay);
-
- public static readonly StyledProperty SelectedDateFormatProperty =
- AvaloniaProperty.Register(
- nameof(SelectedDateFormat),
- defaultValue: CalendarDatePickerFormat.Short,
- validate: IsValidSelectedDateFormat);
-
- public static readonly StyledProperty CustomDateFormatStringProperty =
- AvaloniaProperty.Register(
- nameof(CustomDateFormatString),
- defaultValue: "d",
- validate: IsValidDateFormatString);
-
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(Text),
- o => o.Text,
- (o, v) => o.Text = v);
- public static readonly StyledProperty WatermarkProperty =
- TextBox.WatermarkProperty.AddOwner();
- public static readonly StyledProperty UseFloatingWatermarkProperty =
- TextBox.UseFloatingWatermarkProperty.AddOwner();
-
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty HorizontalContentAlignmentProperty =
- ContentControl.HorizontalContentAlignmentProperty.AddOwner();
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty VerticalContentAlignmentProperty =
- ContentControl.VerticalContentAlignmentProperty.AddOwner();
-
- ///
- /// Gets or sets the date to display.
- ///
- ///
- /// The date to display. The default
- /// .
- ///
- ///
- /// The specified date is not in the range defined by
- ///
- /// and
- /// .
- ///
- public DateTime DisplayDate
- {
- get { return _displayDate; }
- set { SetAndRaise(DisplayDateProperty, ref _displayDate, value); }
- }
-
- ///
- /// Gets or sets the first date to be displayed.
- ///
- /// The first date to display.
- public DateTime? DisplayDateStart
- {
- get { return _displayDateStart; }
- set { SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value); }
- }
-
- ///
- /// Gets or sets the last date to be displayed.
- ///
- /// The last date to display.
- public DateTime? DisplayDateEnd
- {
- get { return _displayDateEnd; }
- set { SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value); }
- }
-
- ///
- /// Gets or sets the day that is considered the beginning of the week.
- ///
- ///
- /// A representing the beginning of
- /// the week. The default is .
- ///
- public DayOfWeek FirstDayOfWeek
- {
- get { return GetValue(FirstDayOfWeekProperty); }
- set { SetValue(FirstDayOfWeekProperty, value); }
- }
-
- ///
- /// Gets or sets a value indicating whether the drop-down
- /// is open or closed.
- ///
- ///
- /// True if the is
- /// open; otherwise, false. The default is false.
- ///
- public bool IsDropDownOpen
- {
- get { return _isDropDownOpen; }
- set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); }
- }
-
- ///
- /// Gets or sets a value indicating whether the current date will be
- /// highlighted.
- ///
- ///
- /// True if the current date is highlighted; otherwise, false. The
- /// default is true.
- ///
- public bool IsTodayHighlighted
- {
- get { return GetValue(IsTodayHighlightedProperty); }
- set { SetValue(IsTodayHighlightedProperty, value); }
- }
-
- ///
- /// Gets or sets the currently selected date.
- ///
- ///
- /// The date currently selected. The default is null.
- ///
- ///
- /// The specified date is not in the range defined by
- ///
- /// and
- /// ,
- /// or the specified date is in the
- ///
- /// collection.
- ///
- public DateTime? SelectedDate
- {
- get { return _selectedDate; }
- set { SetAndRaise(SelectedDateProperty, ref _selectedDate, value); }
- }
-
- ///
- /// Gets or sets the format that is used to display the selected date.
- ///
- ///
- /// The format that is used to display the selected date. The default is
- /// .
- ///
- ///
- /// An specified format is not valid.
- ///
- public CalendarDatePickerFormat SelectedDateFormat
- {
- get { return GetValue(SelectedDateFormatProperty); }
- set { SetValue(SelectedDateFormatProperty, value); }
- }
-
- public string CustomDateFormatString
- {
- get { return GetValue(CustomDateFormatStringProperty); }
- set { SetValue(CustomDateFormatStringProperty, value); }
- }
-
- ///
- /// Gets or sets the text that is displayed by the
- /// .
- ///
- ///
- /// The text displayed by the
- /// .
- ///
- ///
- /// The text entered cannot be parsed to a valid date, and the exception
- /// is not suppressed.
- ///
- ///
- /// The text entered parses to a date that is not selectable.
- ///
- public string? Text
- {
- get { return _text; }
- set { SetAndRaise(TextProperty, ref _text, value); }
- }
-
- public string? Watermark
- {
- get { return GetValue(WatermarkProperty); }
- set { SetValue(WatermarkProperty, value); }
- }
- public bool UseFloatingWatermark
- {
- get { return GetValue(UseFloatingWatermarkProperty); }
- set { SetValue(UseFloatingWatermarkProperty, value); }
- }
-
-
- ///
- /// Gets or sets the horizontal alignment of the content within the control.
- ///
- public HorizontalAlignment HorizontalContentAlignment
- {
- get => GetValue(HorizontalContentAlignmentProperty);
- set => SetValue(HorizontalContentAlignmentProperty, value);
- }
-
- ///
- /// Gets or sets the vertical alignment of the content within the control.
- ///
- public VerticalAlignment VerticalContentAlignment
- {
- get => GetValue(VerticalContentAlignmentProperty);
- set => SetValue(VerticalContentAlignmentProperty, value);
- }
+ private bool _isFlyoutOpen = false;
+ private bool _isPressed = false;
///
/// Occurs when the drop-down
@@ -431,16 +85,10 @@ namespace Avalonia.Controls
static CalendarDatePicker()
{
FocusableProperty.OverrideDefaultValue(true);
-
- IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
- SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
- SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e));
- CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e));
- TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e));
}
+
///
- /// Initializes a new instance of the
- /// class.
+ /// Initializes a new instance of the class.
///
public CalendarDatePicker()
{
@@ -449,6 +97,16 @@ namespace Avalonia.Controls
DisplayDate = DateTime.Today;
}
+ ///
+ /// Updates the visual state of the control by applying latest PseudoClasses.
+ ///
+ protected void UpdatePseudoClasses()
+ {
+ PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen);
+ PseudoClasses.Set(pcPressed, _isPressed);
+ }
+
+ ///
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_calendar != null)
@@ -505,8 +163,9 @@ namespace Avalonia.Controls
if(_dropDownButton != null)
{
_dropDownButton.Click += DropDownButton_Click;
- _buttonPointerPressedSubscription =
- _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
+ _buttonPointerPressedSubscription = new CompositeDisposable(
+ _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true),
+ _dropDownButton.AddDisposableHandler(PointerReleasedEvent, DropDownButton_PointerReleased, handledEventsToo: true));
}
if (_textBox != null)
@@ -538,16 +197,200 @@ namespace Avalonia.Controls
SetSelectedDate();
}
}
+
+ UpdatePseudoClasses();
+ }
+
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ // CustomDateFormatString
+ if (change.Property == CustomDateFormatStringProperty)
+ {
+ if (SelectedDateFormat == CalendarDatePickerFormat.Custom)
+ {
+ OnDateFormatChanged();
+ }
+ }
+ // IsDropDownOpen
+ else if (change.Property == IsDropDownOpenProperty)
+ {
+ var (oldValue, newValue) = change.GetOldAndNewValue();
+
+ if (_popUp != null && _popUp.Child != null)
+ {
+ if (newValue != oldValue)
+ {
+ if (_calendar!.DisplayMode != CalendarMode.Month)
+ {
+ _calendar.DisplayMode = CalendarMode.Month;
+ }
+
+ if (newValue)
+ {
+ OpenDropDown();
+ }
+ else
+ {
+ _popUp.IsOpen = false;
+ _isFlyoutOpen = _popUp.IsOpen;
+ _isPressed = false;
+
+ UpdatePseudoClasses();
+ OnCalendarClosed(new RoutedEventArgs());
+ }
+ }
+ }
+ }
+ // SelectedDate
+ else if (change.Property == SelectedDateProperty)
+ {
+ var (removedDate, addedDate) = change.GetOldAndNewValue();
+
+ if (SelectedDate != null)
+ {
+ DateTime day = SelectedDate.Value;
+
+ // When the SelectedDateProperty change is done from
+ // OnTextPropertyChanged method, two-way binding breaks if
+ // BeginInvoke is not used:
+ Threading.Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ _settingSelectedDate = true;
+ Text = DateTimeToString(day);
+ _settingSelectedDate = false;
+ OnDateSelected(addedDate, removedDate);
+ });
+
+ // When DatePickerDisplayDateFlag is TRUE, the SelectedDate
+ // change is coming from the Calendar UI itself, so, we
+ // shouldn't change the DisplayDate since it will automatically
+ // be changed by the Calendar
+ if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag))
+ {
+ DisplayDate = day;
+ }
+
+ if(_calendar != null)
+ {
+ _calendar.CalendarDatePickerDisplayDateFlag = false;
+ }
+ }
+ else
+ {
+ _settingSelectedDate = true;
+ SetWaterMarkText();
+ _settingSelectedDate = false;
+ OnDateSelected(addedDate, removedDate);
+ }
+ }
+ // SelectedDateFormat
+ else if (change.Property == SelectedDateFormatProperty)
+ {
+ OnDateFormatChanged();
+ }
+ // Text
+ else if (change.Property == TextProperty)
+ {
+ var (oldValue, newValue) = change.GetOldAndNewValue();
+
+ if (!_suspendTextChangeHandler)
+ {
+ if (newValue != null)
+ {
+ if (_textBox != null)
+ {
+ _textBox.Text = newValue;
+ }
+ else
+ {
+ _defaultText = newValue;
+ }
+
+ if (!_settingSelectedDate)
+ {
+ SetSelectedDate();
+ }
+ }
+ else
+ {
+ if (!_settingSelectedDate)
+ {
+ _settingSelectedDate = true;
+ SelectedDate = null;
+ _settingSelectedDate = false;
+ }
+ }
+ }
+ else
+ {
+ SetWaterMarkText();
+ }
+ }
+
+ base.OnPropertyChanged(change);
}
+ ///
protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error)
{
if (property == SelectedDateProperty)
{
DataValidationErrors.SetError(this, error);
}
+
+ base.UpdateDataValidation(property, state, error);
+ }
+
+ ///
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ base.OnPointerPressed(e);
+
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ {
+ e.Handled = true;
+
+ _ignoreButtonClick = _isPopupClosing;
+
+ _isPressed = true;
+ UpdatePseudoClasses();
+ }
}
+ ///
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ base.OnPointerReleased(e);
+
+ if (_isPressed && e.InitialPressMouseButton == MouseButton.Left)
+ {
+ e.Handled = true;
+
+ if (!_ignoreButtonClick)
+ {
+ TogglePopUp();
+ }
+ else
+ {
+ _ignoreButtonClick = false;
+ }
+
+ _isPressed = false;
+ UpdatePseudoClasses();
+ }
+ }
+
+ ///
+ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
+ {
+ base.OnPointerCaptureLost(e);
+
+ _isPressed = false;
+ UpdatePseudoClasses();
+ }
+
+ ///
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
base.OnPointerWheelChanged(e);
@@ -562,6 +405,8 @@ namespace Avalonia.Controls
}
}
}
+
+ ///
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
@@ -576,78 +421,57 @@ namespace Avalonia.Controls
}
}
}
+
+ ///
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
+ _isPressed = false;
+ UpdatePseudoClasses();
+
SetSelectedDate();
}
-
- private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
+
+ ///
+ protected override void OnKeyUp(KeyEventArgs e)
{
- var oldValue = (bool)e.OldValue!;
- var value = (bool)e.NewValue!;
+ var key = e.Key;
- if (_popUp != null && _popUp.Child != null)
+ if ((key == Key.Space || key == Key.Enter) && IsEffectivelyEnabled) // Key.GamepadA is not currently supported
+ {
+ // Since the TextBox is used for direct date entry,
+ // it isn't supported to open the popup/flyout using these keys.
+ // Other controls open the popup/flyout here.
+ }
+ else if (key == Key.Down && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && IsEffectivelyEnabled)
{
- if (value != oldValue)
+ // It is only possible to open the popup using these keys.
+ // This is important as the down key is handled by calendar.
+ // If down also closed the popup, the date would move 1 week
+ // and then close the popup. This isn't user friendly at all.
+ // (calendar doesn't mark as handled either).
+ // The Escape key will still close the popup.
+ if (IsDropDownOpen == false)
{
- if (_calendar!.DisplayMode != CalendarMode.Month)
- {
- _calendar.DisplayMode = CalendarMode.Month;
- }
+ e.Handled = true;
- if (value)
+ if (!_ignoreButtonClick)
{
- OpenDropDown();
+ TogglePopUp();
}
else
{
- _popUp.IsOpen = false;
- OnCalendarClosed(new RoutedEventArgs());
+ _ignoreButtonClick = false;
}
- }
- }
- }
- private void OnSelectedDateChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var addedDate = (DateTime?)e.NewValue;
- var removedDate = (DateTime?)e.OldValue;
- if (SelectedDate != null)
- {
- DateTime day = SelectedDate.Value;
-
- // When the SelectedDateProperty change is done from
- // OnTextPropertyChanged method, two-way binding breaks if
- // BeginInvoke is not used:
- Threading.Dispatcher.UIThread.InvokeAsync(() =>
- {
- _settingSelectedDate = true;
- Text = DateTimeToString(day);
- _settingSelectedDate = false;
- OnDateSelected(addedDate, removedDate);
- });
-
- // When DatePickerDisplayDateFlag is TRUE, the SelectedDate
- // change is coming from the Calendar UI itself, so, we
- // shouldn't change the DisplayDate since it will automatically
- // be changed by the Calendar
- if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag))
- {
- DisplayDate = day;
+ UpdatePseudoClasses();
}
- if(_calendar != null)
- _calendar.CalendarDatePickerDisplayDateFlag = false;
- }
- else
- {
- _settingSelectedDate = true;
- SetWaterMarkText();
- _settingSelectedDate = false;
- OnDateSelected(addedDate, removedDate);
}
+
+ base.OnKeyUp(e);
}
+
private void OnDateFormatChanged()
{
if (_textBox != null)
@@ -672,54 +496,6 @@ namespace Avalonia.Controls
}
}
}
- private void OnSelectedDateFormatChanged(AvaloniaPropertyChangedEventArgs e)
- {
- OnDateFormatChanged();
- }
- private void OnCustomDateFormatStringChanged(AvaloniaPropertyChangedEventArgs e)
- {
- if(SelectedDateFormat == CalendarDatePickerFormat.Custom)
- {
- OnDateFormatChanged();
- }
- }
- private void OnTextChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var oldValue = (string?)e.OldValue;
- var value = (string?)e.NewValue;
-
- if (!_suspendTextChangeHandler)
- {
- if (value != null)
- {
- if (_textBox != null)
- {
- _textBox.Text = value;
- }
- else
- {
- _defaultText = value;
- }
- if (!_settingSelectedDate)
- {
- SetSelectedDate();
- }
- }
- else
- {
- if (!_settingSelectedDate)
- {
- _settingSelectedDate = true;
- SelectedDate = null;
- _settingSelectedDate = false;
- }
- }
- }
- else
- {
- SetWaterMarkText();
- }
- }
///
/// Raises the
@@ -735,6 +511,7 @@ namespace Avalonia.Controls
{
DateValidationError?.Invoke(this, e);
}
+
private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
{
EventHandler? handler = this.SelectedDateChanged;
@@ -756,10 +533,12 @@ namespace Avalonia.Controls
handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
}
}
+
private void OnCalendarClosed(EventArgs e)
{
CalendarClosed?.Invoke(this, e);
}
+
private void OnCalendarOpened(EventArgs e)
{
CalendarOpened?.Invoke(this, e);
@@ -769,7 +548,8 @@ namespace Avalonia.Controls
{
Focus();
IsDropDownOpen = false;
- }
+ }
+
private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
{
if (e.AddedDate != this.DisplayDate)
@@ -777,6 +557,7 @@ namespace Avalonia.Controls
SetValue(DisplayDateProperty, (DateTime) e.AddedDate!);
}
}
+
private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
{
Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!");
@@ -802,6 +583,7 @@ namespace Avalonia.Controls
}
}
}
+
private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
@@ -810,6 +592,7 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+
private void Calendar_KeyDown(object? sender, KeyEventArgs e)
{
Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
@@ -825,10 +608,12 @@ namespace Avalonia.Controls
}
}
}
+
private void TextBox_GotFocus(object? sender, RoutedEventArgs e)
{
IsDropDownOpen = false;
}
+
private void TextBox_KeyDown(object? sender, KeyEventArgs e)
{
if (!e.Handled)
@@ -836,6 +621,7 @@ namespace Avalonia.Controls
e.Handled = ProcessDatePickerKey(e);
}
}
+
private void TextBox_TextChanged()
{
if (_textBox != null)
@@ -845,21 +631,33 @@ namespace Avalonia.Controls
_suspendTextChangeHandler = false;
}
}
+
private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
{
_ignoreButtonClick = _isPopupClosing;
+
+ _isPressed = true;
+ UpdatePseudoClasses();
+ }
+
+ private void DropDownButton_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ _isPressed = false;
+ UpdatePseudoClasses();
}
+
private void DropDownButton_Click(object? sender, RoutedEventArgs e)
{
if (!_ignoreButtonClick)
{
- HandlePopUp();
+ TogglePopUp();
}
else
{
_ignoreButtonClick = false;
}
}
+
private void PopUp_Closed(object? sender, EventArgs e)
{
IsDropDownOpen = false;
@@ -871,7 +669,11 @@ namespace Avalonia.Controls
}
}
- private void HandlePopUp()
+ ///
+ /// Toggles the property to open/close the calendar popup.
+ /// This will automatically adjust control focus as well.
+ ///
+ private void TogglePopUp()
{
if (IsDropDownOpen)
{
@@ -880,24 +682,28 @@ namespace Avalonia.Controls
}
else
{
- ProcessTextBox();
+ SetSelectedDate();
+ IsDropDownOpen = true;
+ _calendar!.Focus();
}
}
+
private void OpenDropDown()
{
if (_calendar != null)
{
_calendar.Focus();
- OpenPopUp();
+
+ // Open the PopUp
+ _onOpenSelectedDate = SelectedDate;
+ _popUp!.IsOpen = true;
+ _isFlyoutOpen = _popUp!.IsOpen;
+
+ UpdatePseudoClasses();
_calendar.ResetStates();
OnCalendarOpened(new RoutedEventArgs());
}
}
- private void OpenPopUp()
- {
- _onOpenSelectedDate = SelectedDate;
- _popUp!.IsOpen = true;
- }
///
/// Input text is parsed in the correct format and changed into a
@@ -944,8 +750,10 @@ namespace Avalonia.Controls
throw textParseError.Exception;
}
}
+
return null;
}
+
private string? DateTimeToString(DateTime d)
{
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
@@ -959,11 +767,12 @@ namespace Avalonia.Controls
case CalendarDatePickerFormat.Custom:
return string.Format(CultureInfo.CurrentCulture, d.ToString(CustomDateFormatString, dtfi));
}
+
return null;
}
+
private bool ProcessDatePickerKey(KeyEventArgs e)
{
-
switch (e.Key)
{
case Key.Enter:
@@ -975,20 +784,16 @@ namespace Avalonia.Controls
{
if ((e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control)
{
- HandlePopUp();
+ TogglePopUp();
return true;
}
break;
}
}
+
return false;
}
- private void ProcessTextBox()
- {
- SetSelectedDate();
- IsDropDownOpen = true;
- _calendar!.Focus();
- }
+
private void SetSelectedDate()
{
if (_textBox != null)
@@ -1037,6 +842,7 @@ namespace Avalonia.Controls
}
}
}
+
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
@@ -1070,6 +876,7 @@ namespace Avalonia.Controls
}
}
}
+
private void SetWaterMarkText()
{
if (_textBox != null)
@@ -1111,32 +918,10 @@ namespace Avalonia.Controls
|| value == CalendarDatePickerFormat.Short
|| value == CalendarDatePickerFormat.Custom;
}
+
private static bool IsValidDateFormatString(string formatString)
{
return !string.IsNullOrWhiteSpace(formatString);
}
- private static DateTime DiscardDayTime(DateTime d)
- {
- int year = d.Year;
- int month = d.Month;
- DateTime newD = new DateTime(year, month, 1, 0, 0, 0);
- return newD;
- }
- private static DateTime? DiscardTime(DateTime? d)
- {
- if (d == null)
- {
- return null;
- }
- else
- {
- DateTime discarded = (DateTime) d;
- int year = discarded.Year;
- int month = discarded.Month;
- int day = discarded.Day;
- DateTime newD = new DateTime(year, month, day, 0, 0, 0);
- return newD;
- }
- }
}
}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs
new file mode 100644
index 0000000000..647910cb6b
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs
@@ -0,0 +1,87 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Provides data for the
+ ///
+ /// event.
+ ///
+ public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
+ {
+ private bool _throwException;
+
+ ///
+ /// Initializes a new instance of the
+ ///
+ /// class.
+ ///
+ ///
+ /// The initial exception from the
+ ///
+ /// event.
+ ///
+ ///
+ /// The text that caused the
+ ///
+ /// event.
+ ///
+ public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text)
+ {
+ Text = text;
+ Exception = exception;
+ }
+
+ ///
+ /// Gets the initial exception associated with the
+ ///
+ /// event.
+ ///
+ ///
+ /// The exception associated with the validation failure.
+ ///
+ public Exception Exception { get; private set; }
+
+ ///
+ /// Gets the text that caused the
+ ///
+ /// event.
+ ///
+ ///
+ /// The text that caused the validation failure.
+ ///
+ public string Text { get; private set; }
+
+ ///
+ /// Gets or sets a value indicating whether
+ ///
+ /// should be thrown.
+ ///
+ ///
+ /// True if the exception should be thrown; otherwise, false.
+ ///
+ ///
+ /// If set to true and
+ ///
+ /// is null.
+ ///
+ public bool ThrowException
+ {
+ get => _throwException;
+ set
+ {
+ if (value && Exception == null)
+ {
+ throw new ArgumentException("Cannot Throw Null Exception");
+ }
+
+ _throwException = value;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs
new file mode 100644
index 0000000000..4d96859d75
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs
@@ -0,0 +1,31 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Specifies date formats for a
+ /// .
+ ///
+ public enum CalendarDatePickerFormat
+ {
+ ///
+ /// Specifies that the date should be displayed using unabbreviated days
+ /// of the week and month names.
+ ///
+ Long = 0,
+
+ ///
+ /// Specifies that the date should be displayed using abbreviated days
+ /// of the week and month names.
+ ///
+ Short = 1,
+
+ ///
+ /// Specifies that the date should be displayed using a custom format string.
+ ///
+ Custom = 2
+ }
+}
diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs
index fabf8978c7..adee7d4d90 100644
--- a/src/Avalonia.Controls/Canvas.cs
+++ b/src/Avalonia.Controls/Canvas.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reactive.Concurrency;
using Avalonia.Input;
using Avalonia.Layout;
@@ -159,47 +160,57 @@ namespace Avalonia.Controls
}
///
- /// Arranges the control's children.
+ /// Arranges a single child.
///
- /// The size allocated to the control.
- /// The space taken.
- protected override Size ArrangeOverride(Size finalSize)
+ /// The child to arrange.
+ /// The size allocated to the canvas.
+ protected virtual void ArrangeChild(Control child, Size finalSize)
{
- foreach (Control child in Children)
- {
- double x = 0.0;
- double y = 0.0;
- double elementLeft = GetLeft(child);
+ double x = 0.0;
+ double y = 0.0;
+ double elementLeft = GetLeft(child);
- if (!double.IsNaN(elementLeft))
- {
- x = elementLeft;
- }
- else
+ if (!double.IsNaN(elementLeft))
+ {
+ x = elementLeft;
+ }
+ else
+ {
+ // Arrange with right.
+ double elementRight = GetRight(child);
+ if (!double.IsNaN(elementRight))
{
- // Arrange with right.
- double elementRight = GetRight(child);
- if (!double.IsNaN(elementRight))
- {
- x = finalSize.Width - child.DesiredSize.Width - elementRight;
- }
+ x = finalSize.Width - child.DesiredSize.Width - elementRight;
}
+ }
- double elementTop = GetTop(child);
- if (!double.IsNaN(elementTop) )
- {
- y = elementTop;
- }
- else
+ double elementTop = GetTop(child);
+ if (!double.IsNaN(elementTop))
+ {
+ y = elementTop;
+ }
+ else
+ {
+ double elementBottom = GetBottom(child);
+ if (!double.IsNaN(elementBottom))
{
- double elementBottom = GetBottom(child);
- if (!double.IsNaN(elementBottom))
- {
- y = finalSize.Height - child.DesiredSize.Height - elementBottom;
- }
+ y = finalSize.Height - child.DesiredSize.Height - elementBottom;
}
+ }
- child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+ child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+ }
+
+ ///
+ /// Arranges the control's children.
+ ///
+ /// The size allocated to the control.
+ /// The space taken.
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ foreach (Control child in Children)
+ {
+ ArrangeChild(child, finalSize);
}
return finalSize;
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index ee2378101a..2b122d4174 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty PlacementRectProperty =
- AvaloniaProperty.Register(nameof(PlacementRect));
+ Popup.PlacementRectProperty.AddOwner();
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index f04c79505e..047667567d 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -38,13 +38,13 @@ namespace Avalonia.Controls
/// Defines the property
///
public static readonly StyledProperty