To jest przykład, który pokazuje, jak utworzyć pole tekstowe znaku wodnego w 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 jest zdefiniowany jako:
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();}}}
Uwaga: to nie jest mój kod. Znalazłem to tutaj , ale myślę, że to najlepsze podejście.
Najlepsze podejście? zdecydowanie nie! Czy naprawdę chcesz wpisywać tak wiele wierszy kodu za każdym razem, gdy potrzebujesz znaku wodnego? Rozwiązanie z dołączoną właściwością jest znacznie łatwiejsze do ponownego wykorzystania ...
Thomas Levesque
5
Rozważ utworzenie UserControl.
CSharper
6
Chociaż naprawdę doceniam twój wysiłek pomocy społeczności, naprawdę muszę powiedzieć, że nie jest to nawet godne podejście.
r41n
2
Ten kod został stworzony przez Andy L. Można go znaleźć w projekcie kodu .
aloisdg przenosi się do codidact.com
440
Możesz utworzyć znak wodny, który można dodać do dowolnego TextBoxz dołączoną właściwością. Oto źródło dołączonej właściwości:
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}
Attached Property korzysta z klasy o nazwie WatermarkAdorner, oto źródło:
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}
Teraz możesz umieścić znak wodny na dowolnym TextBoxie:
<AdornerDecorator><TextBox x:Name="SearchTextBox"><controls:WatermarkService.Watermark><TextBlock>Type here to search text</TextBlock></controls:WatermarkService.Watermark></TextBox></AdornerDecorator>
Znakiem wodnym może być cokolwiek chcesz (tekst, obrazy ...). Oprócz pracy dla TextBox, ten znak wodny działa również dla ComboBox i ItemControls.
Rozwiązałem to modyfikując przypisanie marginesu konstruktora WatermarkAdorner jako: Margines = nowa Grubość (Control.Padding.Left, Control.Padding.Top + 1, Control.Padding.Right, Control.Padding.Bottom)
JoanComasFdz
3
@JohnMyczek Aby zlokalizować znak wodny: jak mogę powiązać TextBox.Text w deklaracji xaml Watermark z właściwością z ViewModel?
JoanComasFdz
7
@Matze @JoanComasFdz Oto jak mogę powiązać TextBlock.Textwłaściwość z moim modelem widoku (wstaw to do WatermarkAdornerkonstruktora): FrameworkElement feWatermark = watermark as FrameworkElement;if(feWatermark != null && feWatermark.DataContext == null) { feWatermark.DataContext = this.Control.DataContext; }
Sean Hall
9
Możliwy link do pamięci tutaj. Dodajesz kontrolki ze znakiem wodnym do wewnętrznego słownika statycznego, ale nigdy ich nie usuwasz. Prawdopodobnie zapobiegnie to gromadzeniu śmieci po zakończeniu pracy. Rozważałbym użycie tutaj słabego odniesienia.
Jared G
3
Oprócz słownika statycznego itemcontrols, kod PropertyDescriptor również przecieka pamięć. Musisz wywołać RemoveValueChanged (). Dlatego zachowaj ostrożność podczas korzystania z tego kodu.
muku
284
Tylko używając XAML, bez rozszerzeń, bez konwerterów:
<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>
Niezwykle prosty, najlepiej też imo. Nie wiem, dlaczego miałbyś używać tych wszystkich innych, skoro możesz mieć te 10 linii skryptu xaml i to wszystko. Dzięki.
dj.lnxss
4
Możesz dodać a Padding="6,3,0,0"do TextBlock.
aloisdg przenosi się do codidact.com
1
Bardzo fajnie, ale nie działa na Windows Phone Silverlight :-(
Andrea Antonangeli
14
Jak sprawić, by był to Szablon Kontrolny wielokrotnego użytku?
Richard
2
@cyrianox Wynika to z faktu, że właściwość Password w PasswordBox nie jest wiążąca ze względów bezpieczeństwa. Możesz ustawić tę opcję jako możliwą do wiązania za pomocą tego przykładu tutaj: wpftutorial.net/PasswordBox.html, jednak prawdopodobnie szybsze i łatwiejsze jest użycie zdarzenia PasswordChanged i kodu z tyłu, aby ustawić widoczność w tym przypadku.
Na mojej maszynie win8 wszystkie kontrolki WPF Toolkit mają style Windows 7 (zaokrąglone rogi itp.). A każda kontrolka zestawu narzędzi WPF wygląda zupełnie nie na miejscu po zmieszaniu ze standardowymi kontrolkami.
Gman
1
Podejście Johna Myczka do „Attached Property” ma błąd, w którym gdyby pole tekstowe było pokryte innym elementem, znak wodny przeciekłby i byłby nadal widoczny. To rozwiązanie nie ma tego problemu. (Szkoda, że nie zauważyłem tego wcześniej, ponieważ i tak już korzystam z zestawu narzędzi). Zasługuje na więcej pochwał.
Dax Fohl
W rozwiązaniu Johna Myczka występuje również wyraźny przeciek pamięci, w którym usługa znaku wodnego zachowa odniesienie w słowniku statycznym do dowolnego elementu ItemsControl, do którego przymocowany jest znak wodny. Można to zdecydowanie naprawić, ale wypróbuję wersję Extended WPF Toolkit.
Ok, to może nie być 3 linie w formacie XAML, ale jest to dość proste.
Należy jednak zauważyć, że używa niestandardowej metody rozszerzenia właściwości Text o nazwie „IsEmpty”. Musisz to zaimplementować samodzielnie, jednak wydaje się, że w artykule nie ma o tym mowy.
Widziałem rozwiązanie Johna Myczek za , a jego komentarze o kompatybilność ComboBoxi PasswordBox, więc poprawiła rozwiązanie Johna Myczek, a tutaj jest:
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}
Teraz ComboBoxmożna również Editablei PasswordBoxmożna dodać znak wodny. Nie zapomnij użyć powyższego komentarza JoanComasFdz, aby rozwiązać problem marży.
I, oczywiście, cały kredyt należy się Johnowi Myczkowi.
Świetne rozwiązanie. Dlaczego warto śledzić właściwość Foreground? SetBinding (TextProperty, new Binding ()) zgłasza InvalidOperationException: Powiązanie dwukierunkowe wymaga ścieżki lub XPath?
Tim Murphy,
Ukryłem właściwość Foreground, ponieważ TextBoxWatermarked używa jej do własnych celów. Nie wiem, dlaczego zgłaszany jest wyjątek InvalidOperationException, być może jeśli używasz WPF (użyłem go z Silverlight), musisz przekazać wartość null zamiast nowej Binding ().
Vitaliy Ulantikov
2
Aby użyć tego kodu w WPF, użyj BindingOperations.ClearBinding(this, TextProperty)zamiast SetBinding(TextProperty, new Binding())w obu miejscach.
Sebastian Krysmanski
1
To faktycznie zmienia Textsię w znak wodny. Nie działałoby dla mnie.
homaryzm
prawdopodobnie przydatne, aby dodać do tego wiersze przestrzeni nazw lub w pełni zakwalifikować niektóre z tych rzeczy.
Richard
6
Miałem trochę trudności, gdy korzystałem z kodu @ john-myczek z powiązanym TextBox. Ponieważ TextBox nie podnosi zdarzenia skupienia po aktualizacji, znak wodny pozostałby widoczny pod nowym tekstem. Aby to naprawić, po prostu dodałem inny moduł obsługi zdarzeń:
Szkoda, że nie zauważyłem tej odpowiedzi, zanim zrobiłem to sam.
homaryzm
5
@Veton - Naprawdę podoba mi się prostota twojego rozwiązania, ale moja reputacja nie jest jeszcze wystarczająco wysoka, aby cię zaskoczyć.
@Tim Murphy - Ten błąd „Powiązanie dwukierunkowe wymaga ścieżki lub XPath” był łatwą poprawką ... zaktualizowany kod, w tym kilka innych drobnych poprawek (tylko testowane 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);}}}
Konwerter, tak jak jest teraz napisane, nie jest konieczny, aby był to MultiConverter, ale w tej wersji można go łatwo rozszerzyć
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}}
i na koniec kod za:
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 dla WPF ma wbudowaną kontrolę znaku wodnego, jeśli wolisz nie tworzyć własnych. Jest dość prosty w użyciu.
<AdornerDecorator><TextBoxName="txtSomeText"Width="200"HorizontalAlignment="Right"><Controls:TextBoxHelper.Watermark>I'm a watermark!</Controls:TextBoxHelper.Watermark></TextBox></AdornerDecorator>
To jest pole tekstowe z przezroczystym tłem nakładającym się na etykietę. Szary tekst etykiety staje się przezroczysty przez wyzwalacz danych, który jest uruchamiany, gdy związany tekst jest czymś innym niż pusty ciąg.
Aby zwiększyć możliwość ponownego wykorzystania tego stylu, możesz także utworzyć zestaw dołączonych właściwości do kontrolowania rzeczywistego tekstu banera, koloru, orientacji itp.
Jest to doskonały przykład, który opisuje, jak tego nie robić, zwłaszcza w przypadku WPF.
Alexandru Dicu,
0
Ta technika wykorzystuje właściwość Tło do pokazywania / ukrywania zastępczego pola tekstowego. Symbol zastępczy jest wyświetlany, gdy fokus jest aktywny
Jak to działa:
Gdy pusty, tło TextBox ustawione na Przezroczyste, aby wyświetlać tekst PlaceHolder.
Gdy puste tło nie jest ustawione na Biały, aby ukryć tekst PlaceHolder.
Oto podstawowy przykład. Na własne potrzeby przekształciłem to w UserControl.
Możesz zachować osobną wartość dla wprowadzonego tekstu i ustawić go wraz z polem „Tekst” pola tekstowego w zdarzeniach „GotFocus” i „LostFocus”. Po uzyskaniu fokusu należy wyczyścić pole tekstowe, jeśli nie ma żadnej wartości. A kiedy stracisz fokus, będziesz chciał ustawić wartość „Tekst” z pola tekstowego, a następnie zresetować wartość „Tekst” pola tekstowego do elementu zastępczego, jeśli jest pusty.
Jeśli zamiast widoczności znaku wodnego zależy od stanu skupienia kontrolki, chcesz, aby zależało to od tego, czy użytkownik wprowadził dowolny tekst, możesz zaktualizować odpowiedź Johna Myczka (od OnWatermarkChangeddołu) do
Ma to większy sens, jeśli pole tekstowe jest automatycznie ustawiane podczas wyświetlania formularza lub podczas wiązania danych z właściwością Text.
Również jeśli twój znak wodny jest zawsze tylko ciągiem i potrzebujesz stylu znaku wodnego, aby pasował do stylu pola tekstowego, to w Adorner wykonaj:
Oto moje podejście Doskonale nadaje się do MVVM, w którym sprawdzam również, czy pole tekstowe jest aktywne, możesz także użyć zwykłego wyzwalacza tylko dla wartości tekstowej, ale chodzi o to, że zmieniam tło Obraz po zmianie wartości:
Postanowiłem rozwiązać ten problem poprzez zachowanie. Wykorzystuje Hintwłaściwość do zdefiniowania tekstu do wyświetlenia (może być również obiektem, jeśli wolisz) oraz Valuewłaściwość do oceny, czy podpowiedź powinna być widoczna, czy nie.
Zachowanie deklaruje się w następujący sposób:
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});}}
Obejmuje cel własnym szablonem, dodając do niego etykietę:
Odpowiedzi:
To jest przykład, który pokazuje, jak utworzyć pole tekstowe znaku wodnego w WPF:
TextInputToVisibilityConverter jest zdefiniowany jako:
Uwaga: to nie jest mój kod. Znalazłem to tutaj , ale myślę, że to najlepsze podejście.
źródło
Możesz utworzyć znak wodny, który można dodać do dowolnego
TextBox
z dołączoną właściwością. Oto źródło dołączonej właściwości:Attached Property korzysta z klasy o nazwie
WatermarkAdorner
, oto źródło:Teraz możesz umieścić znak wodny na dowolnym TextBoxie:
Znakiem wodnym może być cokolwiek chcesz (tekst, obrazy ...). Oprócz pracy dla TextBox, ten znak wodny działa również dla ComboBox i ItemControls.
Ten kod został zaadaptowany z tego postu na blogu .
źródło
TextBlock.Text
właściwość z moim modelem widoku (wstaw to doWatermarkAdorner
konstruktora):FrameworkElement feWatermark = watermark as FrameworkElement;
if(feWatermark != null && feWatermark.DataContext == null)
{feWatermark.DataContext = this.Control.DataContext;
}Tylko używając XAML, bez rozszerzeń, bez konwerterów:
źródło
Padding="6,3,0,0"
doTextBlock
.Nie mogę uwierzyć, że nikt nie opublikował oczywistego Rozszerzonego zestawu narzędzi WPF - WatermarkTextBox od Xceed. Działa całkiem dobrze i jest open source na wypadek, gdybyś chciał dostosować.
źródło
W CodeProject jest artykuł o tym, jak to zrobić w „3 liniach XAML”.
Ok, to może nie być 3 linie w formacie XAML, ale jest to dość proste.
Należy jednak zauważyć, że używa niestandardowej metody rozszerzenia właściwości Text o nazwie „IsEmpty”. Musisz to zaimplementować samodzielnie, jednak wydaje się, że w artykule nie ma o tym mowy.
źródło
IsHitTestVisible="False"
. Powinien także pojawić się po TextBox, w przeciwnym razie może nie być widoczny, jeśli TextBox ma tło.Text.IsEmpty
działa: IsEmpty jest rozwiązywany z CollectionView.IsEmptyWidziałem rozwiązanie Johna Myczek za , a jego komentarze o kompatybilność
ComboBox
iPasswordBox
, więc poprawiła rozwiązanie Johna Myczek, a tutaj jest:Teraz
ComboBox
można równieżEditable
iPasswordBox
można dodać znak wodny. Nie zapomnij użyć powyższego komentarza JoanComasFdz, aby rozwiązać problem marży.I, oczywiście, cały kredyt należy się Johnowi Myczkowi.
źródło
Proste rozwiązanie wykorzystujące styl:
Świetne rozwiązanie:
https://code.msdn.microsoft.com/windowsdesktop/In-place-hit-messages-for-18db3a6c
źródło
Ta biblioteka ma znak wodny.
Pakiet Nuget
Przykładowe użycie:
źródło
Stworzyłem implementację tylko kodu źródłowego, która działa dobrze również dla WPF i Silverlight:
Stosowanie:
źródło
BindingOperations.ClearBinding(this, TextProperty)
zamiastSetBinding(TextProperty, new Binding())
w obu miejscach.Text
się w znak wodny. Nie działałoby dla mnie.Miałem trochę trudności, gdy korzystałem z kodu @ john-myczek z powiązanym TextBox. Ponieważ TextBox nie podnosi zdarzenia skupienia po aktualizacji, znak wodny pozostałby widoczny pod nowym tekstem. Aby to naprawić, po prostu dodałem inny moduł obsługi zdarzeń:
źródło
@Veton - Naprawdę podoba mi się prostota twojego rozwiązania, ale moja reputacja nie jest jeszcze wystarczająco wysoka, aby cię zaskoczyć.
@Tim Murphy - Ten błąd „Powiązanie dwukierunkowe wymaga ścieżki lub XPath” był łatwą poprawką ... zaktualizowany kod, w tym kilka innych drobnych poprawek (tylko testowane WPF):
źródło
możesz użyć
GetFocus()
iLostFocus()
wydarzeń, aby to zrobićoto przykład:
źródło
najprostszy sposób na znak wodny TextBox
i dodaj styl tekstowy StaticResource
źródło
Może to pomóc w sprawdzeniu go za pomocą kodu. Po zastosowaniu w polu hasła wyświetli się Hasło, które zniknie po typie użytkownika.
źródło
Cóż, tu jest moje: niekoniecznie najlepsze, ale ponieważ jest proste, łatwo je edytować według własnego gustu.
Konwerter, tak jak jest teraz napisane, nie jest konieczny, aby był to MultiConverter, ale w tej wersji można go łatwo rozszerzyć
i na koniec kod za:
źródło
źródło
MahApps.Metro dla WPF ma wbudowaną kontrolę znaku wodnego, jeśli wolisz nie tworzyć własnych. Jest dość prosty w użyciu.
źródło
Skonfiguruj pole tekstowe z tekstem zastępczym w delikatnym kolorze ...
Gdy pole tekstowe uzyska fokus, wyczyść je i zmień kolor tekstu
źródło
Oto najprostsze rozwiązanie:
To jest pole tekstowe z przezroczystym tłem nakładającym się na etykietę. Szary tekst etykiety staje się przezroczysty przez wyzwalacz danych, który jest uruchamiany, gdy związany tekst jest czymś innym niż pusty ciąg.
źródło
Zobacz także tę odpowiedź . Możesz to zrobić o wiele łatwiej dzięki VisualBrush i niektórym wyzwalaczom w stylu:
Aby zwiększyć możliwość ponownego wykorzystania tego stylu, możesz także utworzyć zestaw dołączonych właściwości do kontrolowania rzeczywistego tekstu banera, koloru, orientacji itp.
źródło
cześć, postawiłem to zadanie w zachowaniu. więc musisz po prostu dodać coś takiego do swojego pola tekstowego
mój post na blogu znajdziesz tutaj
źródło
Moje rozwiązanie jest dość proste.
W moim oknie logowania. xaml jest taki.
kod jest taki.
Wystarczy, że zdecydujesz się ukryć lub pokazać pole tekstowe znaku wodnego. Chociaż nie jest piękny, ale działa dobrze.
źródło
Ta technika wykorzystuje właściwość Tło do pokazywania / ukrywania zastępczego pola tekstowego.
Symbol zastępczy jest wyświetlany, gdy fokus jest aktywny
Jak to działa:
Oto podstawowy przykład. Na własne potrzeby przekształciłem to w UserControl.
Oto ValueConverter do wykrywania niepustych ciągów w DataTrigger.
źródło
Możesz zachować osobną wartość dla wprowadzonego tekstu i ustawić go wraz z polem „Tekst” pola tekstowego w zdarzeniach „GotFocus” i „LostFocus”. Po uzyskaniu fokusu należy wyczyścić pole tekstowe, jeśli nie ma żadnej wartości. A kiedy stracisz fokus, będziesz chciał ustawić wartość „Tekst” z pola tekstowego, a następnie zresetować wartość „Tekst” pola tekstowego do elementu zastępczego, jeśli jest pusty.
Następnie musisz tylko upewnić się, że wartość „Tekst” pola tekstowego jest zainicjowana na tekst zastępczy.
Możesz dalej wyodrębnić to do klasy, która rozszerza klasę „TextBox”, a następnie użyć go ponownie w całym projekcie.
A potem można to dodać bezpośrednio w xaml.
źródło
Jeśli zamiast widoczności znaku wodnego zależy od stanu skupienia kontrolki, chcesz, aby zależało to od tego, czy użytkownik wprowadził dowolny tekst, możesz zaktualizować odpowiedź Johna Myczka (od
OnWatermarkChanged
dołu) doMa to większy sens, jeśli pole tekstowe jest automatycznie ustawiane podczas wyświetlania formularza lub podczas wiązania danych z właściwością Text.
Również jeśli twój znak wodny jest zawsze tylko ciągiem i potrzebujesz stylu znaku wodnego, aby pasował do stylu pola tekstowego, to w Adorner wykonaj:
źródło
Oto moje podejście Doskonale nadaje się do MVVM, w którym sprawdzam również, czy pole tekstowe jest aktywne, możesz także użyć zwykłego wyzwalacza tylko dla wartości tekstowej, ale chodzi o to, że zmieniam tło Obraz po zmianie wartości:
źródło
Postanowiłem rozwiązać ten problem poprzez zachowanie. Wykorzystuje
Hint
właściwość do zdefiniowania tekstu do wyświetlenia (może być również obiektem, jeśli wolisz) orazValue
właściwość do oceny, czy podpowiedź powinna być widoczna, czy nie.Zachowanie deklaruje się w następujący sposób:
Obejmuje cel własnym szablonem, dodając do niego etykietę:
Aby go użyć, po prostu dodaj go jako zachowanie i powiąż swoje wartości (w moim przypadku dodam go w ControlTemplate, stąd wiązanie):
Chciałbym uzyskać informację zwrotną, jeśli jest to uważane za czyste rozwiązanie. Nie wymaga statycznych słowników i dlatego nie ma wycieków pamięci.
źródło
Znalazłem ten sposób, aby zrobić to w bardzo szybki i łatwy sposób
Może może pomóc każdemu, kto to zrobi
Źródło: http://www.admindiaries.com/displaying-a-please-select-watermark-type-text-in-a-wpf-combobox/
źródło
źródło
Dodaj mahapps.metro do swojego projektu. Dodaj pole tekstowe z powyższym kodem do okna.
źródło