diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs
index c55d6de10f..5d258cb040 100644
--- a/src/Avalonia.Base/Media/DrawingContext.cs
+++ b/src/Avalonia.Base/Media/DrawingContext.cs
@@ -283,7 +283,8 @@ namespace Avalonia.Media
Opacity,
Clip,
GeometryClip,
- OpacityMask
+ OpacityMask,
+ RenderOptions
}
public RestoreState(DrawingContext context, PushedStateType type)
@@ -308,6 +309,8 @@ namespace Avalonia.Media
_context.PopGeometryClipCore();
else if (_type == PushedStateType.OpacityMask)
_context.PopOpacityMaskCore();
+ else if (_type == PushedStateType.RenderOptions)
+ _context.PopRenderOptionsCore();
}
}
@@ -400,6 +403,20 @@ namespace Avalonia.Media
return new PushedState(this);
}
+ ///
+ /// Pushes render options.
+ ///
+ /// The render options.
+ /// A disposable to undo the render options.
+ public PushedState PushRenderOptions(RenderOptions renderOptions)
+ {
+ PushRenderOptionsCore(renderOptions);
+ _states ??= StateStackPool.Get();
+ _states.Push(new RestoreState(this, RestoreState.PushedStateType.RenderOptions));
+ return new PushedState(this);
+ }
+ protected abstract void PushRenderOptionsCore(RenderOptions renderOptions);
+
[Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix);
[Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
@@ -415,6 +432,7 @@ namespace Avalonia.Media
protected abstract void PopOpacityCore();
protected abstract void PopOpacityMaskCore();
protected abstract void PopTransformCore();
+ protected abstract void PopRenderOptionsCore();
private static bool PenIsVisible(IPen? pen)
{
diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs
index 1f3c74c51c..7299bff850 100644
--- a/src/Avalonia.Base/Media/DrawingGroup.cs
+++ b/src/Avalonia.Base/Media/DrawingGroup.cs
@@ -53,6 +53,8 @@ namespace Avalonia.Media
set => SetValue(OpacityMaskProperty, value);
}
+ internal RenderOptions? RenderOptions { get; set; }
+
///
/// Gets or sets the collection that contains the child geometries.
///
@@ -75,6 +77,7 @@ namespace Avalonia.Media
using (context.PushOpacity(Opacity))
using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default)
using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, bounds) : default)
+ using (RenderOptions != null ? context.PushRenderOptions(RenderOptions.Value) : default)
{
foreach (var drawing in Children)
{
@@ -313,6 +316,15 @@ namespace Avalonia.Media
drawingGroup.Transform = new MatrixTransform(matrix);
}
+ protected override void PushRenderOptionsCore(RenderOptions renderOptions)
+ {
+ // Instantiate a new drawing group and set it as the _currentDrawingGroup
+ var drawingGroup = PushNewDrawingGroup();
+
+ // Set the render options on the new DrawingGroup
+ drawingGroup.RenderOptions = renderOptions;
+ }
+
protected override void PopClipCore() => Pop();
protected override void PopGeometryClipCore() => Pop();
@@ -323,6 +335,8 @@ namespace Avalonia.Media
protected override void PopTransformCore() => Pop();
+ protected override void PopRenderOptionsCore() => Pop();
+
///
/// Creates a new DrawingGroup for a Push* call by setting the
/// _currentDrawingGroup to a newly instantiated DrawingGroup,
diff --git a/src/Avalonia.Base/Media/PlatformDrawingContext.cs b/src/Avalonia.Base/Media/PlatformDrawingContext.cs
index b81582f845..410d996db2 100644
--- a/src/Avalonia.Base/Media/PlatformDrawingContext.cs
+++ b/src/Avalonia.Base/Media/PlatformDrawingContext.cs
@@ -90,6 +90,8 @@ internal sealed class PlatformDrawingContext : DrawingContext
_impl.Transform = matrix * current;
}
+ protected override void PushRenderOptionsCore(RenderOptions renderOptions) => _impl.PushRenderOptions(renderOptions);
+
protected override void PopClipCore() => _impl.PopClip();
protected override void PopGeometryClipCore() => _impl.PopGeometryClip();
@@ -102,6 +104,8 @@ internal sealed class PlatformDrawingContext : DrawingContext
_impl.Transform =
(_transforms ?? throw new ObjectDisposedException(nameof(PlatformDrawingContext))).Pop();
+ protected override void PopRenderOptionsCore() => _impl.PopRenderOptions();
+
protected override void DisposeCore()
{
if (_ownsImpl)
diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
index b52378d3b3..fe411c350d 100644
--- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
@@ -160,6 +160,17 @@ namespace Avalonia.Platform
///
void PopGeometryClip();
+ ///
+ /// Pushes render options.
+ ///
+ /// The render options.
+ void PushRenderOptions(RenderOptions renderOptions);
+
+ ///
+ /// Pops the latest render options.
+ ///
+ void PopRenderOptions();
+
///
/// Attempts to get an optional feature from the drawing context implementation.
///
diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs
index 2d3b5b0f22..44c8f66bb3 100644
--- a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs
@@ -211,3 +211,18 @@ abstract class RenderDataBrushAndPenNode : IRenderDataItemWithServerResources
public abstract Rect? Bounds { get; }
public abstract bool HitTest(Point p);
}
+
+class RenderDataRenderOptionsNode : RenderDataPushNode
+{
+ public RenderOptions RenderOptions { get; set; }
+
+ public override void Push(ref RenderDataNodeRenderContext context)
+ {
+ context.Context.PushRenderOptions(RenderOptions);
+ }
+
+ public override void Pop(ref RenderDataNodeRenderContext context)
+ {
+ context.Context.PopRenderOptions();
+ }
+}
diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs
index e7b14f138d..870a084d31 100644
--- a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs
@@ -259,6 +259,12 @@ internal class RenderDataDrawingContext : DrawingContext
});
}
+ protected override void PushRenderOptionsCore(RenderOptions renderOptions) => Push(new RenderDataRenderOptionsNode()
+ {
+ RenderOptions = renderOptions
+ });
+
+
protected override void PopClipCore() => Pop();
protected override void PopGeometryClipCore() => Pop();
@@ -269,6 +275,8 @@ internal class RenderDataDrawingContext : DrawingContext
protected override void PopTransformCore() => Pop();
+ protected override void PopRenderOptionsCore() => Pop();
+
internal override void DrawBitmap(IRef? source, double opacity, Rect sourceRect, Rect destRect)
{
if (source == null || sourceRect.IsEmpty() || destRect.IsEmpty())
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
index 61f8c8451f..56a572b343 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
@@ -122,6 +122,11 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.PushOpacityMask(mask, bounds);
}
+ public void PushRenderOptions(RenderOptions renderOptions)
+ {
+ _impl.PushRenderOptions(renderOptions);
+ }
+
public void PopOpacityMask()
{
_impl.PopOpacityMask();
@@ -137,6 +142,11 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.PopGeometryClip();
}
+ public void PopRenderOptions()
+ {
+ _impl.PopRenderOptions();
+ }
+
public object? GetFeature(Type t) => _impl.GetFeature(t);
diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 7293874671..b03ae8a2ca 100644
--- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -542,6 +542,16 @@ namespace Avalonia.Headless
{
}
+
+ public void PushRenderOptions(RenderOptions renderOptions)
+ {
+
+ }
+
+ public void PopRenderOptions()
+ {
+
+ }
}
private class HeadlessRenderTarget : IRenderTarget
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
index f95d086bf6..4d93408ce8 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
@@ -1,6 +1,7 @@
using System.Linq;
using XamlX.Ast;
using XamlX.Transform;
+using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
@@ -22,26 +23,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IXamlAstTypeReference targetType;
var templatableBaseType = context.Configuration.TypeSystem.GetType("Avalonia.Controls.Control");
-
- if ((tt?.Values.FirstOrDefault() is XamlTypeExtensionNode tn))
- {
- targetType = tn.Value;
- }
- else
+
+ targetType = tt?.Values.FirstOrDefault() switch
{
- var parentScope = context.ParentNodes().OfType()
- .FirstOrDefault();
- if (parentScope?.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style)
- targetType = parentScope.TargetType;
- else if (context.ParentNodes().Skip(1).FirstOrDefault() is XamlAstObjectNode directParentNode
- && templatableBaseType.IsAssignableFrom(directParentNode.Type.GetClrType()))
- targetType = directParentNode.Type;
- else
- targetType = new XamlAstClrTypeReference(node,
- templatableBaseType, false);
- }
-
-
+ XamlTypeExtensionNode tn => tn.Value,
+ XamlAstTextNode textNode => TypeReferenceResolver.ResolveType(context, textNode.Text, false, textNode, true),
+ _ when context.ParentNodes()
+ .OfType()
+ .FirstOrDefault() is { ScopeType: AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style } parentScope => parentScope.TargetType,
+ _ when context.ParentNodes().Skip(1).FirstOrDefault() is XamlAstObjectNode directParentNode
+ && templatableBaseType.IsAssignableFrom(directParentNode.Type.GetClrType()) => directParentNode.Type,
+ _ => new XamlAstClrTypeReference(node,
+ templatableBaseType, false)
+ };
return new AvaloniaXamlIlTargetTypeMetadataNode(on, targetType,
AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate);
@@ -59,7 +53,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ControlTemplate,
Transitions
}
-
+
public AvaloniaXamlIlTargetTypeMetadataNode(IXamlAstValueNode value, IXamlAstTypeReference targetType,
ScopeTypes type)
: base(value, value)
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index 76d236e18a..5736b7110c 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -23,6 +23,7 @@ namespace Avalonia.Skia
private readonly Vector _dpi;
private readonly Stack _maskStack = new();
private readonly Stack _opacityStack = new();
+ private readonly Stack _renderOptionsStack = new();
private readonly Matrix? _postTransform;
private double _currentOpacity = 1.0f;
private readonly bool _disableSubpixelTextRendering;
@@ -634,6 +635,21 @@ namespace Avalonia.Skia
_currentOpacity = _opacityStack.Pop();
}
+ ///
+ public void PushRenderOptions(RenderOptions renderOptions)
+ {
+ CheckLease();
+
+ _renderOptionsStack.Push(RenderOptions);
+
+ RenderOptions = RenderOptions.MergeWith(renderOptions);
+ }
+
+ public void PopRenderOptions()
+ {
+ RenderOptions = _renderOptionsStack.Pop();
+ }
+
///
public virtual void Dispose()
{
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index 5579278f21..8b288598ab 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -24,6 +24,8 @@ namespace Avalonia.Direct2D1.Media
private readonly SharpDX.DXGI.SwapChain1 _swapChain;
private readonly Action _finishedCallback;
+ private readonly Stack _renderOptionsStack = new();
+
///
/// Initializes a new instance of the class.
///
@@ -488,6 +490,18 @@ namespace Avalonia.Direct2D1.Media
PopLayer();
}
+ public void PushRenderOptions(RenderOptions renderOptions)
+ {
+ _renderOptionsStack.Push(RenderOptions);
+
+ RenderOptions = RenderOptions.MergeWith(renderOptions);
+ }
+
+ public void PopRenderOptions()
+ {
+ RenderOptions = _renderOptionsStack.Pop();
+ }
+
private void PopLayer()
{
var layer = _layers.Pop();
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 9244da5064..9650c44bef 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -49,7 +49,6 @@ namespace Avalonia.Win32
public Win32Platform()
{
- SetDpiAwareness();
CreateMessageWindow();
_dispatcher = new Win32DispatcherImpl(_hwnd);
}
@@ -80,6 +79,9 @@ namespace Avalonia.Win32
public static void Initialize(Win32PlatformOptions options)
{
s_options = options;
+
+ SetDpiAwareness();
+
var renderTimer = options.ShouldRenderOnUIThread ? new UiThreadRenderTimer(60) : new DefaultRenderTimer(60);
AvaloniaLocator.CurrentMutable
@@ -264,12 +266,31 @@ namespace Avalonia.Win32
var user32 = LoadLibrary("user32.dll");
var method = GetProcAddress(user32, nameof(SetProcessDpiAwarenessContext));
+ var dpiAwareness = Options.DpiAwareness;
+
if (method != IntPtr.Zero)
{
- if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) ||
- SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
+ if (dpiAwareness == Win32DpiAwareness.Unaware)
{
- return;
+ if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE))
+ {
+ return;
+ }
+ }
+ else if (dpiAwareness == Win32DpiAwareness.SystemDpiAware)
+ {
+ if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
+ {
+ return;
+ }
+ }
+ else if (dpiAwareness == Win32DpiAwareness.PerMonitorDpiAware)
+ {
+ if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) ||
+ SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
+ {
+ return;
+ }
}
}
@@ -278,11 +299,20 @@ namespace Avalonia.Win32
if (method != IntPtr.Zero)
{
- SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE);
+ var awareness = (dpiAwareness) switch
+ {
+ Win32DpiAwareness.Unaware => PROCESS_DPI_AWARENESS.PROCESS_DPI_UNAWARE,
+ Win32DpiAwareness.SystemDpiAware => PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE,
+ Win32DpiAwareness.PerMonitorDpiAware => PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE,
+ _ => PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE,
+ };
+
+ SetProcessDpiAwareness(awareness);
return;
}
- SetProcessDPIAware();
+ if (dpiAwareness != Win32DpiAwareness.Unaware)
+ SetProcessDPIAware();
}
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
index bbb4c37d7e..eac18dc080 100644
--- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
+++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
@@ -25,6 +25,27 @@ public enum Win32RenderingMode
Wgl = 3
}
+///
+/// Represents the DPI Awareness for the application.
+///
+public enum Win32DpiAwareness
+{
+ ///
+ /// The application is DPI unaware.
+ ///
+ Unaware,
+
+ ///
+ /// The application is system DPI aware. It will query DPI once and will not adjust to new DPI changes
+ ///
+ SystemDpiAware,
+
+ ///
+ /// The application is per-monitor DPI aware. It adjust its scale factor whenever DPI changes.
+ ///
+ PerMonitorDpiAware
+}
+
///
/// Represents the Win32 window composition mode.
///
@@ -137,4 +158,9 @@ public class Win32PlatformOptions
/// and only accepts null or .
///
public IPlatformGraphics? CustomPlatformGraphics { get; set; }
+
+ ///
+ /// Gets or sets the application's DPI awareness.
+ ///
+ public Win32DpiAwareness DpiAwareness { get; set; } = Win32DpiAwareness.PerMonitorDpiAware;
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs
index ea2407e18d..e21f6cd276 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs
@@ -286,6 +286,24 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Result);
}
+ [Fact]
+ public void ControlTemplate_With_String_TargetType()
+ {
+ var xaml = @"
+
+
+
+";
+ var template = AvaloniaRuntimeXamlLoader.Parse(xaml);
+
+ Assert.Equal(typeof(ContentControl), template.TargetType);
+
+ Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Result);
+ }
+
+
[Fact]
public void ControlTemplate_With_Panel_Children_Are_Added()
{