Accueil > Silverlight, windows phone, Windows Phone 7 > [WP7] : TextBox avec une indication visuelle (Watermark)

[WP7] : TextBox avec une indication visuelle (Watermark)

Nous allons voir aujourd’hui comment faire une TextBox contenant une indication visuelle pour l’utilisateur en Windows Phone 7, tout cela de la façon la plus simple possible.

Ce type de TextBox existe un peu partout dans le système mais n’est pas proposé nativement dans le kit de développement.

WP7-BingSearch

Nous verrons plus tard comment améliorer tout ça 🙂

Pour cela :

Création du composant

  • Créez un projet WP7
  • Ajoutez une classe « TextBoxInfo.cs » et ajoutez le squelette suivant :
/// <summary>
	/// TextBox permettant d'afficher une indication à l'utilisateur
	/// </summary>
	public class TextBoxInfo : TextBox
	{
		#region Constructeurs
		/// <summary>
		/// Initialise une nouvelle instance de la TextBox permettant d'afficher une indication à l'utilisateur
		/// </summary>
		public TextBoxInfo()
		{
			DefaultStyleKey = typeof(TextBoxInfo);
		}
		#endregion
	}
  • Ajoutez un TemplatePart correspondant à l’emplacement que nous utiliserons pour l’indication visuelle
/// <summary>
	/// TextBox permettant d'afficher une indication à l'utilisateur
	/// </summary>
	[TemplatePart(Name = "WatermarkContentElement", Type = typeof(FrameworkElement))]
	public class TextBoxInfo : TextBox
	{
  • Ajoutez une DP et son wrapper permettant de définir le contenu de l’indication visuelle
/// <summary>
	/// TextBox permettant d'afficher une indication à l'utilisateur
	/// </summary>
	[TemplatePart(Name = "WatermarkContentElement", Type = typeof(FrameworkElement))]
	public class TextBoxInfo : TextBox
	{
		#region DPs
		/// <summary>
		/// Définit la DP permettant de mentionner le contenu à afficher pour l'indication dans la TextBox
		/// </summary>
		public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(Object), typeof(TextBoxInfo), new PropertyMetadata(OnWatermarkPropertyChanged));

		#endregion

		#region Constructeurs
		/// <summary>
		/// Initialise une nouvelle instance de la TextBox permettant d'afficher une indication à l'utilisateur
		/// </summary>
		public TextBoxInfo()
		{
			DefaultStyleKey = typeof(TextBoxInfo);
		}
		#endregion

		#region Propriétées
		/// <summary>
		/// Obtient ou définit l'indication visuelle à afficher
		/// </summary>
		public Object WatermarkContent
		{
			get
			{
				return GetValue(WatermarkContentProperty) as Object;
			}
			set
			{
				SetValue(WatermarkContentProperty, value);
			}
		}
		#endregion

	}
  • Maintenant redéfinissez la méthode OnApplyTemplate appelée avant l’affichage du composant

Nous récupérons le TemplatePart et le gardons en membre privé.

/// <summary>
	/// TextBox permettant d'afficher une indication à l'utilisateur
	/// </summary>
	[TemplatePart(Name = "WatermarkContentElement", Type = typeof(FrameworkElement))]
	public class TextBoxInfo : TextBox
	{
		#region Membres
		private ContentControl _watermarkContent;
		#endregion
		...
		/// <summary>
		/// Appelé juste avant d'être affiché à l'écran
		/// </summary>
		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			_watermarkContent = GetTemplateChild("WatermarkContentElement") as ContentControl;

			if (_watermarkContent != null)
				ComputeWatermarkContentVisibility();
		}
		...
}
  • Pour terminer redéfinissez les méthodes OnGotFocus et OnLostFocus

Nous faisons ici la logique d’affichage et masquage de l’indication visuelle en fonction du texte saisi par l’utilisateur et du Focus.

