Sdílet prostřednictvím


Přidání zástupného symbolu do textového pole

Následující příklad ukazuje, jak zobrazit zástupný text v TextBox případě, že TextBox je prázdný. TextBox Pokud obsahuje text, zástupný text je skrytý. Zástupný text pomáhá uživatelům pochopit, jaký typ vstupu TextBox očekává.

Ukázková aplikace se dvěma ovládacími prvky TextBox, které mají v nich zástupné symboly. První textové pole obsahuje příklad jména a druhý příklad e-mailu.

V tomto článku získáte informace o těchto tématech:

  • Vytvořte připojenou vlastnost pro zadání zástupného textu.
  • Vytvořte doplněk pro zobrazení zástupného textu.
  • Přidejte připojenou TextBox vlastnost do ovládacího prvku.

Vytvořte připojenou vlastnost

S připojenými vlastnostmi můžete k ovládacímu prvku připojit hodnoty. Tuto funkci používáte hodně ve WPF, například při nastavení Grid.Row nebo Panel.ZIndex vlastnostech ovládacího prvku. Další informace najdete v přehledu přidružených vlastností. Tento příklad používá připojené vlastnosti k přidání zástupného textu do objektu TextBox.

  1. Přidejte do projektu TextBoxHelper novou třídu a otevřete ji.

  2. Přidejte následující obory názvů:

    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    Imports System.Linq
    Imports System.Security.Cryptography
    Imports System.Windows
    Imports System.Windows.Controls
    Imports System.Windows.Documents
    Imports System.Windows.Media
    
  3. Vytvořte novou vlastnost závislosti s názvem Placeholder.

    Tato závislá vlastnost používá zpětný delegát změny vlastnosti.

    public static string GetPlaceholder(DependencyObject obj) =>
        (string)obj.GetValue(PlaceholderProperty);
    
    public static void SetPlaceholder(DependencyObject obj, string value) =>
        obj.SetValue(PlaceholderProperty, value);
    
    public static readonly DependencyProperty PlaceholderProperty =
        DependencyProperty.RegisterAttached(
            "Placeholder",
            typeof(string),
            typeof(TextBoxHelper),
            new FrameworkPropertyMetadata(
                defaultValue: null,
                propertyChangedCallback: OnPlaceholderChanged)
            );
    
    Public Shared Function GetPlaceholder(obj As DependencyObject) As String
        Return obj.GetValue(PlaceholderProperty)
    End Function
    
    Public Shared Sub SetPlaceholder(obj As DependencyObject, value As String)
        obj.SetValue(PlaceholderProperty, value)
    End Sub
    
    Public Shared ReadOnly PlaceholderProperty As DependencyProperty =
        DependencyProperty.RegisterAttached(
            "Placeholder",
            GetType(String),
            GetType(TextBoxHelper),
            New FrameworkPropertyMetadata(
                defaultValue:=Nothing,
                propertyChangedCallback:=AddressOf OnPlaceholderChanged)
            )
    
  4. Vytvořte metodu OnPlaceholderChanged pro integraci připojené vlastnosti s vlastností TextBox.

    private static void OnPlaceholderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBoxControl)
        {
            if (!textBoxControl.IsLoaded)
            {
                // Ensure that the events are not added multiple times
                textBoxControl.Loaded -= TextBoxControl_Loaded;
                textBoxControl.Loaded += TextBoxControl_Loaded;
            }
    
            textBoxControl.TextChanged -= TextBoxControl_TextChanged;
            textBoxControl.TextChanged += TextBoxControl_TextChanged;
    
            // If the adorner exists, invalidate it to draw the current text
            if (GetOrCreateAdorner(textBoxControl, out PlaceholderAdorner adorner))
                adorner.InvalidateVisual();
        }
    }
    
    Private Shared Sub OnPlaceholderChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim textBoxControl = TryCast(d, TextBox)
    
        If textBoxControl IsNot Nothing Then
    
            If Not textBoxControl.IsLoaded Then
    
                'Ensure that the events are not added multiple times
                RemoveHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded
                AddHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded
    
            End If
    
            RemoveHandler textBoxControl.TextChanged, AddressOf TextBoxControl_TextChanged
            AddHandler textBoxControl.TextChanged, AddressOf TextBoxControl_TextChanged
    
            'If the adorner exists, invalidate it to draw the current text
            Dim adorner As PlaceholderAdorner = Nothing
            If GetOrCreateAdorner(textBoxControl, adorner) Then
                adorner.InvalidateVisual()
            End If
    
        End If
    
    End Sub
    

    Tato metoda se volá dvěma způsoby, když se změní hodnota připojené vlastnosti:

    • Když je připojená vlastnost poprvé přidána do TextBox, je zavolána tato metoda. Tato akce poskytuje možnost integrovat připojenou vlastnost s událostmi ovládacího prvku.
    • Kdykoli je tato vlastnost změněna, může být adorner zneplatněný, aby se aktualizoval zástupný text vizuálu.

    Metoda GetOrCreateAdorner se vytvoří v další části.

  5. Vytvořte obslužné rutiny událostí pro TextBox.

    private static void TextBoxControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox textBoxControl)
        {
            textBoxControl.Loaded -= TextBoxControl_Loaded;
            GetOrCreateAdorner(textBoxControl, out _);
        }
    }
    
    private static void TextBoxControl_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (sender is TextBox textBoxControl
            && GetOrCreateAdorner(textBoxControl, out PlaceholderAdorner adorner))
        {
            // Control has text. Hide the adorner.
            if (textBoxControl.Text.Length > 0)
                adorner.Visibility = Visibility.Hidden;
    
            // Control has no text. Show the adorner.
            else
                adorner.Visibility = Visibility.Visible;
        }
    }
    
    Private Shared Sub TextBoxControl_Loaded(sender As Object, e As RoutedEventArgs)
        Dim textBoxControl As TextBox = TryCast(sender, TextBox)
    
        If textBoxControl IsNot Nothing Then
            RemoveHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded
            GetOrCreateAdorner(textBoxControl, Nothing)
        End If
    End Sub
    
    Private Shared Sub TextBoxControl_TextChanged(sender As Object, e As TextChangedEventArgs)
        Dim textBoxControl As TextBox = TryCast(sender, TextBox)
        Dim adorner As PlaceholderAdorner = Nothing
    
        If textBoxControl IsNot Nothing AndAlso GetOrCreateAdorner(textBoxControl, adorner) Then
    
            If textBoxControl.Text.Length > 0 Then
                'Control has text. Hide the adorner.
                adorner.Visibility = Visibility.Hidden
            Else
                'Control has no text. Show the adorner.
                adorner.Visibility = Visibility.Visible
            End If
    
        End If
    End Sub
    

    Událost Loaded se zpracovává tak, aby se po použití šablony ovládacího prvku mohl vytvořit adorner. Obslužná rutina se odebere po vyvolání události a vytvoří se adorner.

    Událost TextChanged je zpracována, aby se zajistilo, že je ozdoba skrytá nebo zobrazená v závislosti na tom, jestli Text má určitou hodnotu.

