ลายน้ำ / คำใบ้ข้อความ / กล่องข้อความตัวยึด


264

ฉันจะใส่ข้อความลงในกล่องข้อความซึ่งจะถูกลบโดยอัตโนมัติเมื่อผู้ใช้พิมพ์บางสิ่งในนั้นได้อย่างไร


48
สิ่งนี้เรียกว่า 'ตัวยึดตำแหน่ง' ใน HTML ฉันพูดถึงสิ่งนี้เพื่อช่วยให้คน google หน้านี้
Scott Stafford

3
หากคุณกำลังเขียนแอพ UWP บน Windows 10 สิ่งนี้จะง่ายกว่ามาก <TextBox PlaceholderText = "ค้นหา" /> ข้อมูลเพิ่มเติม: msdn.microsoft.com/en-us/library/windows/apps/ ......
Bleak Morn

คำตอบ:


57

นี่คือตัวอย่างที่สาธิตวิธีสร้างกล่องข้อความลายน้ำใน WPF:

<Window x:Class="WaterMarkTextBoxDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WaterMarkTextBoxDemo"
    Height="200" Width="400">

    <Window.Resources>

        <SolidColorBrush x:Key="brushWatermarkBackground" Color="White" />
        <SolidColorBrush x:Key="brushWatermarkForeground" Color="LightSteelBlue" />
        <SolidColorBrush x:Key="brushWatermarkBorder" Color="Indigo" />

        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <local:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />

        <Style x:Key="EntryFieldStyle" TargetType="Grid" >
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="20,0" />
        </Style>

    </Window.Resources>


    <Grid Background="LightBlue">

        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
            <TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
                       Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
            <TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
        </Grid>

        <Grid Grid.Row="1" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
            <TextBlock Margin="5,2" Text="This dissappears as the control gets focus..." Foreground="{StaticResource brushWatermarkForeground}" >
                <TextBlock.Visibility>
                    <MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
                        <Binding ElementName="txtUserEntry2" Path="Text.IsEmpty" />
                        <Binding ElementName="txtUserEntry2" Path="IsFocused" />
                    </MultiBinding>
                </TextBlock.Visibility>
            </TextBlock>
            <TextBox Name="txtUserEntry2" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
        </Grid>

    </Grid>

</Window>

TextInputToVisibilityConverter ถูกกำหนดเป็น:

using System;
using System.Windows.Data;
using System.Windows;

namespace WaterMarkTextBoxDemo
{
    public class TextInputToVisibilityConverter : IMultiValueConverter
    {
        public object Convert( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            // Always test MultiValueConverter inputs for non-null
            // (to avoid crash bugs for views in the designer)
            if (values[0] is bool && values[1] is bool)
            {
                bool hasText = !(bool)values[0];
                bool hasFocus = (bool)values[1];

                if (hasFocus || hasText)
                    return Visibility.Collapsed;
            }

            return Visibility.Visible;
        }


        public object[] ConvertBack( object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture )
        {
            throw new NotImplementedException();
        }
    }
}

หมายเหตุ:นี่ไม่ใช่รหัสของฉัน ฉันพบที่นี่แต่ฉันคิดว่านี่เป็นวิธีที่ดีที่สุด


5
ฉันจะนำไปใช้ในกล่องรหัสผ่านได้อย่างไร?
Sauron

91
วิธีที่ดีที่สุด ไม่แน่นอน! คุณต้องการพิมพ์รหัสหลายบรรทัดทุกครั้งที่คุณต้องการลายน้ำหรือไม่? วิธีการแก้ปัญหาที่มีคุณสมบัติที่แนบมาง่ายมากที่จะนำมาใช้ ...
โทมัส Levesque

5
พิจารณาสร้าง UserControl
CSharper

6
แม้ว่าฉันจะซาบซึ้งในความพยายามของคุณที่จะช่วยเหลือชุมชน แต่ฉันก็ต้องบอกว่านี่คือสิ่งที่ไกลเกินกว่าจะเป็นแนวทางที่ดี
r41n

2
รหัสนี้ถูกสร้างขึ้นโดยแอนดี้แอลคุณสามารถค้นหาได้ในcodeproject
aloisdg กำลังย้ายไปยัง codidact.com

440

คุณสามารถสร้างลายน้ำที่สามารถเพิ่มไปยังใด ๆ ที่TextBoxมีคุณสมบัติที่แนบมา นี่คือแหล่งที่มาสำหรับคุณสมบัติที่แนบมา:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;

/// <summary>
/// Class that provides the Watermark attached property
/// </summary>
public static class WatermarkService
{
    /// <summary>
    /// Watermark Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
       "Watermark",
       typeof(object),
       typeof(WatermarkService),
       new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));

    #region Private Fields

    /// <summary>
    /// Dictionary of ItemsControls
    /// </summary>
    private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();

    #endregion

    /// <summary>
    /// Gets the Watermark property.  This dependency property indicates the watermark for the control.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
    /// <returns>The value of the Watermark property</returns>
    public static object GetWatermark(DependencyObject d)
    {
        return (object)d.GetValue(WatermarkProperty);
    }

    /// <summary>
    /// Sets the Watermark property.  This dependency property indicates the watermark for the control.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
    /// <param name="value">value of the property</param>
    public static void SetWatermark(DependencyObject d, object value)
    {
        d.SetValue(WatermarkProperty, value);
    }

    /// <summary>
    /// Handles changes to the Watermark property.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
    /// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
    private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Control control = (Control)d;
        control.Loaded += Control_Loaded;

        if (d is ComboBox)
        {
            control.GotKeyboardFocus += Control_GotKeyboardFocus;
            control.LostKeyboardFocus += Control_Loaded;
        }
        else if (d is TextBox)
        {
            control.GotKeyboardFocus += Control_GotKeyboardFocus;
            control.LostKeyboardFocus += Control_Loaded;
            ((TextBox)control).TextChanged += Control_GotKeyboardFocus;
        }

        if (d is ItemsControl && !(d is ComboBox))
        {
            ItemsControl i = (ItemsControl)d;

            // for Items property  
            i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
            itemsControls.Add(i.ItemContainerGenerator, i);

            // for ItemsSource property  
            DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
            prop.AddValueChanged(i, ItemsSourceChanged);
        }
    }

    #region Event Handlers

    /// <summary>
    /// Handle the GotFocus event on the control
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
    private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
    {
        Control c = (Control)sender;
        if (ShouldShowWatermark(c))
        {
            ShowWatermark(c);
        }
        else
        {
            RemoveWatermark(c);
        }
    }

    /// <summary>
    /// Handle the Loaded and LostFocus event on the control
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
    private static void Control_Loaded(object sender, RoutedEventArgs e)
    {
        Control control = (Control)sender;
        if (ShouldShowWatermark(control))
        {
            ShowWatermark(control);
        }
    }

    /// <summary>
    /// Event handler for the items source changed event
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
    private static void ItemsSourceChanged(object sender, EventArgs e)
    {
        ItemsControl c = (ItemsControl)sender;
        if (c.ItemsSource != null)
        {
            if (ShouldShowWatermark(c))
            {
                ShowWatermark(c);
            }
            else
            {
                RemoveWatermark(c);
            }
        }
        else
        {
            ShowWatermark(c);
        }
    }

    /// <summary>
    /// Event handler for the items changed event
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
    private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
    {
        ItemsControl control;
        if (itemsControls.TryGetValue(sender, out control))
        {
            if (ShouldShowWatermark(control))
            {
                ShowWatermark(control);
            }
            else
            {
                RemoveWatermark(control);
            }
        }
    }

    #endregion

    #region Helper Methods

    /// <summary>
    /// Remove the watermark from the specified element
    /// </summary>
    /// <param name="control">Element to remove the watermark from</param>
    private static void RemoveWatermark(UIElement control)
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

        // layer could be null if control is no longer in the visual tree
        if (layer != null)
        {
            Adorner[] adorners = layer.GetAdorners(control);
            if (adorners == null)
            {
                return;
            }

            foreach (Adorner adorner in adorners)
            {
                if (adorner is WatermarkAdorner)
                {
                    adorner.Visibility = Visibility.Hidden;
                    layer.Remove(adorner);
                }
            }
        }
    }

    /// <summary>
    /// Show the watermark on the specified control
    /// </summary>
    /// <param name="control">Control to show the watermark on</param>
    private static void ShowWatermark(Control control)
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

        // layer could be null if control is no longer in the visual tree
        if (layer != null)
        {
            layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
        }
    }

    /// <summary>
    /// Indicates whether or not the watermark should be shown on the specified control
    /// </summary>
    /// <param name="c"><see cref="Control"/> to test</param>
    /// <returns>true if the watermark should be shown; false otherwise</returns>
    private static bool ShouldShowWatermark(Control c)
    {
        if (c is ComboBox)
        {
            return (c as ComboBox).Text == string.Empty;
        }
        else if (c is TextBoxBase)
        {
            return (c as TextBox).Text == string.Empty;
        }
        else if (c is ItemsControl)
        {
            return (c as ItemsControl).Items.Count == 0;
        }
        else
        {
            return false;
        }
    }

    #endregion
}

