Je sais beaucoup trop tard, mais pour les futurs publics:
Cette solution utilise le modèle de contrôle de la zone de texte original et ajoute une fonctionnalité qui remplace la valeur de la propriété Text avec la propriété Tag de la zone de texte lorsque Le TextBox texte est vide. C'est un style assez complexe mais ça marche! (Sans code derrière !!):
<Style x:Key="TextBoxPlaceHolder" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Microsoft_Windows_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Text">
<Setter.Value>
<MultiBinding Converter="{StaticResource TextBoxPlaceHolderConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Text" />
<Binding RelativeSource="{RelativeSource Self}" Path="Tag" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Text">
<Setter.Value>
<MultiBinding Converter="{StaticResource TextBoxPlaceHolderConverter}" ConverterParameter="True">
<Binding RelativeSource="{RelativeSource Self}" Path="Text" />
<Binding RelativeSource="{RelativeSource Self}" Path="Tag" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource StringsEqualMultiConverter}" Mode="OneWay">
<Binding RelativeSource="{RelativeSource Self}" Path="Text" />
<Binding RelativeSource="{RelativeSource Self}" Path="Tag" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsFocused}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="#FF7C7C80"/>
<Setter Property="FontStyle" Value="Italic"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Et Les deux convertisseurs fournis:
public class TextBoxPlaceHolderConverter : IMultiValueConverter
{
private static object s_OriginalTag;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
s_OriginalTag = values[1];
var actualText = values[0] as string;
var textToReturn = actualText ?? string.Empty;
var tagText = values[1] is string ? values[1] as string : null;
if (!(parameter is string && parameter.ToString() == "True"))
{
if (actualText != null && tagText != null)
{
if (actualText.Length == 0) // no text
{
textToReturn = tagText;
}
else
{
textToReturn = actualText;
}
}
}
else
{
if (actualText == tagText)
{
textToReturn = string.Empty;
}
}
return textToReturn;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
object[] toReturn = new object[2];
toReturn[0] = value;
toReturn[1] = s_OriginalTag;
return toReturn;
}
}
public class StringsEqualMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var stringToCompare = string.Empty;
if (values != null)
{
if (values.Length > 0 && values[0] is string)
{
stringToCompare = values[0] as string;
}
}
var boolToReturn = values.Aggregate(true, (current, value) => current && (value is string && value.ToString().Equals(stringToCompare)));
return boolToReturn;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Simple et efficace! Merci, exactement ce que je cherchais! – fra