Vytvořit ozdobu

Vizuál Adorner je spojen s ovládacím prvkem a vykreslen v AdornerLayer. Další informace najdete v Přehled Adorners.

  1. Otevřete třídu TextBoxHelper.

  2. Přidejte následující kód pro vytvoření GetOrCreateAdorner metody.

    private static bool GetOrCreateAdorner(TextBox textBoxControl, out PlaceholderAdorner adorner)
    {
        // Get the adorner layer
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(textBoxControl);
    
        // If null, it doesn't exist or the control's template isn't loaded
        if (layer == null)
        {
            adorner = null;
            return false;
        }
    
        // Layer exists, try to find the adorner
        adorner = layer.GetAdorners(textBoxControl)?.OfType<PlaceholderAdorner>().FirstOrDefault();
    
        // Adorner never added to control, so add it
        if (adorner == null)
        {
            adorner = new PlaceholderAdorner(textBoxControl);
            layer.Add(adorner);
        }
    
        return true;
    }
    
    Private Shared Function GetOrCreateAdorner(textBoxControl As TextBox, ByRef adorner As PlaceholderAdorner) As Boolean
    
        'Get the adorner layer
        Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(textBoxControl)
    
        'If nothing, it doesn't exist or the control's template isn't loaded
        If layer Is Nothing Then
            adorner = Nothing
            Return False
        End If
    
        'Layer exists, try to find the adorner
        adorner = layer.GetAdorners(textBoxControl)?.OfType(Of PlaceholderAdorner)().FirstOrDefault()
    
        'Adorner never added to control, so add it
        If adorner Is Nothing Then
            adorner = New PlaceholderAdorner(textBoxControl)
            layer.Add(adorner)
        End If
    
        Return True
    
    End Function
    

    Tato metoda poskytuje bezpečný způsob, jak přidat nebo získat Adorner. Ozdoby vyžadují zvýšenou bezpečnost, protože jsou přidány do kontrolního AdornerLayer, který nemusí existovat. Když se u ovládacího prvku použije připojená vlastnost XAML, šablona ovládacího prvku ještě nebyla použita k vytvoření vizuálního stromu, takže vrstva doplňku neexistuje. Vrstva dekorace má být načtena po načtení ovládacího prvku. Vrstva doplňku může také chybět, pokud se u ovládacího prvku použije šablona, která vynechá vrstvu doplňku.

  3. Přidejte podřízenou třídu s názvem PlaceholderAdorner do třídy TextBoxHelper.

    public class PlaceholderAdorner : Adorner
    {
        public PlaceholderAdorner(TextBox textBox) : base(textBox) { }
    
        protected override void OnRender(DrawingContext drawingContext)
        {
            TextBox textBoxControl = (TextBox)AdornedElement;
    
            string placeholderValue = TextBoxHelper.GetPlaceholder(textBoxControl);
    
            if (string.IsNullOrEmpty(placeholderValue))
                return;
    
            // Create the formatted text object
            FormattedText text = new FormattedText(
                                        placeholderValue,
                                        System.Globalization.CultureInfo.CurrentCulture,
                                        textBoxControl.FlowDirection,
                                        new Typeface(textBoxControl.FontFamily,
                                                     textBoxControl.FontStyle,
                                                     textBoxControl.FontWeight,
                                                     textBoxControl.FontStretch),
                                        textBoxControl.FontSize,
                                        SystemColors.InactiveCaptionBrush,
                                        VisualTreeHelper.GetDpi(textBoxControl).PixelsPerDip);
    
            text.MaxTextWidth = System.Math.Max(textBoxControl.ActualWidth - textBoxControl.Padding.Left - textBoxControl.Padding.Right, 10);
            text.MaxTextHeight = System.Math.Max(textBoxControl.ActualHeight, 10);
    
            // Render based on padding of the control, to try and match where the textbox places text
            Point renderingOffset = new Point(textBoxControl.Padding.Left, textBoxControl.Padding.Top);
    
            // Template contains the content part; adjust sizes to try and align the text
            if (textBoxControl.Template.FindName("PART_ContentHost", textBoxControl) is FrameworkElement part)
            {
                Point partPosition = part.TransformToAncestor(textBoxControl).Transform(new Point(0, 0));
                renderingOffset.X += partPosition.X;
                renderingOffset.Y += partPosition.Y;
    
                text.MaxTextWidth = System.Math.Max(part.ActualWidth - renderingOffset.X, 10);
                text.MaxTextHeight = System.Math.Max(part.ActualHeight, 10);
            }
    
            // Draw the text
            drawingContext.DrawText(text, renderingOffset);
        }
    }
    
    Public Class PlaceholderAdorner
        Inherits Adorner
    
        Public Sub New(adornedElement As UIElement)
            MyBase.New(adornedElement)
        End Sub
    
        Protected Overrides Sub OnRender(drawingContext As DrawingContext)
            Dim textBoxControl As TextBox = DirectCast(AdornedElement, TextBox)
    
            Dim placeholderValue As String = TextBoxHelper.GetPlaceholder(textBoxControl)
    
            If String.IsNullOrEmpty(placeholderValue) Then
                Return
            End If
    
            'Create the formatted text object
            Dim text As New FormattedText(
                placeholderValue,
                System.Globalization.CultureInfo.CurrentCulture,
                textBoxControl.FlowDirection,
                New Typeface(textBoxControl.FontFamily,
                             textBoxControl.FontStyle,
                             textBoxControl.FontWeight,
                             textBoxControl.FontStretch),
                textBoxControl.FontSize,
                SystemColors.InactiveCaptionBrush,
                VisualTreeHelper.GetDpi(textBoxControl).PixelsPerDip)
    
            text.MaxTextWidth = Math.Max(textBoxControl.ActualWidth - textBoxControl.Padding.Left - textBoxControl.Padding.Right, 10)
            text.MaxTextHeight = Math.Max(textBoxControl.ActualHeight, 10)
    
            'Render based on padding of the control, to try and match where the textbox places text
            Dim renderingOffset As New Point(textBoxControl.Padding.Left, textBoxControl.Padding.Top)
    
            'Template contains the content part; adjust sizes to try and align the text
            Dim part As FrameworkElement = TryCast(textBoxControl.Template.FindName("PART_ContentHost", textBoxControl), FrameworkElement)
    
            If part IsNot Nothing Then
                Dim partPosition As Point = part.TransformToAncestor(textBoxControl).Transform(New Point(0, 0))
                renderingOffset.X += partPosition.X
                renderingOffset.Y += partPosition.Y
    
                text.MaxTextWidth = Math.Max(part.ActualWidth - renderingOffset.X, 10)
                text.MaxTextHeight = Math.Max(part.ActualHeight, 10)
            End If
    
            ' Draw the text
            drawingContext.DrawText(text, renderingOffset)
        End Sub
    
    End Class
    

    Adorner dědí z třídy Adorner. Tento konkrétnídorner přepíše metodu OnRender(DrawingContext) , aby nakreslil zástupný text. Pojďme kód rozčlenět:

    • Nejprve zkontrolujte, zda zástupný text existuje voláním TextBoxHelper.GetPlaceholder(textBoxControl).
    • Vytvořte objekt FormattedText. Tento objekt obsahuje všechny informace o tom, jaký text je nakreslen ve vizuálu.
    • Obě vlastnosti FormattedText.MaxTextWidthFormattedText.MaxTextHeight jsou nastaveny na oblast ovládacího prvku. Nastaví se také minimální hodnota 10, aby byl FormattedText objekt platný.
    • Uložená pozice nakresleného textu je ve renderingOffset.
    • Použijte PART_ContentHost, pokud šablona ovládacího prvku tuto možnost deklaruje. Tato část představuje, kde je text nakreslený na šabloně ovládacího prvku. Pokud se tato část najde, upravte renderingOffset ji tak, aby zohlednila její pozici.
    • Nakreslete text voláním DrawText(FormattedText, Point) a předáním FormattedText objektu a umístěním textu.

