juil.162010

[WPF] Avoir un bouton sans bordure tout en gardant la “zone cliquable” rectangulaire

Published by Pierrick Martos at 14:33 under WPF

Dans les problèmes récurent lors du développement d’application de type WPF, c’est d’avoir une zone cliquable rectangulaire sur un Textblock, un path…

La première solution à laquelle on pense c’est d’utiliser un bouton qui va englober notre textblock, image, ou même un path. Malheureusement, avec le template par défaut du bouton, le rendu final n’est souvent pas terrible : focus bleu ciel, bordure chromé…

Prenons un exemple un peu plus concret. J’ai créé un path qui forme une “croix” et qui sert à fermer mon application WPF, afin de ne pas restreindre la zone de clic à la croix en elle même, je décide d’englober mon path dans un bouton. Pour des raisons d’esthétiques, je décide de garder seulement la croix à l’affichage, typiquement,  je supprime le fond du bouton et sa bordure :

  1: <Button Click="Button_Click_1" Margin="2,0" Padding="0" Cursor="Hand" Background="{x:Null}" BorderBrush="{x:Null}">
  2: 	<Path Cursor="Hand" Data="M739.46579,11.088206 L739.46582,21.949591 748.81644,21.952562 748.81604,11.088299 z" HorizontalAlignment="Right" Height="9.921" Margin="0" Stretch="Fill" Stroke="White" StrokeThickness="2" VerticalAlignment="Center" Width="10">
  3: 	    <Path.Effect>
  4: 	        <DropShadowEffect ShadowDepth="2" Direction="-80"/>
  5: 	    </Path.Effect>
  6: 	</Path>
  7: </Button>

Voici le rendu obtenu :

image

Même en assignant aucun background ni borderBrush, on se retrouve avec un bouton comprenant bien le path (la croix) mais on a toujours une bordure chromé. Cette bordure provient de la classe ButtonChrome qui compose par défaut le template d’un bouton WPF.

La solution pour supprimer cette bordure est d’éditer le template du bouton et de supprimer le “ButtonChrome”. Voici le template par défaut d’un bouton WPF :

  1: <ControlTemplate TargetType="{x:Type Button}">
  2: 	<Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}" SnapsToDevicePixels="true">
  3: 		<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  4: 	</Microsoft_Windows_Themes:ButtonChrome>
  5: 	<ControlTemplate.Triggers>
  6: 		<Trigger Property="IsKeyboardFocused" Value="true">
  7: 			<Setter Property="RenderDefaulted" TargetName="Chrome" Value="true"/>
  8: 		</Trigger>
  9: 		<Trigger Property="ToggleButton.IsChecked" Value="true">
 10: 			<Setter Property="RenderPressed" TargetName="Chrome" Value="true"/>
 11: 		</Trigger>
 12: 		<Trigger Property="IsEnabled" Value="false">
 13: 			<Setter Property="Foreground" Value="#ADADAD"/>
 14: 		</Trigger>
 15: 	</ControlTemplate.Triggers>
 16: </ControlTemplate>

 

Sur la ligne 2 et 3 vous pouvez voir notre fameux ButtonChrome. On va le supprime pour obtenir ceci :

  1: <ControlTemplate TargetType="{x:Type Button}">
  2: 	<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  3: 	<ControlTemplate.Triggers>
  4: 		<Trigger Property="IsEnabled" Value="false">
  5: 			<Setter Property="Foreground" Value="#ADADAD"/>
  6: 		</Trigger>
  7: 	</ControlTemplate.Triggers>
  8: </ControlTemplate>

 

Maintenant, on a bien notre bouton sans aucune bordure :

image

Seulement voilà, la zone cliquable à la souris ne corresponds plus au bouton rectangulaire, l’utilisateur ne pourra cliquer que sur la zone en jaune sur l’image ci-dessous :

image 

Comment réussir à garder la zone cliquable sur tout notre bouton, sans pour autant garder la bordure chromé ?

La solution c’est d’englober le ContentPresenter par un Border avec un fond transparent ! J’insiste sur le fond transparent, si vous mettez simplement un border sans “Background”, la zone cliquable sera toujours celle du path. Voici le XAML du template de notre bouton :

  1: <ControlTemplate TargetType="{x:Type Button}">
  2: 	<Border Background="#00000000" >
  3: 		<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  4: 	</Border>
  5: 	<ControlTemplate.Triggers>
  6: 		<Trigger Property="IsEnabled" Value="false">
  7: 			<Setter Property="Foreground" Value="#ADADAD"/>
  8: 		</Trigger>
  9: 	</ControlTemplate.Triggers>
 10: </ControlTemplate>

 

Le tour est joué ! Vous avez un Path en forme de croix et une zone cliquable rectangulaire qui evitera à votre utilisateur final de passer 3 secondes pour bien positionner sa souris et effectuer son action :)



[KickIt] [Dzone] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Tags: , ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses

Ajouter un commentaire


(Affichera votre icône Gravatar)

  Country flag

biuquote
  • Commentaire
  • Aperçu immédiat
Loading