Reputation: 3858
I would like to create a simple designer which looks like visual studio. Specifically, I would like my items to have the same behavior as in VS: when they're not selected, a simple label/textblock is shown, when they're selected a textbox lets me edit the value. Which is the best way to achieve this?
Thank you
Upvotes: 3
Views: 994
Reputation: 4481
I Know much too late but for future audiences:
This solution use the original TextBox control template and adds a functionality which replaces the Text property value with the TextBox's Tag property when The TextBox Text is Empty. It's pretty complex styling but it works! (without any code behind!!):
<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>
And The two provided converters:
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();
}
}
Upvotes: 0
Reputation: 21873
create a control template for the TextBox and change the appearance of the TextBox as you like when the control is focused or it has content inside.
Upvotes: 4