คุณสมบัติที่แนบมาใช้คลาสที่เรียกว่าWatermarkAdornerนี่คือแหล่งที่มา:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;

/// <summary>
/// Adorner for the watermark
/// </summary>
internal class WatermarkAdorner : Adorner
{
    #region Private Fields

    /// <summary>
    /// <see cref="ContentPresenter"/> that holds the watermark
    /// </summary>
    private readonly ContentPresenter contentPresenter;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="WatermarkAdorner"/> class
    /// </summary>
    /// <param name="adornedElement"><see cref="UIElement"/> to be adorned</param>
    /// <param name="watermark">The watermark</param>
    public WatermarkAdorner(UIElement adornedElement, object watermark) :
       base(adornedElement)
    {
        this.IsHitTestVisible = false;

        this.contentPresenter = new ContentPresenter();
        this.contentPresenter.Content = watermark;
        this.contentPresenter.Opacity = 0.5;
        this.contentPresenter.Margin = new Thickness(Control.Margin.Left + Control.Padding.Left, Control.Margin.Top + Control.Padding.Top, 0, 0);

        if (this.Control is ItemsControl && !(this.Control is ComboBox))
        {
            this.contentPresenter.VerticalAlignment = VerticalAlignment.Center;
            this.contentPresenter.HorizontalAlignment = HorizontalAlignment.Center;
        }

        // Hide the control adorner when the adorned element is hidden
        Binding binding = new Binding("IsVisible");
        binding.Source = adornedElement;
        binding.Converter = new BooleanToVisibilityConverter();
        this.SetBinding(VisibilityProperty, binding);
    }

    #endregion

    #region Protected Properties

    /// <summary>
    /// Gets the number of children for the <see cref="ContainerVisual"/>.
    /// </summary>
    protected override int VisualChildrenCount
    {
        get { return 1; }
    }

    #endregion

    #region Private Properties

    /// <summary>
    /// Gets the control that is being adorned
    /// </summary>
    private Control Control
    {
        get { return (Control)this.AdornedElement; }
    }

    #endregion

    #region Protected Overrides

    /// <summary>
    /// Returns a specified child <see cref="Visual"/> for the parent <see cref="ContainerVisual"/>.
    /// </summary>
    /// <param name="index">A 32-bit signed integer that represents the index value of the child <see cref="Visual"/>. The value of index must be between 0 and <see cref="VisualChildrenCount"/> - 1.</param>
    /// <returns>The child <see cref="Visual"/>.</returns>
    protected override Visual GetVisualChild(int index)
    {
        return this.contentPresenter;
    }

    /// <summary>
    /// Implements any custom measuring behavior for the adorner.
    /// </summary>
    /// <param name="constraint">A size to constrain the adorner to.</param>
    /// <returns>A <see cref="Size"/> object representing the amount of layout space needed by the adorner.</returns>
    protected override Size MeasureOverride(Size constraint)
    {
        // Here's the secret to getting the adorner to cover the whole control
        this.contentPresenter.Measure(Control.RenderSize);
        return Control.RenderSize;
    }

    /// <summary>
    /// When overridden in a derived class, positions child elements and determines a size for a <see cref="FrameworkElement"/> derived class. 
    /// </summary>
    /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
    /// <returns>The actual size used.</returns>
    protected override Size ArrangeOverride(Size finalSize)
    {
        this.contentPresenter.Arrange(new Rect(finalSize));
        return finalSize;
    }

    #endregion
}

ตอนนี้คุณสามารถใส่ลายน้ำบนกล่องข้อความใด ๆ เช่นนี้:

<AdornerDecorator>
   <TextBox x:Name="SearchTextBox">
      <controls:WatermarkService.Watermark>
         <TextBlock>Type here to search text</TextBlock>
      </controls:WatermarkService.Watermark>
   </TextBox>
</AdornerDecorator>

ลายน้ำสามารถเป็นอะไรก็ได้ที่คุณต้องการ (ข้อความรูปภาพ ... ) นอกเหนือจากการทำงานกับ TextBoxes ลายน้ำนี้ยังสามารถใช้ได้กับ ComboBoxes และ ItemControls

รหัสนี้ดัดแปลงจากโพสต์บล็อกนี้


11
ผมเคยแก้ไขได้ปรับเปลี่ยนการกำหนดตัวสร้างหลักประกัน WatermarkAdorner เช่น Margin = ความหนาใหม่ (Control.Padding.Left, Control.Padding.Top + 1, Control.Padding.Right, Control.Padding.Bottom)
JoanComasFdz

3
@JohnMyczek เพื่อแปลลายน้ำ: ฉันจะผูก TextBox.Text อย่างไรในการประกาศ xaml ลายน้ำให้กับคุณสมบัติจาก ViewModel?
JoanComasFdz

7
@Matze @JoanComasFdz นี่คือวิธีที่ฉันสามารถผูกTextBlock.Textคุณสมบัติกับรูปแบบการดูของฉัน (ใส่สิ่งนี้ลงในคอนWatermarkAdornerสตรัคเตอร์): FrameworkElement feWatermark = watermark as FrameworkElement; if(feWatermark != null && feWatermark.DataContext == null) { feWatermark.DataContext = this.Control.DataContext; }
Sean Hall

9
ลิงค์หน่วยความจำที่เป็นไปได้ที่นี่ คุณกำลังเพิ่มตัวควบคุมที่ใส่ลายน้ำให้กับพจนานุกรมแบบคงที่ภายใน แต่ไม่ต้องลบออก การทำเช่นนี้อาจทำให้มุมมองของคุณไม่ถูกรวบรวมเมื่อคุณทำเสร็จแล้ว ฉันจะพิจารณาใช้การอ้างอิงที่อ่อนแอที่นี่
Jared G

3
นอกจากพจนานุกรมแบบคงที่ของ itemcontrols แล้วรหัส PropertyDescriptor ยังมีหน่วยความจำรั่ว คุณต้องโทร RemoveValueChanged () ดังนั้นระวังเมื่อคุณใช้รหัสนี้
muku

284

เพียงใช้ XAML ไม่มีส่วนขยายไม่มีตัวแปลง:

<Grid>
    <TextBox  Width="250"  VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="SearchTermTextBox" Margin="5"/>
    <TextBlock IsHitTestVisible="False" Text="Enter Search Term Here" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" Foreground="DarkGray">
        <TextBlock.Style>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="Visibility" Value="Collapsed"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Grid>

3
เรียบง่ายมาก IMo ที่ดีที่สุดยัง ฉันไม่รู้ว่าทำไมคุณจะใช้อันอื่นเมื่อคุณมีสคริปต์ xaml ทั้ง 10 บรรทัดและนั่นเป็นเหตุผล ขอบคุณ
dj.lnxss

4
คุณอาจต้องการที่จะเพิ่มไปPadding="6,3,0,0" TextBlock
aloisdg กำลังย้ายไปยัง codidact.com