Použijte připojenou vlastnost

Jakmile je připojená vlastnost definována, musí se její obor názvů importovat do XAML a potom odkazovat na TextBox ovládací prvek. Následující kód mapuje obor názvů .NET DotnetDocsSample na obor názvů XML l.

<Window x:Class="DotnetDocsSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:DotnetDocsSample"
        Title="Recipe Tracker" Width="400" SizeToContent="Height">
    <StackPanel Margin="10">
        <TextBlock FontSize="20" TextWrapping="Wrap">Welcome to Recipe Tracker! To get started, create a new account.</TextBlock>
        
        <Label Padding="0,5">Name</Label>
        <TextBox l:TextBoxHelper.Placeholder="Ex. Jeffry Goh" />
        
        <Label Padding="0,5">Email</Label>
        <TextBox l:TextBoxHelper.Placeholder="jeffry@contoso.com" />
        
        <Label Padding="0,5">Password</Label>
        <PasswordBox />
        
        <Button HorizontalAlignment="Right" Width="100" Margin="0,10,0,5">Submit</Button>
    </StackPanel>
</Window>

Připojená vlastnost je přidána k TextBox pomocí syntaxe xmlNamespace:Class.Property.

Kompletní příklad

Následující kód je úplná TextBoxHelper třída.

public static class TextBoxHelper
{
    public static string GetPlaceholder(DependencyObject obj) =>
        (string)obj.GetValue(PlaceholderProperty);

