Это пример, который демонстрирует, как создать текстовое поле водяного знака в 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"><SetterProperty="HorizontalAlignment"Value="Stretch"/><SetterProperty="VerticalAlignment"Value="Center"/><SetterProperty="Margin"Value="20,0"/></Style></Window.Resources><GridBackground="LightBlue"><Grid.RowDefinitions><RowDefinition/><RowDefinition/><RowDefinition/></Grid.RowDefinitions><GridGrid.Row="0"Background="{StaticResource brushWatermarkBackground}"Style="{StaticResource EntryFieldStyle}"><TextBlockMargin="5,2"Text="This prompt dissappears as you type..."Foreground="{StaticResource brushWatermarkForeground}"Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}"/><TextBoxName="txtUserEntry"Background="Transparent"BorderBrush="{StaticResource brushWatermarkBorder}"/></Grid><GridGrid.Row="1"Background="{StaticResource brushWatermarkBackground}"Style="{StaticResource EntryFieldStyle}"><TextBlockMargin="5,2"Text="This dissappears as the control gets focus..."Foreground="{StaticResource brushWatermarkForeground}"><TextBlock.Visibility><MultiBindingConverter="{StaticResource TextInputToVisibilityConverter}"><BindingElementName="txtUserEntry2"Path="Text.IsEmpty"/><BindingElementName="txtUserEntry2"Path="IsFocused"/></MultiBinding></TextBlock.Visibility></TextBlock><TextBoxName="txtUserEntry2"Background="Transparent"BorderBrush="{StaticResource brushWatermarkBorder}"/></Grid></Grid></Window>
TextInputToVisibilityConverter определяется как:
using System;
using System.Windows.Data;
using System.Windows;
namespace WaterMarkTextBoxDemo{publicclassTextInputToVisibilityConverter:IMultiValueConverter{publicobjectConvert(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]isbool&& values[1]isbool){bool hasText =!(bool)values[0];bool hasFocus =(bool)values[1];if(hasFocus || hasText)returnVisibility.Collapsed;}returnVisibility.Visible;}publicobject[]ConvertBack(objectvalue,Type[] targetTypes,object parameter,System.Globalization.CultureInfo culture ){thrownewNotImplementedException();}}}
Примечание: это не мой код. Я нашел это здесь , но я думаю, что это лучший подход.
Лучший подход? конечно нет! Вы действительно хотите набирать так много строк кода каждый раз, когда вам нужен водяной знак? Решение с прикрепленным свойством намного проще для повторного использования ...
Томас Левеск
5
Рассмотрите возможность создания UserControl.
CSharper
6
Хотя я действительно ценю ваши усилия по оказанию помощи сообществу, я действительно должен сказать, что это далеко не приличный подход.
r41n
2
Этот код был сделан Энди Л. Вы можете найти его на codeproject .
aloisdg переходит на codidact.com
440
Вы можете создать водяной знак, который можно добавить к любому TextBoxс помощью свойства Attached. Вот источник для Attached Property:
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>publicstaticclassWatermarkService{/// <summary>/// Watermark Attached Dependency Property/// </summary>publicstaticreadonlyDependencyPropertyWatermarkProperty=DependencyProperty.RegisterAttached("Watermark",typeof(object),typeof(WatermarkService),newFrameworkPropertyMetadata((object)null,newPropertyChangedCallback(OnWatermarkChanged)));#region Private Fields/// <summary>/// Dictionary of ItemsControls/// </summary>privatestaticreadonlyDictionary<object,ItemsControl> itemsControls =newDictionary<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>publicstaticobjectGetWatermark(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>publicstaticvoidSetWatermark(DependencyObject d,objectvalue){
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>privatestaticvoidOnWatermarkChanged(DependencyObject d,DependencyPropertyChangedEventArgs e){Control control =(Control)d;
control.Loaded+=Control_Loaded;if(d isComboBox){
control.GotKeyboardFocus+=Control_GotKeyboardFocus;
control.LostKeyboardFocus+=Control_Loaded;}elseif(d isTextBox){
control.GotKeyboardFocus+=Control_GotKeyboardFocus;
control.LostKeyboardFocus+=Control_Loaded;((TextBox)control).TextChanged+=Control_GotKeyboardFocus;}if(d isItemsControl&&!(d isComboBox)){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>privatestaticvoidControl_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>privatestaticvoidControl_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>privatestaticvoidItemsSourceChanged(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>privatestaticvoidItemsChanged(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>privatestaticvoidRemoveWatermark(UIElement control){AdornerLayer layer =AdornerLayer.GetAdornerLayer(control);// layer could be null if control is no longer in the visual treeif(layer !=null){Adorner[] adorners = layer.GetAdorners(control);if(adorners ==null){return;}foreach(Adorner adorner in adorners){if(adorner isWatermarkAdorner){
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>privatestaticvoidShowWatermark(Control control){AdornerLayer layer =AdornerLayer.GetAdornerLayer(control);// layer could be null if control is no longer in the visual treeif(layer !=null){
layer.Add(newWatermarkAdorner(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>privatestaticboolShouldShowWatermark(Control c){if(c isComboBox){return(c asComboBox).Text==string.Empty;}elseif(c isTextBoxBase){return(c asTextBox).Text==string.Empty;}elseif(c isItemsControl){return(c asItemsControl).Items.Count==0;}else{returnfalse;}}#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>internalclassWatermarkAdorner:Adorner{#region Private Fields/// <summary>/// <see cref="ContentPresenter"/> that holds the watermark/// </summary>privatereadonlyContentPresenter 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>publicWatermarkAdorner(UIElement adornedElement,object watermark):base(adornedElement){this.IsHitTestVisible=false;this.contentPresenter =newContentPresenter();this.contentPresenter.Content= watermark;this.contentPresenter.Opacity=0.5;this.contentPresenter.Margin=newThickness(Control.Margin.Left+Control.Padding.Left,Control.Margin.Top+Control.Padding.Top,0,0);if(this.ControlisItemsControl&&!(this.ControlisComboBox)){this.contentPresenter.VerticalAlignment=VerticalAlignment.Center;this.contentPresenter.HorizontalAlignment=HorizontalAlignment.Center;}// Hide the control adorner when the adorned element is hiddenBinding binding =newBinding("IsVisible");
binding.Source= adornedElement;
binding.Converter=newBooleanToVisibilityConverter();this.SetBinding(VisibilityProperty, binding);}#endregion#region Protected Properties/// <summary>/// Gets the number of children for the <see cref="ContainerVisual"/>./// </summary>protectedoverrideintVisualChildrenCount{get{return1;}}#endregion#region Private Properties/// <summary>/// Gets the control that is being adorned/// </summary>privateControlControl{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>protectedoverrideVisualGetVisualChild(int index){returnthis.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>protectedoverrideSizeMeasureOverride(Size constraint){// Here's the secret to getting the adorner to cover the whole controlthis.contentPresenter.Measure(Control.RenderSize);returnControl.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>protectedoverrideSizeArrangeOverride(Size finalSize){this.contentPresenter.Arrange(newRect(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.
Я решил это, изменив назначение Margin для конструктора WatermarkAdorner следующим образом: Margin = new Thickness (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; }
Шон Холл
9
Возможная ссылка на память здесь. Вы добавляете элементы управления с водяными знаками во внутренний статический словарь, но не удаляете их. Это, вероятно, предотвратит сбор мусора, когда вы закончите с ними. Я хотел бы рассмотреть использование слабой ссылки здесь.
Джаред Дж
3
Помимо статического словаря itemcontrols, код PropertyDescriptor также пропускает память. Вам нужно вызвать RemoveValueChanged (). Так что будьте осторожны при использовании этого кода.
Муку
284
Просто используя XAML, без расширений, без конвертеров:
<Grid><TextBoxWidth="250"VerticalAlignment="Center"HorizontalAlignment="Left" x:Name="SearchTermTextBox"Margin="5"/><TextBlockIsHitTestVisible="False"Text="Enter Search Term Here"VerticalAlignment="Center"HorizontalAlignment="Left"Margin="10,0,0,0"Foreground="DarkGray"><TextBlock.Style><StyleTargetType="{x:Type TextBlock}"><SetterProperty="Visibility"Value="Collapsed"/><Style.Triggers><DataTriggerBinding="{Binding Text, ElementName=SearchTermTextBox}"Value=""><SetterProperty="Visibility"Value="Visible"/></DataTrigger></Style.Triggers></Style></TextBlock.Style></TextBlock></Grid>
Чрезвычайно простой, лучший ИМО тоже. Я не знаю, почему вы бы использовали все эти другие, когда у вас могут быть эти 10 строк xaml-скрипта, и все. Спасибо.
dj.lnxss
4
Вы можете добавить Padding="6,3,0,0"к TextBlock.
aloisdg переходит на codidact.com
1
Очень хорошо, но это не работает на Windows Phone Silverlight :-(
Андреа Антонангели
14
Как сделать этот шаблон управления многоразовым?
Ричард
2
@cyrianox Это потому, что свойство Password в PasswordBox не может быть привязано по соображениям безопасности. Вы можете сделать это привязываемым, используя этот пример здесь: wpftutorial.net/PasswordBox.html, однако, возможно, быстрее и проще просто использовать событие PasswordChanged и код позади, чтобы установить видимость в этом случае.
АРС
54
Я не могу поверить, что никто не опубликовал очевидный расширенный инструментарий WPF - WatermarkTextBox от Xceed. Он работает довольно хорошо и с открытым исходным кодом, если вы хотите настроить.
На моей машине с win8 все элементы управления WPF Toolkit имеют стили Windows 7 (закругленные углы и т. Д.). И любой элемент управления WPF выглядит совершенно неуместно при смешивании со стандартными элементами управления.
Gman
1
Подход «Прикрепленное свойство» Джона Мычека имеет ошибку, из-за которой, если текстовое поле было закрыто другим элементом, водяной знак просачивался и оставался видимым. Это решение не имеет такой проблемы. (Жаль, что я заметил это раньше, так как я уже использую инструментарий в любом случае). Заслуживает больше голосов.
Дакс Фол
Решение Джона Мычека также имеет явную утечку памяти, когда WatermarkService будет хранить ссылку в статическом словаре на любой ItemsControl, к которому прикрепляется водяной знак. Это, безусловно, можно исправить, но я попробую версию Extended WPF Toolkit.
Хорошо, ну не может быть 3 строки XAML отформатирован, но это довольно просто.
Однако следует отметить, что он использует нестандартный метод расширения для свойства Text, называемый «IsEmpty». Вы должны реализовать это самостоятельно, однако в статье, похоже, об этом не говорится.
Я видел решение Джона Мычека и его комментарии о совместимости ComboBoxи PasswordBoxпоэтому улучшил решение Джона Мычека, и вот оно:
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>publicstaticclassWatermarkService{/// <summary>/// Watermark Attached Dependency Property/// </summary>publicstaticreadonlyDependencyPropertyWatermarkProperty=DependencyProperty.RegisterAttached("Watermark",typeof(object),typeof(WatermarkService),newFrameworkPropertyMetadata((object)null,newPropertyChangedCallback(OnWatermarkChanged)));#region Private Fields/// <summary>/// Dictionary of ItemsControls/// </summary>privatestaticreadonlyDictionary<object,ItemsControl> itemsControls =newDictionary<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>publicstaticobjectGetWatermark(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>publicstaticvoidSetWatermark(DependencyObject d,objectvalue){
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>privatestaticvoidOnWatermarkChanged(DependencyObject d,DependencyPropertyChangedEventArgs e){Control control =(Control)d;
control.Loaded+=Control_Loaded;if(d isTextBox|| d isPasswordBox){
control.GotKeyboardFocus+=Control_GotKeyboardFocus;
control.LostKeyboardFocus+=Control_Loaded;}elseif(d isComboBox){
control.GotKeyboardFocus+=Control_GotKeyboardFocus;
control.LostKeyboardFocus+=Control_Loaded;(d asComboBox).SelectionChanged+=newSelectionChangedEventHandler(SelectionChanged);}elseif(d isItemsControl){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>privatestaticvoidSelectionChanged(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>privatestaticvoidControl_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>privatestaticvoidControl_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>privatestaticvoidItemsSourceChanged(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>privatestaticvoidItemsChanged(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>privatestaticvoidRemoveWatermark(UIElement control){AdornerLayer layer =AdornerLayer.GetAdornerLayer(control);// layer could be null if control is no longer in the visual treeif(layer !=null){Adorner[] adorners = layer.GetAdorners(control);if(adorners ==null){return;}foreach(Adorner adorner in adorners){if(adorner isWatermarkAdorner){
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>privatestaticvoidShowWatermark(Control control){AdornerLayer layer =AdornerLayer.GetAdornerLayer(control);// layer could be null if control is no longer in the visual treeif(layer !=null){
layer.Add(newWatermarkAdorner(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>privatestaticboolShouldShowWatermark(Control c){if(c isComboBox){return(c asComboBox).SelectedItem==null;//return (c as ComboBox).Text == string.Empty;}elseif(c isTextBoxBase){return(c asTextBox).Text==string.Empty;}elseif(c isPasswordBox){return(c asPasswordBox).Password==string.Empty;}elseif(c isItemsControl){return(c asItemsControl).Items.Count==0;}else{returnfalse;}}#endregion}
Теперь ComboBoxможет быть EditableиPasswordBox может добавить водяной знак тоже. Не забудьте использовать комментарий JoanComasFdz выше, чтобы решить проблему маржи.
Отличное решение. Зачем скрывать свойство переднего плана? SetBinding (TextProperty, new Binding ()) создает исключение InvalidOperationException: для двусторонней привязки требуется путь или XPath?
Тим Мерфи
Я скрываю свойство Foreground, потому что TextBoxWatermarked использует его для своих собственных целей. Я не знаю, почему выбрасывается InvalidOperationException, возможно, если вы используете WPF (я использовал его с Silverlight), вам нужно передать null вместо новой Binding ().
Виталий Улантиков
2
Чтобы использовать этот код в WPF, используйте BindingOperations.ClearBinding(this, TextProperty)вместо SetBinding(TextProperty, new Binding())обоих мест.
Себастьян Крысманский
1
Это на самом деле меняется Textна водяной знак. Не будет работать для меня
омар
вероятно, полезно добавить к этому строки пространства имен или полностью квалифицировать некоторые из этих вещей.
Ричард
6
Я столкнулся с некоторыми трудностями при использовании кода @ john-myczek со связанным TextBox. Поскольку TextBox не вызывает событие фокуса при его обновлении, водяной знак остается видимым под новым текстом. Чтобы это исправить, я просто добавил еще один обработчик событий:
Жаль, что я не заметил этот ответ, прежде чем я сделал это сам.
омар
5
@Veton - мне очень нравится простота вашего решения, но моя репутация еще недостаточно высока, чтобы столкнуть вас.
@Tim Murphy - Ошибка «Двустороннее связывание требует Path или XPath» была легко исправлена ... обновленный код, включая некоторые другие небольшие изменения (проверено только WPF):
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;publicclassTextBoxWatermarked:TextBox{publicstringWatermark{get{return(string)GetValue(WaterMarkProperty);}set{SetValue(WaterMarkProperty,value);}}publicstaticreadonlyDependencyPropertyWaterMarkProperty=DependencyProperty.Register("Watermark",typeof(string),typeof(TextBoxWatermarked),newPropertyMetadata(newPropertyChangedCallback(OnWatermarkChanged)));privatebool _isWatermarked =false;privateBinding _textBinding =null;publicTextBoxWatermarked(){Loaded+=(s, ea)=>ShowWatermark();}protectedoverridevoidOnGotFocus(RoutedEventArgs e){base.OnGotFocus(e);HideWatermark();}protectedoverridevoidOnLostFocus(RoutedEventArgs e){base.OnLostFocus(e);ShowWatermark();}privatestaticvoidOnWatermarkChanged(DependencyObject sender,DependencyPropertyChangedEventArgs ea){var tbw = sender asTextBoxWatermarked;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();}privatevoidShowWatermark(){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 WatermarkBindingOperations.ClearBinding(this,TextProperty);//set the signature watermark grayForeground=newSolidColorBrush(Colors.Gray);//display our watermark textText=Watermark;}}privatevoidHideWatermark(){if(_isWatermarked){
_isWatermarked =false;ClearValue(ForegroundProperty);Text="";if(_textBinding !=null)SetBinding(TextProperty, _textBinding);}}}
Конвертер, как написано сейчас, не обязательно является мультиконвертером, но в этом случае его можно легко расширить
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WPFControls{classShadowConverter:IMultiValueConverter{#region Implementation of IMultiValueConverterpublicobjectConvert(object[] values,Type targetType,object parameter,CultureInfo culture){var text =(string) values[0];return text ==string.Empty?Visibility.Visible:Visibility.Collapsed;}publicobject[]ConvertBack(objectvalue,Type[] targetTypes,object parameter,CultureInfo culture){returnnewobject[0];}#endregion}}
и, наконец, код позади:
using System.Windows;
using System.Windows.Controls;
namespace WPFControls{/// <summary>/// Interaction logic for ShadowedTextBox.xaml/// </summary>publicpartialclassShadowedTextBox:UserControl{publiceventTextChangedEventHandlerTextChanged;publicShadowedTextBox(){InitializeComponent();}publicstaticreadonlyDependencyPropertyWatermarkProperty=DependencyProperty.Register("Watermark",typeof(string),typeof(ShadowedTextBox),newUIPropertyMetadata(string.Empty));publicstaticreadonlyDependencyPropertyTextProperty=DependencyProperty.Register("Text",typeof(string),typeof(ShadowedTextBox),newUIPropertyMetadata(string.Empty));publicstaticreadonlyDependencyPropertyTextChangedProperty=DependencyProperty.Register("TextChanged",typeof(TextChangedEventHandler),typeof(ShadowedTextBox),newUIPropertyMetadata(null));publicstringWatermark{get{return(string)GetValue(WatermarkProperty);}set{SetValue(WatermarkProperty,value);}}publicstringText{get{return(string)GetValue(TextProperty);}set{SetValue(TextProperty,value);}}privatevoid textBox_TextChanged(object sender,TextChangedEventArgs e){if(TextChanged!=null)TextChanged(this, e);}publicvoidClear(){
textBox.Clear();}}}
MahApps.Metro для WPF имеет встроенный элемент управления водяными знаками, если вы не хотите бросать свои собственные. Это довольно просто в использовании.
<AdornerDecorator><TextBoxName="txtSomeText"Width="200"HorizontalAlignment="Right"><Controls:TextBoxHelper.Watermark>I'm a watermark!</Controls:TextBoxHelper.Watermark></TextBox></AdornerDecorator>
Это текстовое поле с прозрачным фоном, наложенным на метку. Серый текст метки становится прозрачным с помощью триггера данных, который срабатывает всякий раз, когда связанный текст является чем-то отличным от пустой строки.
Чтобы увеличить возможность повторного использования этого стиля, вы также можете создать набор прикрепленных свойств для управления фактическим текстом, цветом, ориентацией баннера и т. Д.
Это прекрасный пример, который описывает, как этого не делать, особенно с WPF.
Александру Дику
0
Этот метод использует свойство Background, чтобы показать / скрыть текстовое поле заполнителя. Заполнитель отображается событие, когда текстовое поле имеет фокус
Как это устроено:
Когда пусто, фон TextBox устанавливается в прозрачный для отображения текста PlaceHolder.
Когда не пустой фон, установите Белый, чтобы скрыть текст PlaceHolder.
Вот основной пример. Для своих собственных целей я превратил это в UserControl.
Вы можете сохранить отдельное значение для введенного текста и установить его вместе с полем «Текст» текстового поля в событиях «GotFocus» и «LostFocus». Когда вы получите фокус, вы захотите очистить текстовое поле, если нет значения. И когда вы потеряете фокус, вы захотите установить значение «Текст» из текстового поля, а затем сбросить значение «Текст» текстового поля в качестве заполнителя, если оно пустое.
Если вместо того, чтобы видимость водяного знака зависела от состояния фокуса элемента управления, вы хотите, чтобы он зависел от того, ввел ли пользователь какой-либо текст, вы можете обновить ответ Джона Мычека ( OnWatermarkChangedснизу) до
Это имеет больше смысла, если ваше текстовое поле получает фокус автоматически при отображении формы или при привязке данных к свойству Text.
Также, если ваш водяной знак всегда является просто строкой, и вам нужно, чтобы стиль водяного знака соответствовал стилю текстового поля, то в Adorner выполните:
Вот мой подход Отлично подходит для MVVM, где я также проверяю, имеет ли текстовое поле фокус, вы также можете использовать обычный триггер только для текстового значения, а дело в том, что я просто меняю фоновое изображение при изменении значения:
Я решил решить это через Поведение. Он использует Hintсвойство для определения отображаемого текста (также может быть объектом, если вы предпочитаете) иValue свойство для оценки, должна ли подсказка быть видимой или нет.
Поведение объявляется следующим образом:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;publicclassHintBehavior:Behavior<ContentControl>{publicstaticreadonlyDependencyPropertyHintProperty=DependencyProperty.Register("Hint",typeof(string),typeof(HintBehavior)//, new FrameworkPropertyMetadata(null, OnHintChanged));publicstringHint{get{return(string)GetValue(HintProperty);}set{SetValue(HintProperty,value);}}publicstaticreadonlyDependencyPropertyValueProperty=DependencyProperty.Register("Value",typeof(object),typeof(HintBehavior),newFrameworkPropertyMetadata(null,OnValueChanged));privatestaticvoidOnValueChanged(DependencyObject d,DependencyPropertyChangedEventArgs e){var visible = e.NewValue==null;
d.SetValue(VisibilityProperty, visible ?Visibility.Visible:Visibility.Collapsed);}publicobjectValue{get{returnGetValue(ValueProperty);}set{SetValue(ValueProperty,value);}}publicstaticreadonlyDependencyPropertyVisibilityProperty=DependencyProperty.Register("Visibility",typeof(Visibility),typeof(HintBehavior),newFrameworkPropertyMetadata(Visibility.Visible//, new PropertyChangedCallback(OnVisibilityChanged)));publicVisibilityVisibility{get{return(Visibility)GetValue(VisibilityProperty);}set{SetValue(VisibilityProperty,value);}}publicstaticreadonlyDependencyPropertyForegroundProperty=DependencyProperty.Register("Foreground",typeof(Brush),typeof(HintBehavior),newFrameworkPropertyMetadata(newSolidColorBrush(Colors.DarkGray)//, new PropertyChangedCallback(OnForegroundChanged)));publicBrushForeground{get{return(Brush)GetValue(ForegroundProperty);}set{SetValue(ForegroundProperty,value);}}publicstaticreadonlyDependencyPropertyMarginProperty=DependencyProperty.Register("Margin",typeof(Thickness),typeof(HintBehavior),newFrameworkPropertyMetadata(newThickness(4,5,0,0)//, new PropertyChangedCallback(OnMarginChanged)));publicThicknessMargin{get{return(Thickness)GetValue(MarginProperty);}set{SetValue(MarginProperty,value);}}privatestaticResourceDictionary _hintBehaviorResources;publicstaticResourceDictionaryHintBehaviorResources{get{if(_hintBehaviorResources ==null){var res =newResourceDictionary{Source=newUri("/Mayflower.Client.Core;component/Behaviors/HintBehaviorResources.xaml",UriKind.RelativeOrAbsolute)};
_hintBehaviorResources = res;}return _hintBehaviorResources;}}protectedoverridevoidOnAttached(){base.OnAttached();var t =(ControlTemplate)HintBehaviorResources["HintBehaviorWrapper"];AssociatedObject.Template= t;AssociatedObject.Loaded+=OnLoaded;}privatevoidOnLoaded(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,newBinding("Visibility"){Source=this,Mode=BindingMode.OneWay});
label.SetBinding(ContentControl.ContentProperty,newBinding("Hint"){Source=this,Mode=BindingMode.OneWay});
label.SetBinding(Control.ForegroundProperty,newBinding("Foreground"){Source=this,Mode=BindingMode.OneWay});
label.SetBinding(FrameworkElement.MarginProperty,newBinding("Margin"){Source=this,Mode=BindingMode.OneWay});}}
Он оборачивает цель своим собственным шаблоном, добавляя к нему метку:
Чтобы использовать его, просто добавьте его как поведение и привяжите ваши значения (в моем случае я добавляю его в ControlTemplate, отсюда и привязка):
Ответы:
Это пример, который демонстрирует, как создать текстовое поле водяного знака в WPF:
TextInputToVisibilityConverter определяется как:
Примечание: это не мой код. Я нашел это здесь , но я думаю, что это лучший подход.
источник
Вы можете создать водяной знак, который можно добавить к любому
TextBox
с помощью свойства Attached. Вот источник для Attached Property:Присоединенное свойство использует класс с именем
WatermarkAdorner
, вот этот источник:Теперь вы можете поместить водяной знак на любой текстовый блок, например так:
Водяной знак может быть чем угодно (текст, изображения ...). В дополнение к работе с TextBoxes, этот водяной знак также работает для ComboBoxes и ItemControls.
Этот код был адаптирован из этого блога .
источник
TextBlock.Text
свойство с моей моделью представления (поместите это вWatermarkAdorner
конструктор):FrameworkElement feWatermark = watermark as FrameworkElement;
if(feWatermark != null && feWatermark.DataContext == null)
{feWatermark.DataContext = this.Control.DataContext;
}Просто используя XAML, без расширений, без конвертеров:
источник
Padding="6,3,0,0"
кTextBlock
.Я не могу поверить, что никто не опубликовал очевидный расширенный инструментарий WPF - WatermarkTextBox от Xceed. Он работает довольно хорошо и с открытым исходным кодом, если вы хотите настроить.
источник
В CodeProject есть статья о том, как это сделать, в «3 строчках XAML».
Хорошо, ну не может быть 3 строки XAML отформатирован, но это довольно просто.
Однако следует отметить, что он использует нестандартный метод расширения для свойства Text, называемый «IsEmpty». Вы должны реализовать это самостоятельно, однако в статье, похоже, об этом не говорится.
источник
IsHitTestVisible="False"
. Кроме того, он должен идти после TextBox, иначе он может быть невидим, если у TextBox есть фон.Text.IsEmpty
работает: IsEmpty решается из CollectionView.IsEmptyЯ видел решение Джона Мычека и его комментарии о совместимости
ComboBox
иPasswordBox
поэтому улучшил решение Джона Мычека, и вот оно:Теперь
ComboBox
может бытьEditable
иPasswordBox
может добавить водяной знак тоже. Не забудьте использовать комментарий JoanComasFdz выше, чтобы решить проблему маржи.И, конечно же, вся заслуга Джона Мычека.
источник
Простое решение с использованием стиля:
Отличное решение:
https://code.msdn.microsoft.com/windowsdesktop/In-place-hit-messages-for-18db3a6c
источник
Эта библиотека имеет водяной знак.
Пакет Nuget
Пример использования:
источник
Я создал реализацию только для кода siple, которая прекрасно работает и для WPF и Silverlight:
Использование:
источник
BindingOperations.ClearBinding(this, TextProperty)
вместоSetBinding(TextProperty, new Binding())
обоих мест.Text
на водяной знак. Не будет работать для меняЯ столкнулся с некоторыми трудностями при использовании кода @ john-myczek со связанным TextBox. Поскольку TextBox не вызывает событие фокуса при его обновлении, водяной знак остается видимым под новым текстом. Чтобы это исправить, я просто добавил еще один обработчик событий:
источник
@Veton - мне очень нравится простота вашего решения, но моя репутация еще недостаточно высока, чтобы столкнуть вас.
@Tim Murphy - Ошибка «Двустороннее связывание требует Path или XPath» была легко исправлена ... обновленный код, включая некоторые другие небольшие изменения (проверено только WPF):
источник
Вы можете использовать
GetFocus()
иLostFocus()
события, чтобы сделать этовот пример:
источник
Самый простой способ WaterMark Of TextBox
и добавьте текстовое поле стиля StaticResource
источник
Это может помочь проверить его с вашим кодом. При применении к окну пароля, он покажет пароль, который исчезнет при вводе пользовательских типов.
источник
Ну, вот мой: не обязательно лучший, но, поскольку он прост, его легко редактировать на ваш вкус.
Конвертер, как написано сейчас, не обязательно является мультиконвертером, но в этом случае его можно легко расширить
и, наконец, код позади:
источник
источник
MahApps.Metro для WPF имеет встроенный элемент управления водяными знаками, если вы не хотите бросать свои собственные. Это довольно просто в использовании.
источник
Установите текстовое поле с текстом-заполнителем в мягкий цвет ...
Когда текстовое поле получает фокус, очистите его и измените цвет текста
источник
Вот самое простое решение:
Это текстовое поле с прозрачным фоном, наложенным на метку. Серый текст метки становится прозрачным с помощью триггера данных, который срабатывает всякий раз, когда связанный текст является чем-то отличным от пустой строки.
источник
Также смотрите этот ответ . Вы можете сделать это намного проще с помощью VisualBrush и некоторых триггеров в стиле:
Чтобы увеличить возможность повторного использования этого стиля, вы также можете создать набор прикрепленных свойств для управления фактическим текстом, цветом, ориентацией баннера и т. Д.
источник
Привет я поставил эту задачу в поведение. так что вам просто нужно добавить что-то подобное в текстовое поле
Вы можете найти мой пост здесь
источник
Мое решение довольно простое.
В моем окне входа. XAML такой.
код такой.
Просто решите скрыть или показать текстовое поле водяного знака достаточно. Хоть и не красиво, но хорошо работает.
источник
Этот метод использует свойство Background, чтобы показать / скрыть текстовое поле заполнителя.
Заполнитель отображается событие, когда текстовое поле имеет фокус
Как это устроено:
Вот основной пример. Для своих собственных целей я превратил это в UserControl.
Вот ValueConverter для обнаружения непустых строк в DataTrigger.
источник
Вы можете сохранить отдельное значение для введенного текста и установить его вместе с полем «Текст» текстового поля в событиях «GotFocus» и «LostFocus». Когда вы получите фокус, вы захотите очистить текстовое поле, если нет значения. И когда вы потеряете фокус, вы захотите установить значение «Текст» из текстового поля, а затем сбросить значение «Текст» текстового поля в качестве заполнителя, если оно пустое.
Тогда вам просто нужно убедиться, что значение «Текст» текстового поля инициализировано для текста заполнителя.
Вы можете дополнительно извлечь это в класс, который расширяет класс «TextBox», а затем повторно использовать его в вашем проекте.
И тогда это можно добавить непосредственно в xaml.
источник
Если вместо того, чтобы видимость водяного знака зависела от состояния фокуса элемента управления, вы хотите, чтобы он зависел от того, ввел ли пользователь какой-либо текст, вы можете обновить ответ Джона Мычека (
OnWatermarkChanged
снизу) доЭто имеет больше смысла, если ваше текстовое поле получает фокус автоматически при отображении формы или при привязке данных к свойству Text.
Также, если ваш водяной знак всегда является просто строкой, и вам нужно, чтобы стиль водяного знака соответствовал стилю текстового поля, то в Adorner выполните:
источник
Вот мой подход Отлично подходит для MVVM, где я также проверяю, имеет ли текстовое поле фокус, вы также можете использовать обычный триггер только для текстового значения, а дело в том, что я просто меняю фоновое изображение при изменении значения:
источник
Я решил решить это через Поведение. Он использует
Hint
свойство для определения отображаемого текста (также может быть объектом, если вы предпочитаете) иValue
свойство для оценки, должна ли подсказка быть видимой или нет.Поведение объявляется следующим образом:
Он оборачивает цель своим собственным шаблоном, добавляя к нему метку:
Чтобы использовать его, просто добавьте его как поведение и привяжите ваши значения (в моем случае я добавляю его в ControlTemplate, отсюда и привязка):
Я хотел бы обратной связи, если это считается чистым решением. Он не требует статических словарей и, следовательно, не имеет утечки памяти.
источник
Я нашел этот способ сделать это очень быстро и легко
Может быть, это может помочь любому, кто пытается это сделать
Источник: http://www.admindiaries.com/displaying-a-please-select-watermark-type-text-in-a-wpf-combobox/
источник
источник
Добавьте mahapps.metro в ваш проект. Добавьте текстовое поле с вышеуказанным кодом в окно.
источник