1
ดีมาก แต่มันไม่ทำงานบน Windows Phone Silverlight :-(
Andrea Antonangeli

14
หนึ่งจะทำให้เป็นแม่แบบการควบคุมที่นำมาใช้ใหม่ได้อย่างไร
Richard

2
@cyrianox นี่เป็นเพราะคุณสมบัติรหัสผ่านบน PasswordBox ไม่สามารถผูกมัดได้เนื่องจากเหตุผลด้านความปลอดภัย คุณสามารถทำให้มันไม่สามารถใช้งานได้โดยใช้ตัวอย่างนี้ที่นี่: wpftutorial.net/PasswordBox.htmlแต่มันอาจเร็วและง่ายกว่าเพียงแค่ใช้เหตุการณ์ PasswordChanged และรหัสด้านหลังเพื่อตั้งค่าการมองเห็นในกรณีนี้
apc

54

ฉันไม่อยากจะเชื่อเลยว่าไม่มีใครโพสต์Toolkit WPF แบบขยายที่ชัดเจน- WatermarkTextBoxจาก Xceed มันทำงานได้ค่อนข้างดีและเป็นโอเพ่นซอร์สในกรณีที่คุณต้องการปรับแต่ง


5
บนเครื่อง win8 ของฉันชุดควบคุม WPF Toolkit ทั้งหมดมี windows 7 style (มุมโค้งมน ฯลฯ ) และชุดควบคุมชุดเครื่องมือ WPF นั้นดูสมบูรณ์แบบเมื่อผสมกับตัวควบคุมมาตรฐาน
Gman

1
วิธีการ "คุณสมบัติที่แนบมา" โดย John Myczek มีข้อผิดพลาดเพราะถ้ากล่องข้อความถูกครอบคลุมโดยองค์ประกอบอื่นลายน้ำจะตกผ่านและยังคงมองเห็นได้ วิธีแก้ปัญหานี้ไม่มีปัญหานั้น (หวังว่าฉันจะสังเกตเห็นสิ่งนี้ก่อนหน้านี้เนื่องจากฉันใช้ชุดเครื่องมืออยู่แล้ว) สมควรได้รับการโหวตมากขึ้น
Dax Fohl

โซลูชันของ John Myczek ยังมีหน่วยความจำรั่วที่ชัดเจนซึ่ง WatermarkService จะทำการอ้างอิงในพจนานุกรมแบบคงที่กับ ItemsControl ใด ๆ ที่มีลายน้ำแนบอยู่ อาจแก้ไขได้แน่นอน แต่ฉันจะให้ Extended WPF Toolkit เวอร์ชันลอง
mbargiel

34

มีบทความเกี่ยวกับ CodeProjectเกี่ยวกับวิธีการทำใน "3 บรรทัดของ XAML"

<Grid Background="{StaticResource brushWatermarkBackground}">
  <TextBlock Margin="5,2" Text="Type something..."
             Foreground="{StaticResource brushForeground}"
             Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty,
                          Converter={StaticResource BooleanToVisibilityConverter}}" />
  <TextBox Name="txtUserEntry" Background="Transparent"
           BorderBrush="{StaticResource brushBorder}" />
</Grid>

ตกลงแล้วมันอาจจะไม่ใช่รูปแบบ XAML 3 บรรทัด แต่ก็เป็นเช่นนั้นสวยเรียบง่าย

สิ่งหนึ่งที่ควรทราบก็คือมันใช้วิธีการขยายที่ไม่ได้มาตรฐานในคุณสมบัติข้อความเรียกว่า "IsEmpty" คุณต้องใช้สิ่งนี้ด้วยตัวเอง แต่บทความดูเหมือนจะไม่พูดถึง


9
IsHitTestVisible="False"ช่องควรจะมี นอกจากนี้ควรมาหลังกล่องข้อความมิฉะนั้นอาจไม่สามารถมองเห็นได้ถ้ากล่องข้อความมีพื้นหลัง
ตอบ

บทความใน CodeProject นั้นแย่มาก
Xam


19

ฉันเห็นโซลูชันของ John Myczekและความคิดเห็นเกี่ยวกับความเข้ากันได้ComboBoxและPasswordBoxดังนั้นฉันจึงปรับปรุงโซลูชันของ John Myczek และนี่คือ:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;

/// <summary>
/// Class that provides the Watermark attached property
/// </summary>
public static class WatermarkService
{
    /// <summary>
    /// Watermark Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
       "Watermark",
       typeof(object),
       typeof(WatermarkService),
       new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));

    #region Private Fields

    /// <summary>
    /// Dictionary of ItemsControls
    /// </summary>
    private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();

    #endregion

    /// <summary>
    /// Gets the Watermark property.  This dependency property indicates the watermark for the control.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
    /// <returns>The value of the Watermark property</returns>
    public static object GetWatermark(DependencyObject d)
    {
        return (object)d.GetValue(WatermarkProperty);
    }

    /// <summary>
    /// Sets the Watermark property.  This dependency property indicates the watermark for the control.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
    /// <param name="value">value of the property</param>
    public static void SetWatermark(DependencyObject d, object value)
    {
        d.SetValue(WatermarkProperty, value);
    }

    /// <summary>
    /// Handles changes to the Watermark property.
    /// </summary>
    /// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
    /// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
    private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Control control = (Control)d;
        control.Loaded += Control_Loaded;

        if (d is TextBox || d is PasswordBox)
        {
            control.GotKeyboardFocus += Control_GotKeyboardFocus;
            control.LostKeyboardFocus += Control_Loaded;
        }
        else if (d is ComboBox)
        {
            control.GotKeyboardFocus += Control_GotKeyboardFocus;
            control.LostKeyboardFocus += Control_Loaded;
            (d as ComboBox).SelectionChanged += new SelectionChangedEventHandler(SelectionChanged);
        }
        else if (d is ItemsControl)
        {
            ItemsControl i = (ItemsControl)d;

            // for Items property  
            i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
            itemsControls.Add(i.ItemContainerGenerator, i);

            // for ItemsSource property  
            DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
            prop.AddValueChanged(i, ItemsSourceChanged);
        }
    }

    /// <summary>
    /// Event handler for the selection changed event
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
    private static void SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Control control = (Control)sender;
        if (ShouldShowWatermark(control))
        {
            ShowWatermark(control);
        }
        else
        {
            RemoveWatermark(control);
        }
    }

    #region Event Handlers

    /// <summary>
    /// Handle the GotFocus event on the control
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
    private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
    {
        Control c = (Control)sender;
        if (ShouldShowWatermark(c))
        {
            RemoveWatermark(c);
        }
    }

    /// <summary>
    /// Handle the Loaded and LostFocus event on the control
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
    private static void Control_Loaded(object sender, RoutedEventArgs e)
    {
        Control control = (Control)sender;
        if (ShouldShowWatermark(control))
        {
            ShowWatermark(control);
        }
    }

    /// <summary>
    /// Event handler for the items source changed event
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
    private static void ItemsSourceChanged(object sender, EventArgs e)
    {
        ItemsControl c = (ItemsControl)sender;
        if (c.ItemsSource != null)
        {
            if (ShouldShowWatermark(c))
            {
                ShowWatermark(c);
            }
            else
            {
                RemoveWatermark(c);
            }
        }
        else
        {
            ShowWatermark(c);
        }
    }

    /// <summary>
    /// Event handler for the items changed event
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
    private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
    {
        ItemsControl control;
        if (itemsControls.TryGetValue(sender, out control))
        {
            if (ShouldShowWatermark(control))
            {
                ShowWatermark(control);
            }
            else
            {
                RemoveWatermark(control);
            }
        }
    }

    #endregion

    #region Helper Methods

    /// <summary>
    /// Remove the watermark from the specified element
    /// </summary>
    /// <param name="control">Element to remove the watermark from</param>
    private static void RemoveWatermark(UIElement control)
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

        // layer could be null if control is no longer in the visual tree
        if (layer != null)
        {
            Adorner[] adorners = layer.GetAdorners(control);
            if (adorners == null)
            {
                return;
            }

            foreach (Adorner adorner in adorners)
            {
                if (adorner is WatermarkAdorner)
                {
                    adorner.Visibility = Visibility.Hidden;
                    layer.Remove(adorner);
                }
            }
        }
    }

    /// <summary>
    /// Show the watermark on the specified control
    /// </summary>
    /// <param name="control">Control to show the watermark on</param>
    private static void ShowWatermark(Control control)
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);

        // layer could be null if control is no longer in the visual tree
        if (layer != null)
        {
            layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
        }
    }

    /// <summary>
    /// Indicates whether or not the watermark should be shown on the specified control
    /// </summary>
    /// <param name="c"><see cref="Control"/> to test</param>
    /// <returns>true if the watermark should be shown; false otherwise</returns>
    private static bool ShouldShowWatermark(Control c)
    {
        if (c is ComboBox)
        {
            return (c as ComboBox).SelectedItem == null;
            //return (c as ComboBox).Text == string.Empty;
        }
        else if (c is TextBoxBase)
        {
            return (c as TextBox).Text == string.Empty;
        }
        else if (c is PasswordBox)
        {
            return (c as PasswordBox).Password == string.Empty;
        }
        else if (c is ItemsControl)
        {
            return (c as ItemsControl).Items.Count == 0;
        }
        else
        {
            return false;
        }
    }

    #endregion
}