/// <summary>
	/// TextBox permettant d'afficher une indication à l'utilisateur
	/// </summary>
	[TemplatePart(Name = "WatermarkContentElement", Type = typeof(FrameworkElement))]
	public class TextBoxInfo : TextBox
	{
		#region Membres
		private ContentControl _watermarkContent;
		#endregion

		#region DPs
		/// <summary>
		/// Définit la DP permettant de mentionner le contenu à afficher pour l'indication dans la TextBox
		/// </summary>
		public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(Object), typeof(TextBoxInfo), new PropertyMetadata(OnWatermarkPropertyChanged));

		#endregion

		#region Constructeurs
		/// <summary>
		/// Initialise une nouvelle instance de la TextBox permettant d'afficher une indication à l'utilisateur
		/// </summary>
		public TextBoxInfo()
		{
			DefaultStyleKey = typeof(TextBoxInfo);
		}
		#endregion

		#region Propriétées
		/// <summary>
		/// Obtient ou définit l'indication visuelle à afficher
		/// </summary>
		public Object WatermarkContent
		{
			get
			{
				return GetValue(WatermarkContentProperty) as Object;
			}
			set
			{
				SetValue(WatermarkContentProperty, value);
			}
		}
		#endregion

		#region Méthodes
		private void ComputeWatermarkContentVisibility()
		{
			if (_watermarkContent != null)
				_watermarkContent.Visibility = String.IsNullOrEmpty(this.Text) ? Visibility.Visible : Visibility.Collapsed;
		}
		#endregion

		#region Handlers
		/// <summary>
		/// Appelé juste avant d'être affiché à l'écran
		/// </summary>
		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			_watermarkContent = GetTemplateChild("WatermarkContentElement") as ContentControl;

			if (_watermarkContent != null)
				ComputeWatermarkContentVisibility();
		}

		/// <summary>
		/// Appelé avant que la TextBox récupère le focus
		/// </summary>
		/// <param name="e">Les données de l'évènement</param>
		protected override void OnGotFocus(RoutedEventArgs e)
		{
			if (_watermarkContent != null)
				_watermarkContent.Visibility = Visibility.Collapsed;

			base.OnGotFocus(e);
		}

		/// <summary>
		/// Appelé avant que la TextBox ne perde le focus
		/// </summary>
		/// <param name="e">Les données de l'évènement</param>
		protected override void OnLostFocus(RoutedEventArgs e)
		{
			if ((_watermarkContent != null) && (String.IsNullOrEmpty(Text)))
				_watermarkContent.Visibility = Visibility.Visible;

			base.OnLostFocus(e);
		}

		private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
		{
			TextBoxInfo self = sender as TextBoxInfo;

			if ((self != null) && (self._watermarkContent != null))
			{
				self.ComputeWatermarkContentVisibility();
			}
		}
		#endregion
	}

Création du style associé

Nous disposons dont un nouveau ContentControl que nous utiliserons pour afficher l’indication visuelle.

WP7-TextBoxInfo-Watermark

