Visually Located

XAML and GIS

Creating a behavior to capitalize text

Many apps get data that they show from services. These services generally have their data cased a certain way. If you want to navigate to a page to show data, you may want to have the title of the page be information from the service.

image

The title portion of the page tells the user that this is the profile for Shawn Kendrot. The text for “Shawn Kendrot” came from the service and is cased in Title Case. But if you wanted to follow design guidelines (which are not a requirement), you may want the name to be all upper case.

To accomplish this you have three options, convert the text when you download it, create a value converter, or create a behavior. The first is really not an option, because that means that you can no longer use that text for anything else. Value converters are nice, easy to use, and sometimes overused. Behaviors are nice because they can easily be used within the designer of Blend. If you are not familiar with Blend, you should find time to use it. It is the tool to use when designing your apps.

The thing I love about working with behaviors is that they have a reference to the DependencyObject that they are associate with. They expose this object through the AssociatedObject property. You define the type of the associated object through the generic parameter of the abstract Behavior class

public class ToUpperBehavior : Behavior<TextBlock>
{
}

The other thing I like about behaviors is that you define when they should do work. The Behavior class has two methods that you can override that allow you to specify when you want the behavior to capitalize text. The OnAttached method fires when the AssociatedObject has been attached. It allows you to subscribe to any events of the associated object, or to make any changes that are needed. The OnDetached method fires when the AssociatedObject is being detached. It allows you to unsubscribe from any events that you subscribed to.

protected override void OnAttached()
{
    base.OnAttached();
 
    AssociatedObject.Loaded += AssociatedObjectOnLoaded;
    AssociatedObject.LayoutUpdated += AssociatedObjectOnLayoutUpdated;
}
 
protected override void OnDetaching()
{
    if (AssociatedObject != null)
    {
        AssociatedObject.Loaded -= AssociatedObjectOnLoaded;
        AssociatedObject.LayoutUpdated -= AssociatedObjectOnLayoutUpdated;
    }
    base.OnDetaching();
}

We’ll subscribe to the Loaded and the LayoutUpdated event of the TextBlock. If you already have the data downloaded, you should only need the Loaded event. If you still need to get the data, the LayoutUpdated event let’s us know when the data comes in. When we have text in the TextBlock, we want to unsubscribe from the LayoutUpdated event so we stop getting updates (assuming we don’t need them any more) and to ensure we don’t leak memory through the event.

private void AssociatedObjectOnLayoutUpdated(object sender, EventArgs eventArgs)
{
    UpdateText();
}
 
private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    UpdateText();
    AssociatedObject.Loaded -= AssociatedObjectOnLoaded;
}
 
private void UpdateText()
{
    if (AssociatedObject == null) return;
    if (string.IsNullOrEmpty(AssociatedObject.Text)) return;
 
    AssociatedObject.Text = AssociatedObject.Text.ToUpper();
    AssociatedObject.LayoutUpdated -= AssociatedObjectOnLayoutUpdated;
}

Now, we can drag and drop the behavior within Blend onto the title TextBlock.

image

And we end up with text that has been capitalized.

image

Increasing productivity with ScriptCS

I’ve been working a lot with ScriptCS lately. It’s amazing how productive you can be working with this tool. There are so many times in the past when I would spin up a new project (or reuse an existing one) to test out some algorithm or small piece of code. Usually these projects are console applications that spit out some information at various times. This was the approach I took to test the performance of some IEnumerable extension methods. Using ScriptCS, I am able to remove the need for the extra project file, perform a quick test and then continue on with my development.

Most of the standard namespaces are already included (System, System.Collections.Generic, System.Linq, System.Text, System.Threading.Tasks, System.IO), but there are times when you will need to include the namespace of the class you are using (example: System.Diagnostics.Process class).

image

You can add a using statement if you will be using the namespace a lot.

image

You can also reference your own assemblies.

image

You can build loops within the script.

image

You can even create classes.

image

The above examples can even be pasted into the console! Just make sure that starting { brackets are on the same line as the loop/class/etc.

When you want to extend it even more you can write Script Pack.