ตอนนี้ a ComboBoxสามารถเป็นได้EditableและPasswordBoxสามารถเพิ่มลายน้ำได้เช่นกัน อย่าลืมใช้ความคิดเห็นของ JoanComasFdz ด้านบนเพื่อแก้ปัญหาระยะขอบ

และแน่นอนเครดิตทั้งหมดไปที่ John Myczek


4
อันที่จริงแล้วเป็นเวอร์ชั่นปรับปรุงของรหัส @ john-myczek ที่สวยงามและได้รับการปรับสำหรับกล่องคำสั่งผสม ขอบคุณทั้งคู่!
saamorim

12

วิธีง่ายๆในการใช้สไตล์:

<TextBox>
    <TextBox.Style>
        <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <Style.Resources>
                <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                    <VisualBrush.Visual>
                        <Label Content="MM:SS:HH AM/PM" Foreground="LightGray" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="Text" Value="{x:Null}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter Property="Background" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

ทางออกที่ดี:

https://code.msdn.microsoft.com/windowsdesktop/In-place-hit-messages-for-18db3a6c


1
นี่คือทางออกที่ชอบของฉัน
HoKy22


9

ฉันสร้าง siple code-only implementation ซึ่งทำงานได้ดีสำหรับ WPF และ Silverlight เช่นกัน:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

public class TextBoxWatermarked : TextBox
{
    #region [ Dependency Properties ]

    public static DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark",
                                                                             typeof(string),
                                                                             typeof(TextBoxWatermarked),
                                                                             new PropertyMetadata(new PropertyChangedCallback(OnWatermarkChanged)));


    #endregion


    #region [ Fields ]

    private bool _isWatermarked;
    private Binding _textBinding;

    #endregion


    #region [ Properties ]

    protected new Brush Foreground
    {
        get { return base.Foreground; }
        set { base.Foreground = value; }
    }

    public string Watermark
    {
        get { return (string)GetValue(WatermarkProperty); }
        set { SetValue(WatermarkProperty, value); }
    }

    #endregion


    #region [ .ctor ]

    public TextBoxWatermarked()
    {
        Loaded += (s, ea) => ShowWatermark();
    }

    #endregion


    #region [ Event Handlers ]

    protected override void OnGotFocus(RoutedEventArgs e)
    {
        base.OnGotFocus(e);
        HideWatermark();
    }

    protected override void OnLostFocus(RoutedEventArgs e)
    {
        base.OnLostFocus(e);
        ShowWatermark();
    }

    private static void OnWatermarkChanged(DependencyObject sender, DependencyPropertyChangedEventArgs ea)
    {
        var tbw = sender as TextBoxWatermarked;
        if (tbw == null) return;
        tbw.ShowWatermark();
    }

    #endregion


    #region [ Methods ]

    private void ShowWatermark()
    {
        if (string.IsNullOrEmpty(base.Text))
        {
            _isWatermarked = true;
            base.Foreground = new SolidColorBrush(Colors.Gray);
            var bindingExpression = GetBindingExpression(TextProperty);
            _textBinding = bindingExpression == null ? null : bindingExpression.ParentBinding;
            if (bindingExpression != null)
                bindingExpression.UpdateSource();
            SetBinding(TextProperty, new Binding());
            base.Text = Watermark;
        }
    }

    private void HideWatermark()
    {
        if (_isWatermarked)
        {
            _isWatermarked = false;
            ClearValue(ForegroundProperty);
            base.Text = "";
            SetBinding(TextProperty, _textBinding ?? new Binding());
        }
    }

    #endregion
}

การใช้งาน:

<TextBoxWatermarked Watermark="Some text" />

ทางออกที่ดี เหตุใดจึงเงาคุณสมบัติของพื้นหน้า SetBinding (TextProperty, Binding ใหม่ ()) ส่ง InvalidOperationException: การรวมสองทางต้องใช้ Path หรือ XPath หรือไม่
ทิมเมอร์ฟี

ฉันซ่อนคุณสมบัติพื้นหน้าเพราะ TextBoxWatermarked ใช้เพื่อจุดประสงค์ของตัวเอง ฉันไม่รู้ว่าทำไม InvalidOperationException ถูกส่งออกไปบางทีถ้าคุณใช้ WPF (ฉันใช้กับ Silverlight) คุณต้องผ่านค่า null แทน Binding () ใหม่
Vitaliy Ulantikov

2
ในการใช้รหัสนี้ใน WPF ให้ใช้BindingOperations.ClearBinding(this, TextProperty)แทนSetBinding(TextProperty, new Binding())ทั้งสองแห่ง
เซบาสเตียน Krysmanski

1
นี่เปลี่ยนTextลายน้ำจริงๆ จะไม่ทำงานสำหรับฉัน
กุ้งมังกร

อาจเป็นประโยชน์ในการเพิ่มบรรทัดเนมสเปซให้กับสิ่งนี้หรือทำให้คุณสมบัติบางอย่างของคุณสมบัติเต็มไปด้วยนี้
Richard มิถุนายน

6

ฉันวิ่งเข้าไปในบิตของปัญหาเมื่อใช้ @ john-myczek ของรหัสที่มีกล่องข้อความที่ถูกผูกไว้ เนื่องจากกล่องข้อความไม่ยกเหตุการณ์การโฟกัสเมื่อมีการอัปเดตลายน้ำจะยังคงปรากฏอยู่ใต้ข้อความใหม่ ในการแก้ไขปัญหานี้ฉันเพิ่งเพิ่มตัวจัดการเหตุการณ์อื่น:

if (d is ComboBox || d is TextBox)
{
    control.GotKeyboardFocus += Control_GotKeyboardFocus;
    control.LostKeyboardFocus += Control_Loaded;

    if (d is TextBox)
        (d as TextBox).TextChanged += Control_TextChanged;
}


private static void Control_TextChanged(object sender, RoutedEventArgs e)
{
    var tb = (TextBox)sender;
    if (ShouldShowWatermark(tb))
    {
        ShowWatermark(tb);
    }
    else
    {
        RemoveWatermark(tb);
    }
}

1
หวังว่าฉันจะสังเกตเห็นคำตอบนี้ก่อนที่จะทำเอง
กุ้งมังกร

5

@ Veton - ฉันชอบความเรียบง่ายของโซลูชันของคุณ แต่ชื่อเสียงของฉันยังไม่สูงพอที่จะทำให้คุณสะดุด

@Tim Murphy - ข้อผิดพลาด "การผูกสองทางต้องใช้ Path หรือ XPath" เป็นวิธีแก้ไขที่ง่าย ... รหัสที่อัปเดตรวมถึงการปรับแต่งเล็กน้อยอื่น ๆ (ทดสอบ WPF เท่านั้น):

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