    public static void SetPlaceholder(DependencyObject obj, string value) =>
        obj.SetValue(PlaceholderProperty, value);

    public static readonly DependencyProperty PlaceholderProperty =
        DependencyProperty.RegisterAttached(
            "Placeholder",
            typeof(string),
            typeof(TextBoxHelper),
            new FrameworkPropertyMetadata(
                defaultValue: null,
                propertyChangedCallback: OnPlaceholderChanged)
            );

    private static void OnPlaceholderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBoxControl)
        {
            if (!textBoxControl.IsLoaded)
            {
                // Ensure that the events are not added multiple times
                textBoxControl.Loaded -= TextBoxControl_Loaded;
                textBoxControl.Loaded += TextBoxControl_Loaded;
            }

            textBoxControl.TextChanged -= TextBoxControl_TextChanged;
            textBoxControl.TextChanged += TextBoxControl_TextChanged;

            // If the adorner exists, invalidate it to draw the current text
            if (GetOrCreateAdorner(textBoxControl, out PlaceholderAdorner adorner))
                adorner.InvalidateVisual();
        }
    }

    private static void TextBoxControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox textBoxControl)
        {
            textBoxControl.Loaded -= TextBoxControl_Loaded;
            GetOrCreateAdorner(textBoxControl, out _);
        }
    }

    private static void TextBoxControl_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (sender is TextBox textBoxControl
            && GetOrCreateAdorner(textBoxControl, out PlaceholderAdorner adorner))
        {
            // Control has text. Hide the adorner.
            if (textBoxControl.Text.Length > 0)
                adorner.Visibility = Visibility.Hidden;

            // Control has no text. Show the adorner.
            else
                adorner.Visibility = Visibility.Visible;
        }
    }

    private static bool GetOrCreateAdorner(TextBox textBoxControl, out PlaceholderAdorner adorner)
    {
        // Get the adorner layer
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(textBoxControl);

        // If null, it doesn't exist or the control's template isn't loaded
        if (layer == null)
        {
            adorner = null;
            return false;
        }

        // Layer exists, try to find the adorner
        adorner = layer.GetAdorners(textBoxControl)?.OfType<PlaceholderAdorner>().FirstOrDefault();

        // Adorner never added to control, so add it
        if (adorner == null)
        {
            adorner = new PlaceholderAdorner(textBoxControl);
            layer.Add(adorner);
        }

        return true;
    }

    public class PlaceholderAdorner : Adorner
    {
        public PlaceholderAdorner(TextBox textBox) : base(textBox) { }

        protected override void OnRender(DrawingContext drawingContext)
        {
            TextBox textBoxControl = (TextBox)AdornedElement;

            string placeholderValue = TextBoxHelper.GetPlaceholder(textBoxControl);

            if (string.IsNullOrEmpty(placeholderValue))
                return;

            // Create the formatted text object
            FormattedText text = new FormattedText(
                                        placeholderValue,
                                        System.Globalization.CultureInfo.CurrentCulture,
                                        textBoxControl.FlowDirection,
                                        new Typeface(textBoxControl.FontFamily,
                                                     textBoxControl.FontStyle,
                                                     textBoxControl.FontWeight,
                                                     textBoxControl.FontStretch),
                                        textBoxControl.FontSize,
                                        SystemColors.InactiveCaptionBrush,
                                        VisualTreeHelper.GetDpi(textBoxControl).PixelsPerDip);

            text.MaxTextWidth = System.Math.Max(textBoxControl.ActualWidth - textBoxControl.Padding.Left - textBoxControl.Padding.Right, 10);
            text.MaxTextHeight = System.Math.Max(textBoxControl.ActualHeight, 10);

            // Render based on padding of the control, to try and match where the textbox places text
            Point renderingOffset = new Point(textBoxControl.Padding.Left, textBoxControl.Padding.Top);

            // Template contains the content part; adjust sizes to try and align the text
            if (textBoxControl.Template.FindName("PART_ContentHost", textBoxControl) is FrameworkElement part)
            {
                Point partPosition = part.TransformToAncestor(textBoxControl).Transform(new Point(0, 0));
                renderingOffset.X += partPosition.X;
                renderingOffset.Y += partPosition.Y;

                text.MaxTextWidth = System.Math.Max(part.ActualWidth - renderingOffset.X, 10);
                text.MaxTextHeight = System.Math.Max(part.ActualHeight, 10);
            }

            // Draw the text
            drawingContext.DrawText(text, renderingOffset);
        }
    }
}
Public Class TextBoxHelper

    Public Shared Function GetPlaceholder(obj As DependencyObject) As String
        Return obj.GetValue(PlaceholderProperty)
    End Function

    Public Shared Sub SetPlaceholder(obj As DependencyObject, value As String)
        obj.SetValue(PlaceholderProperty, value)
    End Sub

    Public Shared ReadOnly PlaceholderProperty As DependencyProperty =
        DependencyProperty.RegisterAttached(
            "Placeholder",
            GetType(String),
            GetType(TextBoxHelper),
            New FrameworkPropertyMetadata(
                defaultValue:=Nothing,
                propertyChangedCallback:=AddressOf OnPlaceholderChanged)
            )

    Private Shared Sub OnPlaceholderChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim textBoxControl = TryCast(d, TextBox)

        If textBoxControl IsNot Nothing Then

            If Not textBoxControl.IsLoaded Then

                'Ensure that the events are not added multiple times
                RemoveHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded
                AddHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded

            End If

            RemoveHandler textBoxControl.TextChanged, AddressOf TextBoxControl_TextChanged
            AddHandler textBoxControl.TextChanged, AddressOf TextBoxControl_TextChanged

            'If the adorner exists, invalidate it to draw the current text
            Dim adorner As PlaceholderAdorner = Nothing
            If GetOrCreateAdorner(textBoxControl, adorner) Then
                adorner.InvalidateVisual()
            End If

        End If

    End Sub

    Private Shared Sub TextBoxControl_Loaded(sender As Object, e As RoutedEventArgs)
        Dim textBoxControl As TextBox = TryCast(sender, TextBox)

        If textBoxControl IsNot Nothing Then
            RemoveHandler textBoxControl.Loaded, AddressOf TextBoxControl_Loaded
            GetOrCreateAdorner(textBoxControl, Nothing)
        End If
    End Sub

    Private Shared Sub TextBoxControl_TextChanged(sender As Object, e As TextChangedEventArgs)
        Dim textBoxControl As TextBox = TryCast(sender, TextBox)
        Dim adorner As PlaceholderAdorner = Nothing

        If textBoxControl IsNot Nothing AndAlso GetOrCreateAdorner(textBoxControl, adorner) Then

            If textBoxControl.Text.Length > 0 Then
                'Control has text. Hide the adorner.
                adorner.Visibility = Visibility.Hidden
            Else
                'Control has no text. Show the adorner.
                adorner.Visibility = Visibility.Visible
            End If

        End If
    End Sub


    Private Shared Function GetOrCreateAdorner(textBoxControl As TextBox, ByRef adorner As PlaceholderAdorner) As Boolean

        'Get the adorner layer
        Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(textBoxControl)

        'If nothing, it doesn't exist or the control's template isn't loaded
        If layer Is Nothing Then
            adorner = Nothing
            Return False
        End If

        'Layer exists, try to find the adorner
        adorner = layer.GetAdorners(textBoxControl)?.OfType(Of PlaceholderAdorner)().FirstOrDefault()

        'Adorner never added to control, so add it
        If adorner Is Nothing Then
            adorner = New PlaceholderAdorner(textBoxControl)
            layer.Add(adorner)
        End If

        Return True

    End Function

    Public Class PlaceholderAdorner
        Inherits Adorner

        Public Sub New(adornedElement As UIElement)
            MyBase.New(adornedElement)
        End Sub

        Protected Overrides Sub OnRender(drawingContext As DrawingContext)
            Dim textBoxControl As TextBox = DirectCast(AdornedElement, TextBox)

            Dim placeholderValue As String = TextBoxHelper.GetPlaceholder(textBoxControl)

            If String.IsNullOrEmpty(placeholderValue) Then
                Return
            End If

            'Create the formatted text object
            Dim text As New FormattedText(
                placeholderValue,
                System.Globalization.CultureInfo.CurrentCulture,
                textBoxControl.FlowDirection,
                New Typeface(textBoxControl.FontFamily,
                             textBoxControl.FontStyle,
                             textBoxControl.FontWeight,
                             textBoxControl.FontStretch),
                textBoxControl.FontSize,
                SystemColors.InactiveCaptionBrush,
                VisualTreeHelper.GetDpi(textBoxControl).PixelsPerDip)

            text.MaxTextWidth = Math.Max(textBoxControl.ActualWidth - textBoxControl.Padding.Left - textBoxControl.Padding.Right, 10)
            text.MaxTextHeight = Math.Max(textBoxControl.ActualHeight, 10)

            'Render based on padding of the control, to try and match where the textbox places text
            Dim renderingOffset As New Point(textBoxControl.Padding.Left, textBoxControl.Padding.Top)

            'Template contains the content part; adjust sizes to try and align the text
            Dim part As FrameworkElement = TryCast(textBoxControl.Template.FindName("PART_ContentHost", textBoxControl), FrameworkElement)

            If part IsNot Nothing Then
                Dim partPosition As Point = part.TransformToAncestor(textBoxControl).Transform(New Point(0, 0))
                renderingOffset.X += partPosition.X
                renderingOffset.Y += partPosition.Y

                text.MaxTextWidth = Math.Max(part.ActualWidth - renderingOffset.X, 10)
                text.MaxTextHeight = Math.Max(part.ActualHeight, 10)
            End If

            ' Draw the text
            drawingContext.DrawText(text, renderingOffset)
        End Sub

    End Class
End Class

Viz také