Un TextBlock sélectionnable pour WP

Après quelques semaines très mouvementées dues à la sortie de BlueTomato (anciennement Sonic’s Jewels) et des sessions TechDays, il est temps de publier un petit article 100% code :)

TextBlock mon ami

Dans nos applications, on se sert habituellement de TextBlock pour rendre du texte.
Celui-ci est très puissant mais malheureusement il lui manque une fonctionnalité parfois bien pratique : Le copier-coller !
Seule les TextBoxs bénéficient de cette fonctionnalité dans Silverlight.
Comment faire alors pour que l’on puisse obtenir un texte sélectionnable et permettre enfin à nos adresses mail, telephone ou toutes autres informations d’être copiées vers un mail, SMS ou d’autres applications sans forcément être modificable.

En utilisant un TextBox bien sûr !

Le TextBox à notre rescousse

Commençons par lancer Blend afin de récupérer son style et plus particulièrement son template.

<Style x:Key="TextBoxStyle1" TargetType="TextBox">
	<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="TextBox">
				<Grid Background="Transparent">
					<VisualStateManager.VisualStateGroups>
					     <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>
					</VisualStateGroup>

						<!-- Autres VisualStates -->
					</VisualStateManager.VisualStateGroups>
					<Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">
							<ContentControl Visibility="Collapsed" x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/>
					</Border>
						<!-- Fin du template -->
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

Afin de pouvoir rendre semblable notre TextBox à un TextBlock, nous allons modifier quelques une des propriétés de son Style.
Nous passerons tout d’abord le background du control à Transparent par défaut puis son Foreground de la couleur de base des TextBlocks:

          <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeNormal}"/>
	<Setter Property="Background" Value="Transparent"/>
	<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>

Puis on retire l’ensemble du VisualState Focus afin que ni le fond ni les bords ne soit affecté lorsque le control acquière le focus :

<VisualState x:Name="Focused"/>

Pour finir on retire toutes les informations de bord ou de Margin non indispensable à notre nouveau contrôle :

<Border x:Name="EnabledBorder" Background="{TemplateBinding Background}">
      <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/>
</Border>

Saisie au clavier

On a maintenant un TextBox qui ressemble à un TextBlock mais qui réagit encore au clavier.
Notre objectif n’est pas de ne plus afficher le clavier puisque c’est celui-ci qui nous permet d’atteindre l’icône copier, mais de le rendre inactif.
Il suffit pour se faire de s’abonner à l’évenement TextChanged et d’annuler toute saisie.

        void textboxSelector_TextChanged(object sender, TextChangedEventArgs e)
        {
            // this.Text est le text initiale de la TextBox
            this.textboxSelector.Text = this.Text;
            this.SelectAll();        
        }

TextChanged empêche à la fois la saisie d’une touche et le « Coller » d’éléments mais afin d’être plus réactif on peut ajouter l’évenement KeyDown en lui demandant de ne pas transmettre la touche actuellement frappée.

private void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
	// on annule toutes touches
	e.Handled = true;
}	

On peut noter que l’utilisation de e.Handled n’empêche pas pour autant la possibilité de coller du texte !

Et pourquoi pas un ReadOnly ?

Dans mon projet, je voulais pouvoir utiliser la sélection à l’aide éventuellement d’un scroll ce que ne permet pas le IsReadOnly. Rudy a développer un control basé sur ce principe. Je vous encourage à aller voir sur son blog une implémentation possible : http://www.rudyhuyn.com/blog/​2012/02/15/​un-textblock-selectionnable-pou​r-wp-alternative/

Sélection automatique

Afin d’être plus efficace, le controle pourrait se sélectionner automatiquement lorsqu’il récupère le focus.

private void TextBox_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
   TextBox t = sender as TextBox;
   t.SelectAll();
}

Et pourquoi pas utiliser le Clipboard ?

Apparut dans Mango, le clipboard permet la mise en clipboard du texte de son choix. En revanche la sélection serait moins fine sur un grand texte (à moins de gerer soi-même la sélection du texte) et comme le clavier n’apparait pas, il faudrait rajouter également un petit control permettant la copie.
Cela reste une alternative très intéressante néanmoins (merci à Rudy pour m’avoir rafraichit la mémoire à ce sujet).

http://msdn.microsoft.com/en-us/library/system.windows.clipboard(v=vs.96).aspx

Transformation en control

Pour que notre contrôle soit facilement portable sur différentes applications on va créer un control que l’on nommera TextSelector.
Premièrement un fichier Generic.xaml, qu’il suffira de deposer dans un dossier Theme de notre application si il n’existe pas, permettra de stocker le style du control.
Puis un fichier TextSelector.cs qui contiendra tout le coté programmatique et pourra être positionner n’importe ou à condition qu’il reste dans le même projet que Generic.xaml.

Le control en lui même sera appelé de la manière suivante à partit du namespace xmlns:my= »clr-namespace:SamSoft.Controls » :

<my:TextSelector Text="Ceci est un TextSelector !" AutoSelectAll="True"/>

Les deux fichiers peuvent être téléchargés ici : TextSelector.cs, Generic.xaml

Le résultat !

Une petite vidéo pour constater le résultat :





Une réponse à Un TextBlock sélectionnable pour WP

  1.  

leave your comment