public class TextBoxWatermarked : TextBox
{
  public string Watermark
  {
    get { return (string)GetValue(WaterMarkProperty); }
    set { SetValue(WaterMarkProperty, value); }
  }
  public static readonly DependencyProperty WaterMarkProperty =
      DependencyProperty.Register("Watermark", typeof(string), typeof(TextBoxWatermarked), new PropertyMetadata(new PropertyChangedCallback(OnWatermarkChanged)));

  private bool _isWatermarked = false;
  private Binding _textBinding = null;

  public TextBoxWatermarked()
  {
    Loaded += (s, ea) => ShowWatermark();
  }

  protected override void OnGotFocus(RoutedEventArgs e)
  {
    base.OnGotFocus(e);
    HideWatermark();
  }

  protected override void OnLostFocus(RoutedEventArgs e)
  {
    base.OnLostFocus(e);
    ShowWatermark();
  }

  private static void OnWatermarkChanged(DependencyObject sender, DependencyPropertyChangedEventArgs ea)
  {
    var tbw = sender as TextBoxWatermarked;
    if (tbw == null || !tbw.IsLoaded) return; //needed to check IsLoaded so that we didn't dive into the ShowWatermark() routine before initial Bindings had been made
    tbw.ShowWatermark();
  }

  private void ShowWatermark()
  {
    if (String.IsNullOrEmpty(Text) && !String.IsNullOrEmpty(Watermark))
    {
      _isWatermarked = true;

      //save the existing binding so it can be restored
      _textBinding = BindingOperations.GetBinding(this, TextProperty);

      //blank out the existing binding so we can throw in our Watermark
      BindingOperations.ClearBinding(this, TextProperty);

      //set the signature watermark gray
      Foreground = new SolidColorBrush(Colors.Gray);

      //display our watermark text
      Text = Watermark;
    }
  }

  private void HideWatermark()
  {
    if (_isWatermarked)
    {
      _isWatermarked = false;
      ClearValue(ForegroundProperty);
      Text = "";
      if (_textBinding != null) SetBinding(TextProperty, _textBinding);
    }
  }

}

3

คุณสามารถใช้GetFocus()และLostFocus()กิจกรรมเพื่อทำสิ่งนี้

นี่คือตัวอย่าง:

    private void txtData1_GetFocus(object sender, RoutedEventArgs e)
    {
        if (txtData1.Text == "TextBox1abc")
        {
            txtData1.Text = string.Empty;
        }
    }

    private void txtData1_LostFocus(object sender, RoutedEventArgs e)
    {
        if (txtData1.Text == string.Empty)
        {
            txtData1.Text = "TextBox1abc";
        }
    }

2

วิธีที่ง่ายที่สุดในลายน้ำของกล่องข้อความ

 <Window.Resources>
    <Style x:Key="MyWaterMarkStyle" TargetType="{x:Type TextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Grid>
                        <Border Background="White" BorderBrush="#FF7D8683" BorderThickness="1"/>
                        <ScrollViewer x:Name="PART_ContentHost" Margin="5,0,0,0" VerticalAlignment="Center" />
                        <Label Margin="5,0,0,0" x:Name="WaterMarkLabel" Content="{TemplateBinding Tag}" VerticalAlignment="Center"
                           Visibility="Collapsed" Foreground="Gray" FontFamily="Arial"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="Text" Value=""/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Visibility" TargetName="WaterMarkLabel" Value="Visible"/>
                        </MultiTrigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Foreground" Value="DimGray"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

และเพิ่มสไตล์ข้อความ StaticResource ของกล่องข้อความ

  <TextBox
                Style="{StaticResource MyWaterMarkStyle}"
                Tag="Search Category"
                Grid.Row="0"
                Text="{Binding CategorySearch,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                TextSearch.Text="Search Category"
                >

1
<Window.Resources>

    <Style x:Key="TextBoxUserStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
      <Setter Property="Foreground" Value="Black"/>
      <Setter Property="HorizontalAlignment" Value="Center"/>
      <Setter Property="VerticalContentAlignment" Value="Center"/>
      <Setter Property="Width" Value="225"/>
      <Setter Property="Height" Value="25"/>
      <Setter Property="FontSize" Value="12"/>
      <Setter Property="Padding" Value="1"/>
      <Setter Property="Margin" Value="5"/>
      <Setter Property="AllowDrop" Value="true"/>
      <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type TextBox}">
            <Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
              <Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
                <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
              </Border>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

    <Style x:Key="PasswordBoxVistaStyle" BasedOn="{x:Null}" TargetType="{x:Type PasswordBox}">
      <Setter Property="Foreground" Value="Black"/>
      <Setter Property="HorizontalAlignment" Value="Center"/>
      <Setter Property="VerticalContentAlignment" Value="Center"/>
      <Setter Property="Width" Value="225"/>
      <Setter Property="Height" Value="25"/>
      <Setter Property="FontSize" Value="12"/>
      <Setter Property="Padding" Value="1"/>
      <Setter Property="Margin" Value="5"/>
      <Setter Property="AllowDrop" Value="true"/>
      <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type PasswordBox}">
            <Border x:Name="OuterBorder" BorderBrush="#5AFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
              <Border x:Name="InnerBorder" Background="#FFFFFFFF" BorderBrush="#33000000" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
                <Grid>
                  <Label x:Name="lblPwd" Content="Password" FontSize="11" VerticalAlignment="Center" Margin="2,0,0,0" FontFamily="Verdana" Foreground="#828385" Padding="0"/>
                  <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost"/>
                </Grid>
              </Border>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsFocused" Value="True">
                <Setter Property="Visibility" TargetName="lblPwd" Value="Hidden"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>


        <PasswordBox Style="{StaticResource PasswordBoxVistaStyle}" Margin="169,143,22,0" Name="txtPassword" FontSize="14" TabIndex="2" Height="31" VerticalAlignment="Top" />

สิ่งนี้สามารถช่วยตรวจสอบด้วยรหัสของคุณเมื่อนำไปใช้กับกล่องรหัสผ่านก็จะแสดงรหัสผ่านซึ่งจะหายไปเมื่อ usertypes


1

ที่นี่เป็นของฉัน: ไม่จำเป็นต้องดีที่สุด แต่มันง่ายที่จะแก้ไขรสนิยมของคุณ

<UserControl x:Class="WPFControls.ShadowedTextBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPFControls"
    Name="Root">
<UserControl.Resources>
    <local:ShadowConverter x:Key="ShadowConvert"/>
</UserControl.Resources>
<Grid>
    <TextBox Name="textBox" 
             Foreground="{Binding ElementName=Root, Path=Foreground}"
             Text="{Binding ElementName=Root, Path=Text, UpdateSourceTrigger=PropertyChanged}"
             TextChanged="textBox_TextChanged"
             TextWrapping="Wrap"
             VerticalContentAlignment="Center"/>
    <TextBlock Name="WaterMarkLabel"
           IsHitTestVisible="False"
           Foreground="{Binding ElementName=Root,Path=Foreground}"
           FontWeight="Thin"
           Opacity=".345"
           FontStyle="Italic"
           Text="{Binding ElementName=Root, Path=Watermark}"
           VerticalAlignment="Center"
           TextWrapping="Wrap"
           TextAlignment="Center">
        <TextBlock.Visibility>
            <MultiBinding Converter="{StaticResource ShadowConvert}">
                <Binding ElementName="textBox" Path="Text"/>
            </MultiBinding>
        </TextBlock.Visibility> 
    </TextBlock>
</Grid>

ตัวแปลงตามที่เขียนตอนนี้ไม่จำเป็นว่ามันเป็น MultiConverter แต่ใน wasy นี้มันสามารถขยายได้อย่างง่ายดาย

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WPFControls
{
    class ShadowConverter:IMultiValueConverter
    {
        #region Implementation of IMultiValueConverter

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var text = (string) values[0];
            return text == string.Empty
                       ? Visibility.Visible
                       : Visibility.Collapsed;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[0];
        }

        #endregion
    }
}

และในที่สุดรหัสที่อยู่เบื้องหลัง:

using System.Windows;
using System.Windows.Controls;

namespace WPFControls
{
    /// <summary>
    /// Interaction logic for ShadowedTextBox.xaml
    /// </summary>
    public partial class ShadowedTextBox : UserControl
    {
        public event TextChangedEventHandler TextChanged;

        public ShadowedTextBox()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty WatermarkProperty =
            DependencyProperty.Register("Watermark",
                                        typeof (string),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(string.Empty));

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                                        typeof (string),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(string.Empty));

        public static readonly DependencyProperty TextChangedProperty =
            DependencyProperty.Register("TextChanged",
                                        typeof (TextChangedEventHandler),
                                        typeof (ShadowedTextBox),
                                        new UIPropertyMetadata(null));

        public string Watermark
        {
            get { return (string)GetValue(WatermarkProperty); }
            set
            {
                SetValue(WatermarkProperty, value);
            }
        }

        public string Text
        {
            get { return (string) GetValue(TextProperty); }
            set{SetValue(TextProperty,value);}
        }

        private void textBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (TextChanged != null) TextChanged(this, e);
        }

        public void Clear()
        {
            textBox.Clear();
        }

    }
}

1
<TextBox x:Name="OrderTxt" HorizontalAlignment="Left" VerticalAlignment="Top" VerticalContentAlignment="Center" Margin="10,10,0,0" Width="188" Height="32"/>

<Label IsHitTestVisible="False" Content="Order number" DataContext="{Binding ElementName=OrderTxt}" Foreground="DarkGray">
    <Label.Style>
        <Style TargetType="{x:Type Label}">
            <Setter Property="Visibility" Value="Collapsed"/>
            <Setter Property="Width" Value="{Binding Width}"/>
            <Setter Property="Height" Value="{Binding Height}"/>
            <Setter Property="Margin" Value="{Binding Margin}"/>
            <Setter Property="VerticalAlignment" Value="{Binding VerticalAlignment}"/>
            <Setter Property="HorizontalAlignment" Value="{Binding HorizontalAlignment}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Text}" Value="">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Label.Style>
</Label>

โปรดเพิ่มคำอธิบายบางอย่าง
Mohit Jain

1

MahApps.Metroสำหรับ WPF มีการควบคุมลายน้ำในตัวหากคุณไม่ต้องการหมุนตัวเอง มันค่อนข้างตรงไปตรงมาที่จะใช้

 <AdornerDecorator>
            <TextBox Name="txtSomeText"
                     Width="200"
                     HorizontalAlignment="Right">
                <Controls:TextBoxHelper.Watermark>I'm a watermark!</Controls:TextBoxHelper.Watermark>
            </TextBox>
        </AdornerDecorator>

1

ตั้งค่ากล่องข้อความด้วยข้อความตัวยึดในสีอ่อน ...

public MainWindow ( )
{
    InitializeComponent ( );
    txtInput.Text = "Type something here...";
    txtInput.Foreground = Brushes.DimGray;
}

เมื่อกล่องข้อความได้รับโฟกัสให้ล้างมันและเปลี่ยนสีข้อความ

private void txtInput_GotFocus ( object sender, EventArgs e )
{
    MessageBox.Show ( "got focus" );
    txtInput.Text = "";
    txtInput.Foreground = Brushes.Red;
}

1

นี่คือทางออกที่ง่ายที่สุด:

            <Grid>
                <Label Content="Placeholder text" VerticalAlignment="Center" Margin="10">
                    <Label.Style>
                        <Style TargetType="Label">
                            <Setter Property="Foreground" Value="Transparent"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Expression}" Value="">
                                    <Setter Property="Foreground" Value="Gray"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Label.Style>
                </Label>
                <TextBox HorizontalAlignment="Stretch" Margin="5" Background="Transparent"
                 Text="{Binding Expression, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Padding="5">
                </TextBox>
        </Grid>

นี่คือกล่องข้อความที่มีพื้นหลังโปร่งใสซ้อนทับฉลาก ข้อความสีเทาของป้ายกำกับนั้นโปร่งใสโดยตัวเรียกข้อมูลที่จะยิงเมื่อใดก็ตามที่ข้อความที่ถูกผูกไว้เป็นสิ่งอื่นที่ไม่ใช่สตริงว่าง


1

ดูคำตอบนี้ด้วย คุณสามารถทำให้สิ่งนี้สำเร็จได้ง่ายขึ้นด้วย VisualBrush และทริกเกอร์ในสไตล์:

 <TextBox>
    <TextBox.Style>
        <Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <Style.Resources>
                <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                    <VisualBrush.Visual>
                        <Label Content="Search" Foreground="LightGray" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="Text" Value="{x:Null}">
                    <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter Property="Background" Value="White" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

ในการเพิ่มการใช้งานสไตล์นี้อีกครั้งคุณสามารถสร้างชุดคุณสมบัติที่แนบมาเพื่อควบคุมข้อความแบนเนอร์, สี, การวางแนว ฯลฯ


0

สวัสดีฉันทำให้งานนี้เป็นพฤติกรรม ดังนั้นคุณต้องเพิ่มบางอย่างเช่นนี้ในกล่องข้อความของคุณ

<i:Interaction.Behaviors>
         <Behaviors:TextBoxWatermarkBehavior Label="Test Watermark" LabelStyle="{StaticResource StyleWatermarkLabel}"/>
</i:Interaction.Behaviors>

คุณสามารถค้นหาโพสต์บล็อกของฉันที่นี่


0

ทางออกของฉันค่อนข้างง่าย

ในหน้าต่างเข้าสู่ระบบของฉัน xaml เป็นเช่นนี้

 <DockPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="80" Width="300" LastChildFill="True">
        <Button Margin="5,0,0,0" Click="login_Click" DockPanel.Dock="Right"  VerticalAlignment="Center" ToolTip="Login to system">
            Login
        </Button>
        <StackPanel>
            <TextBox x:Name="userNameWatermarked" Height="25" Foreground="Gray" Text="UserName" GotFocus="userNameWatermarked_GotFocus"></TextBox>
            <TextBox x:Name="userName" Height="25"  TextChanged="loginElement_TextChanged" Visibility="Collapsed" LostFocus="userName_LostFocus" ></TextBox>
            <TextBox x:Name="passwordWatermarked" Height="25" Foreground="Gray" Text="Password"  Margin="0,5,0,5" GotFocus="passwordWatermarked_GotFocus"></TextBox>
            <PasswordBox x:Name="password" Height="25" PasswordChanged="password_PasswordChanged" KeyUp="password_KeyUp" LostFocus="password_LostFocus" Margin="0,5,0,5" Visibility="Collapsed"></PasswordBox>
            <TextBlock x:Name="loginError" Visibility="Hidden" Foreground="Red" FontSize="12"></TextBlock>
        </StackPanel>
    </DockPanel>

รหัสเป็นเช่นนี้

private void userNameWatermarked_GotFocus(object sender, RoutedEventArgs e)
    {
        userNameWatermarked.Visibility = System.Windows.Visibility.Collapsed;
        userName.Visibility = System.Windows.Visibility.Visible;
        userName.Focus();
    }

    private void userName_LostFocus(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrEmpty(this.userName.Text))
        {
            userName.Visibility = System.Windows.Visibility.Collapsed;
            userNameWatermarked.Visibility = System.Windows.Visibility.Visible;
        }
    }

    private void passwordWatermarked_GotFocus(object sender, RoutedEventArgs e)
    {
        passwordWatermarked.Visibility = System.Windows.Visibility.Collapsed;
        password.Visibility = System.Windows.Visibility.Visible;
        password.Focus();
    }

    private void password_LostFocus(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrEmpty(this.password.Password))
        {
            password.Visibility = System.Windows.Visibility.Collapsed;
            passwordWatermarked.Visibility = System.Windows.Visibility.Visible;
        }
    }

เพียงตัดสินใจที่จะซ่อนหรือแสดงกล่องข้อความลายน้ำก็เพียงพอแล้ว แม้ว่าจะไม่สวยงาม แต่ทำงานได้ดี


