{"id":19762561,"url":"https://github.com/guorg/gu.wpf.adorners","last_synced_at":"2025-04-06T19:12:28.976Z","repository":{"id":33958232,"uuid":"51085963","full_name":"GuOrg/Gu.Wpf.Adorners","owner":"GuOrg","description":null,"archived":false,"fork":false,"pushed_at":"2023-12-25T13:09:28.000Z","size":932,"stargazers_count":90,"open_issues_count":13,"forks_count":12,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-06T19:12:24.560Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GuOrg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-02-04T15:45:53.000Z","updated_at":"2024-11-05T04:15:37.000Z","dependencies_parsed_at":"2023-12-25T14:32:36.630Z","dependency_job_id":"28fd45e6-cb85-4659-a95a-8518447dfb5c","html_url":"https://github.com/GuOrg/Gu.Wpf.Adorners","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Wpf.Adorners","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Wpf.Adorners/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Wpf.Adorners/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GuOrg%2FGu.Wpf.Adorners/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GuOrg","download_url":"https://codeload.github.com/GuOrg/Gu.Wpf.Adorners/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535519,"owners_count":20954576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T04:05:05.535Z","updated_at":"2025-04-06T19:12:28.959Z","avatar_url":"https://github.com/GuOrg.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gu.Wpf.Adorners\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![NuGet](https://img.shields.io/nuget/v/Gu.Wpf.Adorners.svg)](https://www.nuget.org/packages/Gu.Wpf.Adorners/)\n[![Build Status](https://dev.azure.com/guorg/Gu.Wpf.Adorners/_apis/build/status/GuOrg.Gu.Wpf.Adorners?branchName=master)](https://dev.azure.com/guorg/Gu.Wpf.Adorners/_build/latest?definitionId=5\u0026branchName=master)\n[![Build status](https://ci.appveyor.com/api/projects/status/7jwv4kskke9kraa0?svg=true)](https://ci.appveyor.com/project/JohanLarsson/gu-wpf-adorners)\n\n## A collection of adorners for wpf.\n\n- [About](#about)\n  - [How to install](#How-to-install)\n- [Watermark](#watermark)\n  - [Basic Usage](#basic-usage) \n    - [TextBox](#textbox)\n    - [PasswordBox](#passwordbox)\n    - [ComboBox](#combobox)\n  - [Advanced Usage](#advanced-usage)\n    - [Binding](#binding)\n    - [Attached Properties](#attached-properties)\n    - [Inheriting Style](#inheriting-style)\n    - [Explicit Styling](#explicit-styling)\n    - [TextStyle](#textstyle)\n    - [Visibility](#visibility)\n    - [Rendered Example](#rendered-example)\n  - [Watermark Properties](#watermark-properties)\n    - [Watermark.Text](#watermarktext)\n    - [Watermark.VisibleWhen](#watermarkvisiblewhen)\n    - [Watermark.TextStyle](#watermarktextstyle)\n      - [Default Watermark Style](#default-watermark-style)\n- [Overlay](#overlay)\n  - [Attached properties](#attached-properties)\n- [Info](#info)\n- [DragAdorner](#dragadorner)\n\n# About\n*An Adorner is a custom FrameworkElement that is bound to a UIElement. Adorners are rendered in an AdornerLayer, which is a rendering surface that is always on top of the adorned element or a collection of adorned elements.*\n\nWith Gu.Wpf.Adorners you can Overlay / Watermark multiple controls.\n\n## How to install\n\n  In a WPF application, install from NuGet.\n\n    ```powershell\n    PM\u003e install-package Gu.Wpf.Adorners\n    ```\n\n  NuGet installs the dll, and adds it as a resource to your project.\n\n# Watermark\n\n## Basic Usage\n\nAdd the namespace to your control.\n```xaml\n\u003cUserControl ...\n             xmlns:adorners=\"http://gu.se/Adorners\"\u003e\n```\n### TextBox\n\n```xaml\n\u003cTextBox adorners:Watermark.Text=\"Write something here\" /\u003e\n```\n\n### PasswordBox\n\n```xaml\n\u003cPasswordBox adorners:Watermark.Text=\"Write something here\" /\u003e\n```\n\n### ComboBox\n\n```xaml\n\u003cComboBox adorners:Watermark.Text=\"Write something here\"\u003e\n    \u003cComboBoxItem\u003eabc\u003c/ComboBoxItem\u003e\n    \u003cComboBoxItem\u003ecde\u003c/ComboBoxItem\u003e\n    \u003cComboBoxItem\u003efgh\u003c/ComboBoxItem\u003e\n\u003c/ComboBox\u003e\n```\n\n## Advanced Usage\nThe below examples apply to `TextBox`, `PasswordBox` and `ComboBox`.\n\n### Binding\nInstead of setting a static text as watermark, you can bind its value:\n```xaml\n\u003c!--Bind to the Text property of a different Element--\u003e\n\u003cTextBox adorners:Watermark.Text=\"{Binding Text, ElementName=ElementNameHere}\" /\u003e\n\n\u003c!--Bind to a property in your ViewModel/codebehind (make sure to set Datacontext)--\u003e\n\u003cTextBox adorners:Watermark.Text=\"{Binding Path=SomeProperty}\" /\u003e\n\n\u003c!--Bind to a static resource using the Gu.Wpf.Localization localization plugin--\u003e\n\u003cTextBox adorners:Watermark.Text=\"{l:Static p:Resources.Label_Password}\" /\u003e\n```\n*For more info about the localization plugin, have a look at [Gu.Wpf.Localization](https://github.com/GuOrg/Gu.Localization)*\n\n### Attached Properties\nAll properties are attached properties so you can do:\n```xaml\n\u003cStackPanel adorners:Watermark.Text=\"Write something here\"\n            adorners:Watermark.TextStyle=\"{StaticResource AdornerTextStyle}\"\n            adorners:Watermark.VisibleWhen=\"EmptyAndNotKeyboardFocused\"\u003e\n    \u003cTextBox Text=\"{Binding SomeProp}\"/\u003e\n    \u003cTextBox Text=\"{Binding SomeOtherProp}\" /\u003e\n\u003c/StackPanel\u003e\n```\n\n### Inheriting Style\n\nThe Watermark inherits the following styles from the UIElement:\n- `FontFamily`\n- `FontStyle`\n- `FontWeight`\n- `FontStretch`\n- `FontSize`\n- `Foreground`\n- `TextEffects`\n\nThe below example will show a font 32 and bold watermark.\n```xaml\n\u003cTextBox adorners:Watermark.Text=\"Foo\"\n         FontSize=\"32\"\n         FontWeight=\"Bold\"/\u003e\n```\n\n### Explicit Styling\n\nBeside inheriting style, you can explicitly set it.\n```xaml\n\u003cUserControl.Resources\u003e\n    \u003cStyle x:Key=\"AdornerTextStyle\" TargetType=\"{x:Type TextBlock}\"\u003e\n        \u003cSetter Property=\"Foreground\" Value=\"Green\" /\u003e\n        \u003cSetter Property=\"Opacity\" Value=\"1\" /\u003e\n    \u003c/Style\u003e\n\u003c/UserControl.Resources\u003e\n...\n\u003cGrid\u003e\n    \u003c!--Style is set explicitly. The Watermark will render with a green Foreground.--\u003e\n    \u003cTextBox adorners:Watermark.Text=\"Explicit style\"\n             adorners:Watermark.TextStyle=\"{StaticResource AdornerTextStyle}\" /\u003e\n\u003c/Grid\u003e\n```\n\nBy setting the `adorners:Watermark` to a ContentControl/Panel (Grid, GroupBox, StackPanel, etc.), all TextBox, PasswordBox and ComboBox children inherit the value.\n```xaml\n\u003cUserControl.Resources\u003e\n    \u003cStyle x:Key=\"AdornerTextStyle\" TargetType=\"{x:Type TextBlock}\"\u003e\n        \u003cSetter Property=\"Foreground\" Value=\"Green\" /\u003e\n        \u003cSetter Property=\"Opacity\" Value=\"1\" /\u003e\n    \u003c/Style\u003e\n    \u003cStyle TargetType=\"{x:Type GroupBox}\"\u003e\n        \u003cSetter Property=\"BorderBrush\" Value=\"Blue\" /\u003e\n        \u003cSetter Property=\"BorderThickness\" Value=\"1\" /\u003e\n    \u003c/Style\u003e\n\u003c/UserControl.Resources\u003e\n...\n\u003cGrid\u003e\n    \u003c!--By setting the Watermark.TextStyle on the ContentContol/Panel, all children inherit the TextStyle.--\u003e\n    \u003c!--Same goes for Text and VisibleWhen (see below)--\u003e\n    \u003cGroupBox adorners:Watermark.Text=\"Inherited style\"\n              adorners:Watermark.TextStyle=\"{StaticResource AdornerTextStyle}\"\n              Header=\"Inherited style\"\u003e\n        \u003cStackPanel\u003e\n            \u003cTextBox /\u003e\n            \u003cTextBox /\u003e\n        \u003c/StackPanel\u003e\n    \u003c/GroupBox\u003e\n\n    \u003c!--Both TextBox will render with the same Watermark Text: \"Inherited text\"--\u003e\n    \u003cGroupBox adorners:Watermark.Text=\"Inherited text\" Header=\"Inherited text\"\u003e\n        \u003cStackPanel\u003e\n            \u003cTextBox /\u003e\n            \u003cTextBox /\u003e\n        \u003c/StackPanel\u003e\n    \u003c/GroupBox\u003e\n\n    \u003c!--The top 3 elements inherit the same Watermark Text, the last one will use its own.--\u003e\n    \u003cGroupBox adorners:Watermark.Text=\"Inherited text\" Header=\"Inherited text\"\u003e\n        \u003cStackPanel\u003e\n            \u003cTextBox /\u003e\n            \u003cPasswordBox /\u003e\n            \u003cComboBox /\u003e\n            \u003cTextBox adorners:Watermark.Text=\"This textbox does not inherit\" /\u003e\n        \u003c/StackPanel\u003e\n    \u003c/GroupBox\u003e\n\u003c/Grid\u003e\n```\n\n### TextStyle\nTextStyle accepts a style for `TextBlock` the text is drawn where the textbox text is drawn so no margins needed.\n\n```xaml\n\u003cPasswordBox adorners:Watermark.Text=\"PASSWORD\"\u003e\n    \u003cadorners:Watermark.TextStyle\u003e\n        \u003cStyle TargetType=\"{x:Type TextBlock}\"\u003e\n            \u003cSetter Property=\"Opacity\" Value=\"0.5\" /\u003e\n            \u003cSetter Property=\"FontStyle\" Value=\"Oblique\" /\u003e\n            \u003cSetter Property=\"VerticalAlignment\" Value=\"Center\" /\u003e\n        \u003c/Style\u003e\n    \u003c/adorners:Watermark.TextStyle\u003e\n\u003c/PasswordBox\u003e\n```\n\n### Visibility\nThe behaviour of the watermark can be set with the `VisibleWhen` property.\n\n```xaml\n\u003c!--The watermark shows as long as the control has no value.--\u003e\n\u003cTextBox adorners:Watermark.Text=\"visible when empty\"\n         adorners:Watermark.VisibleWhen=\"Empty\" /\u003e\n\n\u003c!--The watermark shows as long as the control has no value and is not focused.--\u003e\n\u003cTextBox adorners:Watermark.Text=\"visible when not keyboard focused (default)\"\n         adorners:Watermark.VisibleWhen=\"EmptyAndNotKeyboardFocused\" /\u003e\n```\n\n\n### Rendered Example\nThe above examples render to the following visualisation:\n\n![watermarked](http://i.imgur.com/CGMrn3S.gif)\n\n## Watermark Properties\n\n### Watermark.Text\nThe text displayed as watermark in the UIElement.\n\n### Watermark.VisibleWhen\n- `Empty`\n- `EmptyAndNotKeyboardFocused` *(Default)*\n\n### Watermark.TextStyle\nTextStyle accepts a style for `TextBlock` the text is drawn where the textbox text is drawn so no margins needed.\n\n#### Default Watermark Style\n```xaml\n\u003cStyle TargetType=\"{x:Type local:WatermarkAdorner}\"\u003e\n    \u003cSetter Property=\"IsHitTestVisible\" Value=\"False\" /\u003e\n    \u003cSetter Property=\"Focusable\" Value=\"False\" /\u003e\n    \u003cSetter Property=\"TextElement.FontFamily\" Value=\"{Binding AdornedTextBox.FontFamily, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.FontStyle\" Value=\"{Binding AdornedTextBox.FontStyle, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.FontWeight\" Value=\"{Binding AdornedTextBox.FontWeight, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.FontStretch\" Value=\"{Binding AdornedTextBox.FontStretch, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.FontSize\" Value=\"{Binding AdornedTextBox.FontSize, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.Foreground\" Value=\"{Binding AdornedTextBox.Foreground, RelativeSource={RelativeSource Self}}\" /\u003e\n    \u003cSetter Property=\"TextElement.TextEffects\" Value=\"{Binding Path=(TextElement.TextEffects), RelativeSource ={RelativeSource Self}}\" /\u003e\n\n    \u003cSetter Property=\"TextStyle\"\u003e\n        \u003cSetter.Value\u003e\n            \u003cStyle TargetType=\"{x:Type TextBlock}\"\u003e\n                \u003cSetter Property=\"Opacity\" Value=\"0.5\" /\u003e\n                \u003cSetter Property=\"FontStyle\" Value=\"Oblique\" /\u003e\n            \u003c/Style\u003e\n        \u003c/Setter.Value\u003e\n    \u003c/Setter\u003e\n\u003c/Style\u003e\n```\n\n\n# Overlay\n\nFor adding an overlay to an element.\nThe overlay visibility is controlled with `adorners:Overlay.Visibility`.\n\nSample:\n```xaml\n\u003cButton adorners:Overlay.Visibility=\"{Binding IsChecked,\n                                              ElementName=IsVisibleButton,\n                                              Converter={StaticResource BooleanToVisibilityConverter}}\"\u003e\n    \u003cadorners:Overlay.Content\u003e\n        \u003cBorder BorderBrush=\"HotPink\"\n                BorderThickness=\"2\" /\u003e\n    \u003c/adorners:Overlay.Content\u003e\n\u003c/Button\u003e\n```\nRenders: ![overlay](http://i.imgur.com/Csrqi6L.png)\n\n## Attached properties\nAll properties are attached properties so you can do:\nNote that this sample makes little sense overspecifying, providing it to give copy-paste friendly xaml.\n\n```xaml\n\u003cStackPanel adorners:Overlay.ContentPresenterStyle=\"{StaticResource OverlayStyle}\"\n            adorners:Overlay.ContentTemplateSelector=\"{StaticResource OverlayTemplateSelector}\"\u003e\n    \u003cadorners:Overlay.ContentTemplate\u003e\n        \u003cDataTemplate\u003e\n            \u003cBorder BorderBrush=\"GreenYellow\"\n                    BorderThickness=\"3\"\u003e\n                \u003cTextBlock HorizontalAlignment=\"Center\"\n                            VerticalAlignment=\"Center\"\n                            Text=\"{Binding}\" /\u003e\n            \u003c/Border\u003e\n        \u003c/DataTemplate\u003e\n    \u003c/adorners:Overlay.ContentTemplate\u003e\n\n    \u003cButton Width=\"100\"\n            Height=\"100\"\n            Margin=\"5\"\n            Foreground=\"Yellow\" /\u003e\n\n    \u003cButton Width=\"100\"\n            Height=\"100\"\n            Margin=\"5\"\n            Foreground=\"Blue\" /\u003e\n\u003c/StackPanel\u003e\n```\n\n# Info\n\nThis is very similar to the adorner used for validation in WPF.\n\nSample:\n\n```xaml\n\u003cButton adorners:Info.Visibility=\"{Binding IsChecked,\n                                           ElementName=IsVisibleToggleButton,\n                                           Converter={StaticResource BooleanToVisibilityConverter}}\"\u003e\n    \u003cadorners:Info.Template\u003e\n        \u003cControlTemplate\u003e\n            \u003cGrid\u003e\n                \u003cGrid.RowDefinitions\u003e\n                    \u003cRowDefinition Height=\"Auto\" /\u003e\n                    \u003cRowDefinition Height=\"Auto\" /\u003e\n                \u003c/Grid.RowDefinitions\u003e\n                \u003cBorder HorizontalAlignment=\"Right\"\n                        BorderBrush=\"Blue\"\n                        BorderThickness=\"0,0,0,1\"\u003e\n                    \u003cAdornedElementPlaceholder /\u003e\n                \u003c/Border\u003e\n                \u003cTextBlock Grid.Row=\"1\"\n                            HorizontalAlignment=\"Right\"\n                            Text=\"Some info text\"\n                            TextAlignment=\"Right\" /\u003e\n            \u003c/Grid\u003e\n        \u003c/ControlTemplate\u003e\n    \u003c/adorners:Info.Template\u003e\n\u003c/Button\u003e\n```\nRenders: ![info](http://i.imgur.com/9ODbtO9.png)\n\nThe DataContext of the adorner is bound to DataContext of AdornedElement.\n\n# DragAdorner\n\nShows an adorner that follows the mouse while draging in a drag \u0026 drop operation.\n\n![dragadorner](https://user-images.githubusercontent.com/1640096/31116961-556f2dcc-a828-11e7-941e-d967eb04fed3.gif)\n\nSample:\n\n```cs\nprivate static bool TryGetDropTarget(object sender, out ContentPresenter target)\n{\n    target = null;\n    if (sender is ContentPresenter cp \u0026\u0026\n        cp.Content == null)\n    {\n        target = cp;\n    }\n\n    return target != null;\n}\n\nprivate void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)\n{\n    if (e.Source is ContentPresenter contentPresenter \u0026\u0026\n        contentPresenter.Content != null)\n    {\n        var data = new DataObject(typeof(DragItem), contentPresenter.Content);\n        using (var adorner = DragAdorner.Create(contentPresenter))\n        {\n            data.SetData(typeof(Adorner), adorner);\n            contentPresenter.SetCurrentValue(ContentPresenter.ContentProperty, null);\n            DragDrop.DoDragDrop(contentPresenter, data, DragDropEffects.Move);\n            var target = data.GetData(typeof(UIElement));\n            if (target == null)\n            {\n                contentPresenter.SetCurrentValue(ContentPresenter.ContentProperty, data.GetData(typeof(DragItem)));\n            }\n        }\n    }\n}\n\nprivate void OnDrop(object sender, DragEventArgs e)\n{\n    if (TryGetDropTarget(e.Source, out var contentPresenter))\n    {\n        contentPresenter.SetCurrentValue(ContentPresenter.ContentProperty, e.Data.GetData(typeof(DragItem)));\n        e.Effects = DragDropEffects.Move;\n        e.Data.SetData(typeof(UIElement), contentPresenter);\n        e.Handled = true;\n    }\n}\n\nprivate void OnDragLeave(object sender, DragEventArgs e)\n{\n    if (TryGetDropTarget(e.Source, out var contentPresenter) \u0026\u0026\n        e.Data.GetData(typeof(Adorner)) is ContentDragAdorner adorner)\n    {\n        adorner.RemoveSnap(contentPresenter);\n        e.Effects = DragDropEffects.None;\n        e.Handled = true;\n    }\n}\n\nprivate void OnDragEnter(object sender, DragEventArgs e)\n{\n    if (TryGetDropTarget(e.Source, out var contentPresenter) \u0026\u0026\n        e.Data.GetData(typeof(Adorner)) is ContentDragAdorner adorner)\n    {\n        adorner.SnapTo(contentPresenter);\n        e.Effects = DragDropEffects.Move;\n        e.Handled = true;\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguorg%2Fgu.wpf.adorners","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguorg%2Fgu.wpf.adorners","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguorg%2Fgu.wpf.adorners/lists"}