向 TextBox 添加占位符

以下示例演示如何在 TextBox 为空时在 TextBox 中显示占位符文本。 当 TextBox 包含文本时,占位符文本将隐藏。 占位符文本可帮助用户了解 TextBox 所需的输入类型。

一个示例应用,其中包含两个 TextBox 控件,其中包含占位符。第一个文本框提供一个名称示例,第二个是电子邮件的示例。

本文介绍如何执行以下操作:

  • 创建附加属性以设置占位符文本。
  • 创建装饰器以显示占位符文本。
  • 将附加属性添加到 TextBox 控件。

创建附加属性

使用附加属性,可以将值追加到控件。 可以在 WPF 中大量使用此功能,例如在控件上设置 Grid.RowPanel.ZIndex 属性时。 有关详细信息,请参阅 附加属性概述。 此示例使用附加属性将占位符文本添加到 TextBox

  1. 向名为 TextBoxHelper 的项目添加新类并打开它。

  2. 添加以下命名空间:

    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. 创建名为 Placeholder的新依赖属性。

    此依赖属性使用属性更改的回调委托。

    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. 创建 OnPlaceholderChanged 方法以将附加属性与 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
    

    附加属性值更改时调用此方法有两种方法:

    • 首次将附加属性添加到 TextBox时,将调用此方法。 该操作为附加属性与控件事件的集成提供了机会。
    • 每当此属性发生更改时,装饰器都可以失效以刷新视觉占位符文本。

    下一部分将创建 GetOrCreateAdorner 方法。

  5. 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
    

    处理 Loaded 事件,以便在应用控件的模板后创建点缀对象。 引发事件并创建装饰器后,处理程序将删除自身。

    处理 TextChanged 事件以确保装饰器被隐藏或显示,具体取决于 Text 是否设置为值。

创建装饰器

Adorner 是与控件绑定并在 AdornerLayer 中呈现的视觉元素。 有关详细信息,请参阅装饰器概述

  1. 打开 TextBoxHelper 类。

  2. 添加以下代码以创建 GetOrCreateAdorner 方法。

    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
    

    此方法提供了一种安全的方法来添加或检索 Adorner。 装饰器需要额外的安全性,因为它们被添加到控件的 AdornerLayer,这可能不存在。 将 XAML 附加属性应用于控件时,控件的模板尚未应用于创建可视化树,因此装饰器层不存在。 加载控件后,必须检索装饰器层。 如果省略装饰器层的模板应用于控件,则装饰器层也可能缺失。

  3. 将名为 PlaceholderAdorner 的子类添加到 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 类。 此特定装饰器将替代 OnRender(DrawingContext) 方法来绘制占位符文本。 让我们细分代码:

    • 首先,通过调用 TextBoxHelper.GetPlaceholder(textBoxControl)检查占位符文本是否存在。
    • 创建 FormattedText 对象。 此对象包含有关在视觉对象上绘制文本的所有信息。
    • FormattedText.MaxTextWidthFormattedText.MaxTextHeight 属性都设置为控件的区域。 它们还设置为最小值 10,以确保 FormattedText 对象有效。
    • renderingOffset 存储所绘制文本的位置。
    • 使用 PART_ContentHost 如果控件的模板声明它。 此部分表示在控件模板上绘制文本的位置。 如果找到该部件,请修改 renderingOffset 以考虑其位置。
    • 通过调用 DrawText(FormattedText, Point) 并传递 FormattedText 对象和文本的位置来绘制文本。

应用附加属性

定义附加属性后,需要将其命名空间导入 XAML,然后在 TextBox 控件上引用。 以下代码将 .NET 命名空间 DotnetDocsSample 映射到 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>

附加属性通过使用语法 TextBox 添加到 xmlNamespace:Class.Property

完整示例

以下代码是完整的 TextBoxHelper 类。

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

另请参阅