นี่เป็นตัวอย่างที่สมบูรณ์แบบที่อธิบายถึงวิธีที่จะไม่ทำโดยเฉพาะอย่างยิ่งกับ WPF
Alexandru Dicu

0

เทคนิคนี้ใช้คุณสมบัติพื้นหลังเพื่อแสดง / ซ่อนกล่องข้อความตัวยึด
ตัวยึดตำแหน่งจะแสดงเหตุการณ์เมื่อกล่องข้อความมีโฟกัส

มันทำงานอย่างไร:

  • เมื่อว่างเปล่าพื้นหลังกล่องข้อความตั้งค่าเป็นโปร่งใสเพื่อแสดงข้อความตัวยึดตำแหน่ง
  • เมื่อไม่ได้ตั้งค่าพื้นหลังที่ว่างเปล่าเป็นสีขาวเพื่อปกปิดข้อความตัวยึด

นี่คือตัวอย่างพื้นฐาน เพื่อจุดประสงค์ของฉันฉันได้เปลี่ยนสิ่งนี้เป็น UserControl

<Grid>
    <Grid.Resources>
        <ux:NotEmptyConverter x:Key="NotEmptyConverter" />

        <Style TargetType="{x:Type Control}" x:Key="DefaultStyle">
            <Setter Property="FontSize" Value="20" />
            <Setter Property="Margin" Value="10"/>
            <Setter Property="VerticalAlignment" Value="Center"></Setter>
            <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
        </Style>

        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}"></Style>

    </Grid.Resources>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" Text="Placeholder Text Is Here" Foreground="DarkGray" />
    <TextBox Grid.Row="0" Name="TextBoxEdit" 
            Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
        <TextBox.Style>
            <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=FirstName.Length, FallbackValue=0, TargetNullValue=0}" Value="0">
                        <Setter Property="Background" Value="Transparent"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=FirstName, FallbackValue=0, TargetNullValue=0, Converter={StaticResource NotEmptyConverter}}" Value="false">
                        <Setter Property="Background" Value="White"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</Grid>

นี่คือ ValueConverter เพื่อตรวจจับสตริงที่ไม่ว่างใน DataTrigger

public class NotEmptyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var s = value as string;
        return string.IsNullOrEmpty(s);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

0

คุณสามารถเก็บค่าแยกสำหรับข้อความที่ป้อนและคุณสามารถตั้งค่าพร้อมกับฟิลด์ "ข้อความ" ของกล่องข้อความในเหตุการณ์ "GotFocus" และ "LostFocus" เมื่อคุณได้รับโฟกัสคุณจะต้องล้างกล่องข้อความหากไม่มีค่า และเมื่อคุณสูญเสียโฟกัสคุณจะต้องตั้งค่ารับ "ข้อความ" จากกล่องข้อความแล้วตั้งค่า "ข้อความ" ของกล่องข้อความใหม่ให้เป็นที่ยึดหากว่างเปล่า

private String username = "";

private void usernameTextBox_GotFocus(object sender, RoutedEventArgs e) {
  if (String.IsNullOrEmpty(username)) {
    usernameTextBox.Text = "";
  }
}

private void usernameTextBox_LostFocus(object sender, RoutedEventArgs e) {
  username = usernameTextBox.Text;
  if (String.IsNullOrEmpty(usernameTextBox.Text)) {
    usernameTextBox.Text = "Username";
  }
}

จากนั้นคุณต้องตรวจสอบให้แน่ใจว่าค่า "ข้อความ" ของกล่องข้อความนั้นได้รับการเริ่มต้นเป็นข้อความตัวยึดสถานที่

<TextBox x:Name="usernameTextBox" Text="Username" GotFocus="usernameTextBox_GotFocus" LostFocus="usernameTextBox_LostFocus" />

คุณสามารถแยกสิ่งนี้ลงในคลาสที่ขยายคลาส "กล่องข้อความ" จากนั้นนำมาใช้ใหม่ตลอดโครงการของคุณ

namespace UI {
  public class PlaceholderTextBox : TextBox {
    public String Value { get; set; }
    public String PlaceholderText { get; set; }
    public Brush PlaceholderBrush { get; set; }
    private Brush ValuedBrush { get; set; }

    public PlaceholderTextBox() : base() {}

    protected override void OnInitialized(EventArgs e) {
      base.OnInitialized(e);

      ValuedBrush = this.Foreground;

      if (String.IsNullOrEmpty(this.Text)) {
        this.Text = PlaceholderText;
        this.Foreground = PlaceholderBrush;
      }
    }

    protected override void OnGotFocus(System.Windows.RoutedEventArgs e) {
      this.Foreground = ValuedBrush;
      if (String.IsNullOrEmpty(Value)) {
        this.Text = "";
      }

      base.OnGotFocus(e);
    }

    protected override void OnLostFocus(System.Windows.RoutedEventArgs e) {
      Value = this.Text;
      if (String.IsNullOrEmpty(this.Text)) {
        this.Text = PlaceholderText;
        this.Foreground = PlaceholderBrush;
      }

      base.OnLostFocus(e);
    }
  }
}

แล้วสิ่งนี้สามารถเพิ่มเข้าไปใน xaml ได้โดยตรง

<Window x:Class="UI.LoginWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:UI"
        Initialized="Window_Initialized">
    <Grid>
        <m:PlaceholderTextBox x:Name="usernameTextBox" PlaceholderText="Username" PlaceholderBrush="Gray" />
    </Grid>
</Window>

0

หากแทนที่จะให้การมองเห็นของลายน้ำขึ้นอยู่กับสถานะโฟกัสของตัวควบคุมคุณต้องการให้มันขึ้นอยู่กับว่าผู้ใช้ป้อนข้อความใด ๆ หรือไม่คุณสามารถอัปเดตคำตอบของ John Myczek (จากOnWatermarkChangedล่าง) เป็น

static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    var textbox = (TextBox)d;
    textbox.Loaded += UpdateWatermark;
    textbox.TextChanged += UpdateWatermark;
}

static void UpdateWatermark(object sender, RoutedEventArgs e) {
    var textbox = (TextBox)sender;
    var layer = AdornerLayer.GetAdornerLayer(textbox);
    if (layer != null) {
        if (textbox.Text == string.Empty) {
            layer.Add(new WatermarkAdorner(textbox, GetWatermark(textbox)));
        } else {
            var adorners = layer.GetAdorners(textbox);
            if (adorners == null) {
                return;
            }

            foreach (var adorner in adorners) {
                if (adorner is WatermarkAdorner) {
                    adorner.Visibility = Visibility.Hidden;
                    layer.Remove(adorner);
                }
            }
        }
    }
}

ซึ่งจะเหมาะสมกว่าถ้ากล่องข้อความของคุณได้รับการโฟกัสโดยอัตโนมัติเมื่อแสดงฟอร์มหรือเมื่อ databinding ไปยังคุณสมบัติข้อความ

นอกจากนี้หากลายน้ำของคุณเป็นเพียงสตริงเสมอและคุณต้องการสไตล์ของลายน้ำเพื่อให้ตรงกับสไตล์ของกล่องข้อความดังนั้นใน Adorner จะต้อง:

contentPresenter = new ContentPresenter {
    Content = new TextBlock {
        Text = (string)watermark,
        Foreground = Control.Foreground,
        Background = Control.Background,
        FontFamily = Control.FontFamily,
        FontSize = Control.FontSize,
        ...
    },
    ...
}

0

นี่เป็นวิธีการของฉันดีมากสำหรับ MVVM ที่ฉันตรวจสอบว่ากล่องข้อความมีโฟกัสคุณยังสามารถใช้ทริกเกอร์ปกติสำหรับค่าข้อความได้เช่นกันจุดคือฉันเปลี่ยนพื้นหลังรูปภาพเมื่อค่าเปลี่ยนแปลง:

                    <TextBox.Style>
                        <Style TargetType="TextBox">

                            <Style.Triggers>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsFocused" Value="True"/>
                                        <Condition Property="Text" Value=""/>
                                    </MultiTrigger.Conditions>
                                    <MultiTrigger.Setters>
                                        <Setter Property="Background">
                                            <Setter.Value>
                                                <ImageBrush ImageSource="/Images/Scan.PNG" Stretch="Uniform" AlignmentX="Left"/>
                                            </Setter.Value>
                                        </Setter>
                                    </MultiTrigger.Setters>
                                </MultiTrigger>

                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>

