Visually Located

XAML and GIS

Creating a Behavior to change text to any case

In a previous post we created a behavior that would capitalize any text. This Behavior served a great purpose allowing you to capitalize the text for any TextBlock. What happens when you need to lower case the text? You could create a second Behavior for this, or we could modify the Behavior so that it allows for multiple options. The first option just seems silly when we can reuse code, but how can we specify which text case we want to apply?

In the previous post I talked about some reasons why I like behaviors. One reason I did not list is that you can add Dependency Properties to them. Dependency Properties allow us to set properties in xaml or even better bind to other properties. You can add Dependency Properties to value converters, but that requires your value converter to inherit from DependencyObject. A Behavior already is a DependencyObject!

We’ll start with the ToUpperBehavior from before. We’ll first change the name to ChangeCaseBehavior and we’ll create an enum that will specify what case style we want to change to

public class ChangeCaseBehavior : Behavior<TextBlock>
{
    ...
}
 
public enum TextCase
{
    Upper,
    Lower,
    Title
}

This example will show three styles of text casing. I’ll leave it up to you to add others like camelCase or PascalCase. In our previous Behavior, we called ToUpper on the Text of the TextBlock, now we’ll want to respond to the different possibilities for TextCase. We can accomplish this with a simple switch statement within the UpdateText method

private void UpdateText()
{
    if (AssociatedObject == null) return;
    if (string.IsNullOrEmpty(AssociatedObject.Text)) return;
 
    switch (TextCase)
    {
        case TextCase.Upper:
            AssociatedObject.Text = AssociatedObject.Text.ToUpper();
            break;
        case TextCase.Lower:
            AssociatedObject.Text = AssociatedObject.Text.ToLower();
            break;
        case TextCase.Title:
            char[] text = AssociatedObject.Text.ToCharArray();
            
            // always upper case the first letter
            bool shouldUpper = true;
            for (int i = 0; i < text.Length; i++)
            {
                text[i] = shouldUpper 
                    ? Char.ToUpper(text[i]) 
                    : Char.ToLower(text[i]);
 
                // next letter should be upper case if this is a space
                shouldUpper = text[i] == ' ';
            }
            AssociatedObject.Text = new string(text);
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
    AssociatedObject.LayoutUpdated -= AssociatedObjectOnLayoutUpdated;
}

Next,  we’ll want the ability to set which text casing we want. To do this we’ll add a dependency property to the behavior and respond to the changing of the value.

public TextCase TextCase
{
    get { return (TextCase)GetValue(TextCaseProperty); }
    set { SetValue(TextCaseProperty, value); }
}
 
// Using a DependencyProperty as the backing store for TextCase.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextCaseProperty = DependencyProperty.Register(
    "TextCase",
    typeof(TextCase),
    typeof(ChangeCaseBehavior),
    new PropertyMetadata(TextCase.Upper, OnTextCaseChanged));
 
private static void OnTextCaseChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var behavior = (ChangeCaseBehavior)o;
    behavior.UpdateText();
}

Last time we setup the Behavior through Blend, this time we’ll modify the xaml to specify which case we want.

<TextBlock x:Name="TitleText" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0" 
           Text="{Binding Name}">
    <i:Interaction.Behaviors>
        <Behaviors:ChangeCaseBehavior TextCase="Title"/>
    </i:Interaction.Behaviors>
</TextBlock>

This ensures that our text will be title cased

image

blog comments powered by Disqus