C'est ce qu'on appelle un «espace réservé» en HTML. Je mentionne cela pour aider les gens à google cette page.
Scott Stafford
3
Si vous écrivez des applications UWP sur Windows 10, c'est beaucoup plus facile. <TextBox PlaceholderText = "Search" /> Plus d'informations: msdn.microsoft.com/en-us/library/windows/apps/…
Bleak Morn
Réponses:
57
Voici un exemple qui montre comment créer une zone de texte en filigrane dans 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 est défini comme:
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();}}}
Remarque: ce n'est pas mon code. Je l'ai trouvé ici , mais je pense que c'est la meilleure approche.
Comment puis-je l'appliquer dans une boîte de mot de passe?
Sauron
91
La meilleure approche? certainement pas! Voulez-vous vraiment taper autant de lignes de code chaque fois que vous avez besoin d'un filigrane? La solution avec une propriété attenante est beaucoup plus facile à réutiliser ...
Thomas Levesque
5
Pensez à créer un UserControl.
CSharper
6
Bien que j'apprécie vraiment vos efforts pour aider la communauté, je dois vraiment dire que c'est loin d'être même une approche décente.
r41n
2
Ce code a été créé par Andy L. Vous pouvez le trouver sur codeproject .
aloisdg passe à codidact.com le
440
Vous pouvez créer un filigrane qui peut être ajouté à tout TextBoxavec une propriété attachée. Voici la source de la propriété jointe:
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}
La propriété attachée utilise une classe appelée WatermarkAdorner, voici cette source:
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}
Vous pouvez maintenant mettre un filigrane sur n'importe quel TextBox comme ceci:
<AdornerDecorator><TextBox x:Name="SearchTextBox"><controls:WatermarkService.Watermark><TextBlock>Type here to search text</TextBlock></controls:WatermarkService.Watermark></TextBox></AdornerDecorator>
Le filigrane peut être tout ce que vous voulez (texte, images ...). En plus de travailler pour TextBoxes, ce filigrane fonctionne également pour ComboBoxes et ItemControls.
Je l'ai résolu en modifiant l'affectation de la marge du constructeur WatermarkAdorner comme suit: Margin = new Thickness (Control.Padding.Left, Control.Padding.Top + 1, Control.Padding.Right, Control.Padding.Bottom)
JoanComasFdz
3
@JohnMyczek Afin de localiser le filigrane: comment puis-je lier TextBox.Text dans la déclaration xaml Watermark à une propriété de ViewModel?
JoanComasFdz
7
@Matze @JoanComasFdz Voici comment je peux lier la TextBlock.Textpropriété à mon modèle de vue (mettre ceci dans le WatermarkAdornerconstructeur): FrameworkElement feWatermark = watermark as FrameworkElement;if(feWatermark != null && feWatermark.DataContext == null) { feWatermark.DataContext = this.Control.DataContext; }
Sean Hall
9
Lien mémoire possible ici. Vous ajoutez des contrôles filigranés au dictionnaire statique interne mais ne les supprimez jamais. Cela empêchera probablement vos vues d'être récupérées une fois que vous en aurez fini avec elles. J'envisagerais d'utiliser une référence faible ici.
Jared G
3
Outre le dictionnaire statique des éléments de contrôle, le code PropertyDescriptor fuit également la mémoire. Vous devez appeler RemoveValueChanged (). Soyez donc prudent lorsque vous utilisez ce code.
muku
284
Juste en utilisant XAML, pas d'extensions, pas de convertisseurs:
<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>
Extrêmement simple, meilleur imo aussi. Je ne sais pas pourquoi vous utiliseriez tous ces autres quand vous pourriez avoir ces 10 lignes de script xaml et c'est tout. Merci.
dj.lnxss
4
Vous voudrez peut-être ajouter un Padding="6,3,0,0"au TextBlock.
aloisdg passe à codidact.com le
1
Très bien, mais cela ne fonctionne pas sur Windows Phone Silverlight :-(
Andrea Antonangeli
14
Comment pourrait-on en faire un modèle de contrôle réutilisable?
Richard
2
@cyrianox Cela est dû au fait que la propriété Password sur un PasswordBox n'est pas contraignable pour des raisons de sécurité. Vous pouvez le rendre lié en utilisant cet exemple ici: wpftutorial.net/PasswordBox.html mais il est probablement plus rapide et plus simple d'utiliser simplement l'événement PasswordChanged et le code derrière pour définir la visibilité dans ce cas.
2017
54
Je ne peux pas croire que personne n'a posté la boîte à outils WPF étendue évidente - WatermarkTextBox de Xceed. Il fonctionne assez bien et est open source au cas où vous souhaiteriez le personnaliser.
Sur ma machine win8, tous les contrôles WPF Toolkit ont des styles Windows 7 (coins arrondis, etc.). Et tout contrôle WPF toolkit semble complètement hors de propos lorsqu'il est mélangé avec des contrôles standard.
Gman
1
L'approche "Propriété attachée" par John Myczek a un bug par lequel si la zone de texte était couverte par un autre élément, le filigrane saignerait et serait toujours visible. Cette solution n'a pas ce problème. (J'aimerais avoir remarqué cela plus tôt car j'utilise déjà la boîte à outils de toute façon). Mérite plus de votes positifs.
Dax Fohl
La solution de John Myczek a également une fuite de mémoire apparente, où le WatermarkService gardera une référence dans un dictionnaire statique à tout ItemsControl auquel un filigrane est attaché. Cela pourrait certainement être corrigé, mais je vais essayer la version Extended WPF Toolkit.
Ok, eh bien , il pourrait ne pas être 3 lignes de XAML formaté, mais il est assez simple.
Une chose à noter cependant, c'est qu'il utilise une méthode d'extension non standard sur la propriété Text, appelée "IsEmpty". Vous devez l'implémenter vous-même, mais l'article ne semble pas le mentionner.
Le TextBox devrait avoir IsHitTestVisible="False". En outre, il devrait venir après le TextBox, sinon il pourrait ne pas être visible si le TextBox a un arrière-plan.
ANeves
Cet article dans CodeProject est tout simplement mauvais.
J'ai vu la solution de John Myczek , et ses commentaires sur la compatibilité avec ComboBoxet PasswordBox, alors j'ai amélioré la solution de John Myczek, et la voici:
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}
Maintenant, un ComboBoxpeut aussi être Editable, et PasswordBoxpeut également ajouter un filigrane. N'oubliez pas d'utiliser le commentaire de JoanComasFdz ci-dessus pour résoudre le problème de marge.
Et, bien sûr, tout le mérite revient à John Myczek.
Il s'agit en fait d'une version améliorée du code de la belle pièce @ john-myczek et il a fonctionné avec une amende pour les zones de liste déroulante. Merci à vous deux!
Excellente solution. Pourquoi masquer la propriété Foreground? SetBinding (TextProperty, new Binding ()) lève InvalidOperationException: la liaison bidirectionnelle nécessite Path ou XPath?
Tim Murphy
Je masque la propriété Foreground car TextBoxWatermarked l'utilise à ses propres fins. Je ne sais pas pourquoi InvalidOperationException est levée, peut-être que si vous utilisez WPF (je l'ai utilisé avec Silverlight), vous devez passer null au lieu de new Binding ().
Vitaliy Ulantikov
2
Pour utiliser ce code dans WPF, utilisez BindingOperations.ClearBinding(this, TextProperty)plutôt que SetBinding(TextProperty, new Binding())dans les deux endroits.
Sebastian Krysmanski
1
Cela change Texten fait le filigrane. Ça ne marcherait pas pour moi.
homardisme du
probablement utile pour ajouter des lignes d'espace de noms à cela, ou qualifier complètement certaines de ces choses.
Richard
6
J'ai rencontré un peu de difficulté lors de l'utilisation du code de @ john-myczek avec une TextBox liée. Comme la TextBox ne déclenche pas d'événement de focus lors de sa mise à jour, le filigrane resterait visible sous le nouveau texte. Pour résoudre ce problème, j'ai simplement ajouté un autre gestionnaire d'événements:
J'aimerais avoir remarqué cette réponse avant de le faire moi-même.
homardisme du
5
@Veton - J'aime vraiment la simplicité de votre solution mais ma réputation n'est pas encore assez élevée pour vous heurter encore.
@Tim Murphy - Cette erreur "La liaison bidirectionnelle nécessite Path ou XPath" était une solution facile ... code mis à jour comprenant quelques autres petits ajustements (uniquement testé 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);}}}
Cela peut aider à le vérifier avec votre code.Lorsqu'il est appliqué à la boîte de mot de passe, il affichera le mot de passe, qui disparaîtra lors de la saisie des types.
Le convertisseur, comme il est écrit maintenant, il n'est pas nécessaire qu'il s'agisse d'un MultiConverter, mais dans ce cas, il peut être étendu facilement
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}}
et enfin le code derrière:
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 pour WPF a un contrôle de filigrane intégré, si vous préférez ne pas rouler le vôtre. C'est assez simple à utiliser.
<AdornerDecorator><TextBoxName="txtSomeText"Width="200"HorizontalAlignment="Right"><Controls:TextBoxHelper.Watermark>I'm a watermark!</Controls:TextBoxHelper.Watermark></TextBox></AdornerDecorator>
Il s'agit d'une zone de texte avec fond transparent superposant une étiquette. Le texte gris de l'étiquette est rendu transparent par un déclencheur de données qui se déclenche chaque fois que le texte lié est autre chose qu'une chaîne vide.
Pour augmenter la réutilisation de ce style, vous pouvez également créer un ensemble de propriétés attachées pour contrôler le texte, la couleur, l'orientation, etc. de la bannière de repère réelle.
Ceci est un exemple parfait qui décrit comment ne pas le faire, en particulier avec WPF.
Alexandru Dicu
0
Cette technique utilise la propriété Background pour afficher / masquer la zone de texte d'espace réservé. L'espace réservé est affiché comme événement lorsque Textbox a le focus
Comment ça fonctionne:
Lorsqu'il est vide, l'arrière-plan TextBox est défini sur Transparent pour afficher le texte PlaceHolder.
Lorsqu'il n'est pas vide, le fond est défini sur Blanc pour couvrir le texte PlaceHolder.
Voici un exemple de base. Pour mes propres besoins, j'ai transformé cela en un UserControl.
Vous pouvez conserver une valeur distincte pour le texte saisi et vous pouvez la définir avec le champ "Texte" de la zone de texte dans les événements "GotFocus" et "LostFocus". Lorsque vous obtenez le focus, vous souhaiterez effacer la zone de texte s'il n'y a pas de valeur. Et lorsque vous perdez le focus, vous souhaiterez définir la valeur "Texte" de la zone de texte, puis réinitialiser la valeur "Texte" de la zone de texte sur l'espace réservé s'il est vide.
Si, au lieu que la visibilité du filigrane dépende de l'état de mise au point du contrôle, vous souhaitez que cela dépende de la saisie ou non de texte par l'utilisateur, vous pouvez mettre à jour la réponse de John Myczek (de OnWatermarkChangedbas en) à
Cela a plus de sens si votre zone de texte obtient automatiquement le focus lors de l'affichage du formulaire ou lors de la liaison de données à la propriété Text.
De plus, si votre filigrane est toujours juste une chaîne et que vous avez besoin que le style du filigrane corresponde au style de la zone de texte, dans l'Adorner, faites:
Voici mon approche Est idéal pour MVVM où je vérifie également si la zone de texte a le focus, vous pouvez également utiliser un déclencheur régulier uniquement pour la valeur du texte, le fait est que je change simplement l'image d'arrière-plan lorsque la valeur change:
J'ai décidé de résoudre ce problème via un comportement. Il utilise une Hintpropriété pour définir le texte à afficher (peut également être un objet, si vous préférez) et une Valuepropriété pour évaluer si l'indice doit être visible ou non.
Le comportement est déclaré comme suit:
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});}}
Il enveloppe la cible avec son propre modèle, en y ajoutant une étiquette:
J'adorerais les commentaires si cela est considéré comme une solution propre. Il ne nécessite pas de dictionnaires statiques et n'a donc aucune fuite de mémoire.
Réponses:
Voici un exemple qui montre comment créer une zone de texte en filigrane dans WPF:
TextInputToVisibilityConverter est défini comme:
Remarque: ce n'est pas mon code. Je l'ai trouvé ici , mais je pense que c'est la meilleure approche.
la source
Vous pouvez créer un filigrane qui peut être ajouté à tout
TextBox
avec une propriété attachée. Voici la source de la propriété jointe:La propriété attachée utilise une classe appelée
WatermarkAdorner
, voici cette source:Vous pouvez maintenant mettre un filigrane sur n'importe quel TextBox comme ceci:
Le filigrane peut être tout ce que vous voulez (texte, images ...). En plus de travailler pour TextBoxes, ce filigrane fonctionne également pour ComboBoxes et ItemControls.
Ce code a été adapté de ce billet de blog .
la source
TextBlock.Text
propriété à mon modèle de vue (mettre ceci dans leWatermarkAdorner
constructeur):FrameworkElement feWatermark = watermark as FrameworkElement;
if(feWatermark != null && feWatermark.DataContext == null)
{feWatermark.DataContext = this.Control.DataContext;
}Juste en utilisant XAML, pas d'extensions, pas de convertisseurs:
la source
Padding="6,3,0,0"
auTextBlock
.Je ne peux pas croire que personne n'a posté la boîte à outils WPF étendue évidente - WatermarkTextBox de Xceed. Il fonctionne assez bien et est open source au cas où vous souhaiteriez le personnaliser.
la source
Il y a un article sur CodeProject sur la façon de le faire dans "3 lignes de XAML".
Ok, eh bien , il pourrait ne pas être 3 lignes de XAML formaté, mais il est assez simple.
Une chose à noter cependant, c'est qu'il utilise une méthode d'extension non standard sur la propriété Text, appelée "IsEmpty". Vous devez l'implémenter vous-même, mais l'article ne semble pas le mentionner.
la source
IsHitTestVisible="False"
. En outre, il devrait venir après le TextBox, sinon il pourrait ne pas être visible si le TextBox a un arrière-plan.Text.IsEmpty
: IsEmpty est en cours de résolution à partir de CollectionView.IsEmptyJ'ai vu la solution de John Myczek , et ses commentaires sur la compatibilité avec
ComboBox
etPasswordBox
, alors j'ai amélioré la solution de John Myczek, et la voici:Maintenant, un
ComboBox
peut aussi êtreEditable
, etPasswordBox
peut également ajouter un filigrane. N'oubliez pas d'utiliser le commentaire de JoanComasFdz ci-dessus pour résoudre le problème de marge.Et, bien sûr, tout le mérite revient à John Myczek.
la source
Solution simple utilisant le style:
Excellente solution:
https://code.msdn.microsoft.com/windowsdesktop/In-place-hit-messages-for-18db3a6c
la source
Cette bibliothèque a un filigrane.
Forfait Nuget
Exemple d'utilisation:
la source
J'ai créé une implémentation de code unique qui fonctionne bien pour WPF et Silverlight:
Usage:
la source
BindingOperations.ClearBinding(this, TextProperty)
plutôt queSetBinding(TextProperty, new Binding())
dans les deux endroits.Text
en fait le filigrane. Ça ne marcherait pas pour moi.J'ai rencontré un peu de difficulté lors de l'utilisation du code de @ john-myczek avec une TextBox liée. Comme la TextBox ne déclenche pas d'événement de focus lors de sa mise à jour, le filigrane resterait visible sous le nouveau texte. Pour résoudre ce problème, j'ai simplement ajouté un autre gestionnaire d'événements:
la source
@Veton - J'aime vraiment la simplicité de votre solution mais ma réputation n'est pas encore assez élevée pour vous heurter encore.
@Tim Murphy - Cette erreur "La liaison bidirectionnelle nécessite Path ou XPath" était une solution facile ... code mis à jour comprenant quelques autres petits ajustements (uniquement testé WPF):
la source
vous pouvez utiliser
GetFocus()
et lesLostFocus()
événements pour ce fairevoici l'exemple:
la source
Chemin le plus simple vers l'eau
et ajouter un style StaticResource de zone de texte
la source
Cela peut aider à le vérifier avec votre code.Lorsqu'il est appliqué à la boîte de mot de passe, il affichera le mot de passe, qui disparaîtra lors de la saisie des types.
la source
Et bien voici le mien: pas forcément le meilleur, mais comme c'est simple c'est facile à éditer à votre goût.
Le convertisseur, comme il est écrit maintenant, il n'est pas nécessaire qu'il s'agisse d'un MultiConverter, mais dans ce cas, il peut être étendu facilement
et enfin le code derrière:
la source
la source
MahApps.Metro pour WPF a un contrôle de filigrane intégré, si vous préférez ne pas rouler le vôtre. C'est assez simple à utiliser.
la source
Configurer la zone de texte avec du texte d'espace réservé dans une couleur douce ...
Lorsque la zone de texte obtient le focus, effacez-la et changez la couleur du texte
la source
Voici la solution la plus simple:
Il s'agit d'une zone de texte avec fond transparent superposant une étiquette. Le texte gris de l'étiquette est rendu transparent par un déclencheur de données qui se déclenche chaque fois que le texte lié est autre chose qu'une chaîne vide.
la source
Voir également cette réponse . Vous pouvez accomplir cela beaucoup plus facilement avec un VisualBrush et certains déclencheurs dans un style:
Pour augmenter la réutilisation de ce style, vous pouvez également créer un ensemble de propriétés attachées pour contrôler le texte, la couleur, l'orientation, etc. de la bannière de repère réelle.
la source
salut j'ai mis cette tâche dans un comportement. il vous suffit donc d'ajouter quelque chose comme ça à votre zone de texte
vous pouvez trouver mon blog ici
la source
Ma solution est assez simple.
Dans ma fenêtre de connexion. le xaml est comme ça.
le code est comme ça.
Décidez simplement de masquer ou d'afficher la zone de texte du filigrane. Bien que pas beau, mais fonctionne bien.
la source
Cette technique utilise la propriété Background pour afficher / masquer la zone de texte d'espace réservé.
L'espace réservé est affiché comme événement lorsque Textbox a le focus
Comment ça fonctionne:
Voici un exemple de base. Pour mes propres besoins, j'ai transformé cela en un UserControl.
Voici le ValueConverter pour détecter les chaînes non vides dans le DataTrigger.
la source
Vous pouvez conserver une valeur distincte pour le texte saisi et vous pouvez la définir avec le champ "Texte" de la zone de texte dans les événements "GotFocus" et "LostFocus". Lorsque vous obtenez le focus, vous souhaiterez effacer la zone de texte s'il n'y a pas de valeur. Et lorsque vous perdez le focus, vous souhaiterez définir la valeur "Texte" de la zone de texte, puis réinitialiser la valeur "Texte" de la zone de texte sur l'espace réservé s'il est vide.
Il vous suffit ensuite de vous assurer que la valeur "Texte" de la zone de texte est initialisée au texte de l'espace réservé.
Vous pouvez encore extraire cela dans une classe qui étend la classe "TextBox", puis la réutiliser tout au long de votre projet.
Et puis cela peut être ajouté directement dans le xaml.
la source
Si, au lieu que la visibilité du filigrane dépende de l'état de mise au point du contrôle, vous souhaitez que cela dépende de la saisie ou non de texte par l'utilisateur, vous pouvez mettre à jour la réponse de John Myczek (de
OnWatermarkChanged
bas en) àCela a plus de sens si votre zone de texte obtient automatiquement le focus lors de l'affichage du formulaire ou lors de la liaison de données à la propriété Text.
De plus, si votre filigrane est toujours juste une chaîne et que vous avez besoin que le style du filigrane corresponde au style de la zone de texte, dans l'Adorner, faites:
la source
Voici mon approche Est idéal pour MVVM où je vérifie également si la zone de texte a le focus, vous pouvez également utiliser un déclencheur régulier uniquement pour la valeur du texte, le fait est que je change simplement l'image d'arrière-plan lorsque la valeur change:
la source
J'ai décidé de résoudre ce problème via un comportement. Il utilise une
Hint
propriété pour définir le texte à afficher (peut également être un objet, si vous préférez) et uneValue
propriété pour évaluer si l'indice doit être visible ou non.Le comportement est déclaré comme suit:
Il enveloppe la cible avec son propre modèle, en y ajoutant une étiquette:
Pour l'utiliser, il suffit de l'ajouter comme comportement et de lier vos valeurs (dans mon cas je l'ajoute dans un ControlTemplate, d'où la liaison):
J'adorerais les commentaires si cela est considéré comme une solution propre. Il ne nécessite pas de dictionnaires statiques et n'a donc aucune fuite de mémoire.
la source
J'ai trouvé cette façon de le faire de manière très rapide et facile
Peut-être que cela peut aider quiconque essaie de le faire
Source: http://www.admindiaries.com/displaying-a-please-select-watermark-type-text-in-a-wpf-combobox/
la source
la source
Ajoutez mahapps.metro à votre projet. Ajoutez une zone de texte avec le code ci-dessus à la fenêtre.
la source