0

ฉันตัดสินใจที่จะแก้ปัญหานี้ผ่านพฤติกรรม มันใช้Hintคุณสมบัติในการกำหนดข้อความที่จะแสดง (อาจเป็นวัตถุถ้าคุณต้องการ) และValueคุณสมบัติในการประเมินสภาพอากาศที่ควรจะมองเห็นคำใบ้หรือไม่

มีการประกาศพฤติกรรมดังนี้:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;

    public class HintBehavior : Behavior<ContentControl>
    {
        public static readonly DependencyProperty HintProperty = DependencyProperty
            .Register("Hint", typeof (string), typeof (HintBehavior)
            //, new FrameworkPropertyMetadata(null, OnHintChanged)
            );

        public string Hint
        {
            get { return (string) GetValue(HintProperty); }
            set { SetValue(HintProperty, value); }
        }

        public static readonly DependencyProperty ValueProperty = DependencyProperty
            .Register("Value", typeof (object), typeof (HintBehavior)
                , new FrameworkPropertyMetadata(null, OnValueChanged));

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var visible = e.NewValue == null;
            d.SetValue(VisibilityProperty, visible ? Visibility.Visible : Visibility.Collapsed);
        }

        public object Value
        {
            get { return GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static readonly DependencyProperty VisibilityProperty = DependencyProperty
            .Register("Visibility", typeof (Visibility), typeof (HintBehavior)
                , new FrameworkPropertyMetadata(Visibility.Visible
                    //, new PropertyChangedCallback(OnVisibilityChanged)
                    ));

        public Visibility Visibility
        {
            get { return (Visibility) GetValue(VisibilityProperty); }
            set { SetValue(VisibilityProperty, value); }
        }

        public static readonly DependencyProperty ForegroundProperty = DependencyProperty
            .Register("Foreground", typeof (Brush), typeof (HintBehavior)
                , new FrameworkPropertyMetadata(new SolidColorBrush(Colors.DarkGray)
                    //, new PropertyChangedCallback(OnForegroundChanged)
                    ));

        public Brush Foreground
        {
            get { return (Brush) GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }

        public static readonly DependencyProperty MarginProperty = DependencyProperty
            .Register("Margin", typeof (Thickness), typeof (HintBehavior)
                , new FrameworkPropertyMetadata(new Thickness(4, 5, 0, 0)
                    //, new PropertyChangedCallback(OnMarginChanged)
                    ));

        public Thickness Margin
        {
            get { return (Thickness) GetValue(MarginProperty); }
            set { SetValue(MarginProperty, value); }
        }


        private static ResourceDictionary _hintBehaviorResources;

        public static ResourceDictionary HintBehaviorResources
        {
            get
            {
                if (_hintBehaviorResources == null)
                {
                    var res = new ResourceDictionary
                    {
                        Source = new Uri("/Mayflower.Client.Core;component/Behaviors/HintBehaviorResources.xaml",
                            UriKind.RelativeOrAbsolute)
                    };
                    _hintBehaviorResources = res;
                }
                return _hintBehaviorResources;
            }
        }


        protected override void OnAttached()
        {
            base.OnAttached();
            var t = (ControlTemplate) HintBehaviorResources["HintBehaviorWrapper"];
            AssociatedObject.Template = t;
            AssociatedObject.Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            AssociatedObject.Loaded -= OnLoaded;
            var label = (Label) AssociatedObject.Template.FindName("PART_HintLabel", AssociatedObject);
            label.DataContext = this;
            //label.Content = "Hello...";
            label.SetBinding(UIElement.VisibilityProperty, new Binding("Visibility") {Source = this, Mode = BindingMode.OneWay});
            label.SetBinding(ContentControl.ContentProperty, new Binding("Hint") {Source = this, Mode = BindingMode.OneWay});
            label.SetBinding(Control.ForegroundProperty, new Binding("Foreground") {Source = this, Mode = BindingMode.OneWay});
            label.SetBinding(FrameworkElement.MarginProperty, new Binding("Margin") {Source = this, Mode = BindingMode.OneWay});
        }
    }

มันล้อมรอบเป้าหมายด้วยเทมเพลตของมันเองและเพิ่มเลเบลไว้:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ControlTemplate x:Key="HintBehaviorWrapper" TargetType="{x:Type ContentControl}">
        <Grid>
            <ContentPresenter Content="{TemplateBinding Content}" />
            <Label x:Name="PART_HintLabel" IsHitTestVisible="False" Padding="0" />
        </Grid>
    </ControlTemplate>
</ResourceDictionary>

ที่จะใช้มันเพียงแค่เพิ่มมันเป็นพฤติกรรมและผูกค่าของคุณ (ในกรณีของฉันฉันเพิ่มไว้ใน ControlTemplate จึงมีผลผูกพัน):

<ContentControl>
    <i:Interaction.Behaviors>
        <behaviors:HintBehavior Value="{Binding Property, RelativeSource={RelativeSource TemplatedParent}}"
                                                        Hint="{Binding Hint, RelativeSource={RelativeSource TemplatedParent}}" />
    </i:Interaction.Behaviors>
    <TextBox ... />
</ContentControl>

ฉันจะชอบความคิดเห็นถ้านี่เป็นวิธีที่สะอาด มันไม่จำเป็นต้องใช้พจนานุกรมแบบคงที่และด้วยเหตุนี้จึงไม่มีการรั่วไหลของหน่วยความจำ


0

ฉันพบวิธีนี้ในวิธีที่ง่ายและรวดเร็วมาก

<ComboBox x:Name="comboBox1" SelectedIndex="0" HorizontalAlignment="Left" Margin="202,43,0,0" VerticalAlignment="Top" Width="149">
  <ComboBoxItem Visibility="Collapsed">
    <TextBlock Foreground="Gray" FontStyle="Italic">Please select ...</TextBlock>
  </ComboBoxItem>
  <ComboBoxItem Name="cbiFirst1">First Item</ComboBoxItem>
  <ComboBoxItem Name="cbiSecond1">Second Item</ComboBoxItem>
  <ComboBoxItem Name="cbiThird1">third Item</ComboBoxItem>
</ComboBox>

อาจช่วยให้ใครก็ตามที่พยายามทำสิ่งนี้

ที่มา: http://www.admindiaries.com/displaying-a-please-select-watermark-type-text-in-a-wpf-combobox/


0
namespace PlaceholderForRichTexxBoxInWPF
{
public MainWindow()
        {
            InitializeComponent();
            Application.Current.MainWindow.WindowState = WindowState.Maximized;// maximize window on load

            richTextBox1.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_GotKeyboardFocus);
            richTextBox1.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(rtb_LostKeyboardFocus);
            richTextBox1.AppendText("Place Holder");
            richTextBox1.Foreground = Brushes.Gray;
        }
 private void rtb_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (sender is RichTextBox)
            {
                TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd); 

                if (textRange.Text.Trim().Equals("Place Holder"))
                {
                    ((RichTextBox)sender).Foreground = Brushes.Black;
                    richTextBox1.Document.Blocks.Clear();
                }
            }
        }


        private void rtb_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            //Make sure sender is the correct Control.
            if (sender is RichTextBox)
            {
                //If nothing was entered, reset default text.
                TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd); 

                if (textRange.Text.Trim().Equals(""))
                {
                    ((RichTextBox)sender).Foreground = Brushes.Gray;
                    ((RichTextBox)sender).AppendText("Place Holder");
                }
            }
        }
}

0
<TextBox Controls:TextBoxHelper.Watermark="Watermark"/>

เพิ่ม mahapps.metro ในโครงการของคุณ เพิ่มกล่องข้อความด้วยรหัสด้านบนไปที่หน้าต่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.