<ControlTemplate x:Key="PhoneDisabledTextBoxTemplate"
					 TargetType="TextBox">
		<ContentControl x:Name="ContentElement"
						BorderThickness="0"
						HorizontalContentAlignment="Stretch"
						Margin="{StaticResource PhoneTextBoxInnerMargin}"
						Padding="{TemplateBinding Padding}"
						VerticalContentAlignment="Stretch" />
	</ControlTemplate>

	<Style  TargetType="Controls:TextBoxInfo">
		<Setter Property="FontFamily"
				Value="{StaticResource PhoneFontFamilyNormal}" />
		<Setter Property="FontSize"
				Value="{StaticResource PhoneFontSizeMediumLarge}" />
		<Setter Property="Background"
				Value="{StaticResource PhoneTextBoxBrush}" />
		<Setter Property="Foreground"
				Value="{StaticResource PhoneTextBoxForegroundBrush}" />
		<Setter Property="BorderBrush"
				Value="{StaticResource PhoneTextBoxBrush}" />
		<Setter Property="SelectionBackground"
				Value="{StaticResource PhoneAccentBrush}" />
		<Setter Property="SelectionForeground"
				Value="{StaticResource PhoneTextBoxSelectionForegroundBrush}" />
		<Setter Property="BorderThickness"
				Value="{StaticResource PhoneBorderThickness}" />
		<Setter Property="Padding"
				Value="2" />
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="Controls:TextBoxInfo">
					<Grid Background="Transparent">
						<VisualStateManager.VisualStateGroups>
							<VisualStateGroup x:Name="CommonStates">
								<VisualState x:Name="Normal" />
								<VisualState x:Name="MouseOver" />
								<VisualState x:Name="Disabled">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
																	   Storyboard.TargetName="EnabledBorder">
											<DiscreteObjectKeyFrame KeyTime="0">
												<DiscreteObjectKeyFrame.Value>
													<Visibility>Collapsed</Visibility>
												</DiscreteObjectKeyFrame.Value>
											</DiscreteObjectKeyFrame>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
																	   Storyboard.TargetName="DisabledOrReadonlyBorder">
											<DiscreteObjectKeyFrame KeyTime="0">
												<DiscreteObjectKeyFrame.Value>
													<Visibility>Visible</Visibility>
												</DiscreteObjectKeyFrame.Value>
											</DiscreteObjectKeyFrame>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="ReadOnly">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
																	   Storyboard.TargetName="EnabledBorder">
											<DiscreteObjectKeyFrame KeyTime="0">
												<DiscreteObjectKeyFrame.Value>
													<Visibility>Collapsed</Visibility>
												</DiscreteObjectKeyFrame.Value>
											</DiscreteObjectKeyFrame>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
																	   Storyboard.TargetName="DisabledOrReadonlyBorder">
											<DiscreteObjectKeyFrame KeyTime="0">
												<DiscreteObjectKeyFrame.Value>
													<Visibility>Visible</Visibility>
												</DiscreteObjectKeyFrame.Value>
											</DiscreteObjectKeyFrame>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
																	   Storyboard.TargetName="DisabledOrReadonlyBorder">
											<DiscreteObjectKeyFrame KeyTime="0"
																	Value="{StaticResource PhoneTextBoxBrush}" />
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
																	   Storyboard.TargetName="DisabledOrReadonlyBorder">
											<DiscreteObjectKeyFrame KeyTime="0"
																	Value="{StaticResource PhoneTextBoxBrush}" />
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
																	   Storyboard.TargetName="DisabledOrReadonlyContent">
											<DiscreteObjectKeyFrame KeyTime="0"
																	Value="{StaticResource PhoneTextBoxReadOnlyBrush}" />
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
							<VisualStateGroup x:Name="FocusStates">
								<VisualState x:Name="Focused">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
																	   Storyboard.TargetName="EnabledBorder">
											<DiscreteObjectKeyFrame KeyTime="0"
																	Value="{StaticResource PhoneTextBoxEditBackgroundBrush}" />
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
																	   Storyboard.TargetName="EnabledBorder">
											<DiscreteObjectKeyFrame KeyTime="0"
																	Value="{StaticResource PhoneTextBoxEditBorderBrush}" />
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Unfocused" />

							</VisualStateGroup>
						</VisualStateManager.VisualStateGroups>
						<Border x:Name="EnabledBorder"
								BorderBrush="{TemplateBinding BorderBrush}"
								BorderThickness="{TemplateBinding BorderThickness}"
								Background="{TemplateBinding Background}"
								Margin="{StaticResource PhoneTouchTargetOverhang}">
							<Grid>

								<ContentControl x:Name="WatermarkContentElement"
												IsHitTestVisible="False"
												Background="Transparent"
												Padding="{TemplateBinding Padding}"
												Content="{TemplateBinding WatermarkContent}"
												HorizontalAlignment="Left"
												Opacity="0.5" />

								<ContentControl x:Name="ContentElement"
												BorderThickness="0"
												HorizontalContentAlignment="Stretch"
												Margin="{StaticResource PhoneTextBoxInnerMargin}"
												Padding="{TemplateBinding Padding}"
												VerticalContentAlignment="Stretch" />
							</Grid>
						</Border>
						<Border x:Name="DisabledOrReadonlyBorder"
								BorderBrush="{StaticResource PhoneDisabledBrush}"
								BorderThickness="{TemplateBinding BorderThickness}"
								Background="Transparent"
								Margin="{StaticResource PhoneTouchTargetOverhang}"
								Visibility="Collapsed">
							<TextBox x:Name="DisabledOrReadonlyContent"
									 Background="Transparent"
									 Foreground="{StaticResource PhoneDisabledBrush}"
									 FontWeight="{TemplateBinding FontWeight}"
									 FontStyle="{TemplateBinding FontStyle}"
									 FontSize="{TemplateBinding FontSize}"
									 FontFamily="{TemplateBinding FontFamily}"
									 IsReadOnly="True"
									 SelectionForeground="{TemplateBinding SelectionForeground}"
									 SelectionBackground="{TemplateBinding SelectionBackground}"
									 TextAlignment="{TemplateBinding TextAlignment}"
									 TextWrapping="{TemplateBinding TextWrapping}"
									 Text="{TemplateBinding Text}"
									 Template="{StaticResource PhoneDisabledTextBoxTemplate}" />
						</Border>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>

Utilisation du composant

<Controls:TextBoxInfo WatermarkContent="Rechercher..." />

Rendu

WP7-TextBoxInfo-Watermark-Rendu

Sympa non ? 🙂

Publicités
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

%d blogueurs aiment cette page :