|
| 1 | +--- |
| 2 | +title: Adorners |
| 3 | +author: michael-hawker |
| 4 | +description: Adorners let you overlay content on top of your XAML components in a separate layer on top of everything else. |
| 5 | +keywords: Adorners, Control, Layout, InfoBadge, AdornerLayer, AdornerDecorator, Adorner, Input Validation, Resize, Highlighting |
| 6 | +dev_langs: |
| 7 | + - csharp |
| 8 | +category: Controls |
| 9 | +subcategory: Layout |
| 10 | +discussion-id: 278 |
| 11 | +issue-id: 0 |
| 12 | +icon: assets/icon.png |
| 13 | +--- |
| 14 | + |
| 15 | +# Adorners |
| 16 | + |
| 17 | +Adorners allow a developer to overlay any content on top of another UI element in a separate layer that resides on top of everything else. |
| 18 | + |
| 19 | +## Background |
| 20 | + |
| 21 | +Adorners originally existed in WPF as an extension part of the framework. [You can read more about how they worked in WPF here.](https://learn.microsoft.com/dotnet/desktop/wpf/controls/adorners-overview) See more about the commonalities and differences to WinUI adorners in the migration section below. |
| 22 | + |
| 23 | +### Without Adorners |
| 24 | + |
| 25 | +Imagine a scenario where you have a button or tab that checks a user's e-mail, and you'd like it to display the number of new e-mails that have arrived. |
| 26 | + |
| 27 | +You could try and incorporate a [`InfoBadge`](https://learn.microsoft.com/windows/apps/design/controls/info-badge) into your Visual Tree in order to display this as part of your icon, but that requires you to modify quite a bit of your content, as in this example: |
| 28 | + |
| 29 | +> [!SAMPLE InfoBadgeWithoutAdorner] |
| 30 | +
|
| 31 | +It also, by default, gets confined to the perimeter of the button and clipped, as seen above. |
| 32 | + |
| 33 | +### With Adorners |
| 34 | + |
| 35 | +However, with an Adorner instead, you can abstract this behavior from the content of your control. You can even more easily place the notification outside the bounds of the original element, like so: |
| 36 | + |
| 37 | +> [!SAMPLE AdornersInfoBadgeSample] |
| 38 | +
|
| 39 | +You can see how Adorners react to more dynamic content with this more complete example here: |
| 40 | + |
| 41 | +> [!SAMPLE AdornersTabBadgeSample] |
| 42 | +
|
| 43 | +The above example shows how to leverage XAML animations and data binding alongside the XAML-based Adorner with a `TabViewItem` which can also move or disappear. |
| 44 | + |
| 45 | +## Highlight Example |
| 46 | + |
| 47 | +Adorners can be used in a variety of scenarios. For instance, if you wanted to highlight an element and show it's alignment to other elements in a creativity app: |
| 48 | + |
| 49 | +> [!SAMPLE ElementHighlightAdornerSample] |
| 50 | +
|
| 51 | +The above examples highlights how adorners are sized and positioned directly atop the adorned element. This allows for relative positioning of elements within the context of the Adorner's visuals in relation to the Adorned Element itself. |
| 52 | + |
| 53 | +## Custom Adorner Example |
| 54 | + |
| 55 | +Adorners can be subclassed in order to encapsulate specific logic and/or styling for your scenario. |
| 56 | +For instance, you may want to create a custom Adorner that allows a user to click and edit a piece of text in place. |
| 57 | +The following example uses `IEditableObject` to control the editing lifecycle coordinated with a typical MVVM pattern binding: |
| 58 | + |
| 59 | +> [!SAMPLE InPlaceTextEditorAdornerSample] |
| 60 | +
|
| 61 | +Adorners are template-based controls, but you can use a class-backed resource dictionary to better enable usage of x:Bind for easier creation and binding to the `AdornedElement`, as seen here. |
| 62 | + |
| 63 | +You can see other example of custom adorners with the other Adorner help topics for the built-in adorners provided in this package, such as the `InputValidationAdorner` and `ResizeElementAdorner`. |
| 64 | + |
| 65 | +## Migrating from WPF |
| 66 | + |
| 67 | +The WinUI Adorner API surface adapts many similar names and concepts as WPF Adorners; however, WinUI Adorners are XAML based and make use of the attached properties to make using Adorners much simpler, like Behaviors. Where as defining Adorners in WPF required custom drawing routines. It's possible to replicate many similar scenarios with this new API surface and make better use of XAML features like data binding and styling; however, it will mean rewriting any existing WPF code. |
| 68 | + |
| 69 | +### Concepts |
| 70 | + |
| 71 | +The `AdornerLayer` is still an element of the visual tree which resides atop other content within your app and is the parent of all adorners. In WPF, this is usually already automatically a component of your app or `ScrollViewer`. Like WPF, adorners parent's in the visual tree will be the `AdornerLayer` and not the adorned element. The WinUI-based `AdornerLayer` will automatically be inserted in many common scenarios, otherwise, an `AdornerDecorator` may still be used to direct the placement of the `AdornerLayer` within the Visual Tree. |
| 72 | + |
| 73 | +The `AdornerDecorator` provides a similar purpose to that of its WPF counterpart, it will host an `AdornerLayer`. The main difference with the WinUI API is that the `AdornerDecorator` will wrap your contained content vs. in WPF it sat as a sibling to your content. We feel this makes it easier to use and ensure your adorned elements reside atop your adorned content, it also makes it easier to find within the Visual Tree for performance reasons. |
| 74 | + |
| 75 | +The `Adorner` class in WinUI is now a XAML-based element that can contain any content you wish to overlay atop your adorned element. In WPF, this was a non-visual class that required custom drawing logic to render the adorner's content. This change allows for easier creation of adorners using XAML, data binding, and styling. Many similar concepts and properties still exist between the two, like a reference to the `AdornedElement`. Any loose XAML attached via the `AdornerLayer.Xaml` attached property is automatically wrapped within a basic `Adorner` container. You can either restyle or subclass the `Adorner` class in order to better encapsulate logic of a custom `Adorner` for your specific scenario, like a behavior, as shown above. |
0 commit comments