18

I want all characters in a TextBlock to be displayed in uppercase

<TextBlock Name="tbAbc"
           FontSize="12"
           TextAlignment="Center"
           Text="Channel Name"
           Foreground="{DynamicResource {x:Static r:RibbonSkinResources.RibbonGroupLabelFontColorBrushKey}}" />

The strings are taken through Binding. I don't want to make the strings uppercase in the dictionary itself.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
feralbino
  • 181
  • 1
  • 1
  • 3
  • see [C# string format flag or modifier to lowercase param](http://stackoverflow.com/questions/1839649/c-sharp-string-format-flag-or-modifier-to-lowercase-param) – pushpraj Jul 25 '14 at 12:59
  • Possible duplicate of [WPF/XAML: how to make all text upper case / capital?](http://stackoverflow.com/questions/1762485/wpf-xaml-how-to-make-all-text-upper-case-capital) – Alias Varghese Apr 21 '16 at 07:05

6 Answers6

47

Or use

Typography.Capitals="AllSmallCaps"

in your TextBlock definition.

See here: MSDN - Typography.Capitals

EDIT:

This does not work in Windows Phone 8.1, only in Windows 8.1 ...

TheEye
  • 9,280
  • 2
  • 42
  • 58
17

Implement a custom converter.

using System.Globalization;
using System.Windows.Data;
// ...
public class StringToUpperConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value is string )
        {
            return ((string)value).ToUpper();
        }

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

Then include that in your XAML as a resource:

<local:StringToUpperConverter  x:Key="StringToUpperConverter"/>

And add it to your binding:

Converter={StaticResource StringToUpperConverter}
Vimes
  • 10,577
  • 17
  • 66
  • 86
kidshaw
  • 3,423
  • 2
  • 16
  • 28
  • 1
    You may not want to use "value is string" in there. Try if (value != null) { return value.ToString().ToUpper(culture); } – brenth Oct 04 '19 at 16:50
  • @brenth - I prefer converters that work with a specific type only. If it doesn't get that type, return the value untouched. That might be a little cautious when it expects a string, but this is the pattern I prefer. Not that your suggestion wouldn't work perfectly well. – kidshaw Oct 14 '19 at 13:23
  • 1
    Just a friendly heads-up. If `value` is `null`, then `value is string` will equate to false so your null check can be removed. You can also remove your cast and use the following syntax instead: `if(value is string stringValue){ return stringValue.ToUpper(); }`. I've also added my own answer that uses a single-line null-coalescing approach as well as making the converter a subclass of `MarkupExtension` so you don't need to add your converter to your resources. You can just use it directly as needed right in the binding. Feel free to check it out! :) – Mark A. Donohoe Feb 28 '21 at 03:18
  • @MarkA.Donohoe - always appreciate friendly improvements, and will take a look at markup extensions for future applications also. Thanks for the info and +1 on your answer also. – kidshaw Mar 01 '21 at 11:05
9

You can use an attached property like this:

public static class TextBlock
{
    public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.RegisterAttached(
        "CharacterCasing",
        typeof(CharacterCasing),
        typeof(TextBlock),
        new FrameworkPropertyMetadata(
            CharacterCasing.Normal,
            FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.NotDataBindable,
            OnCharacterCasingChanged));

    private static readonly DependencyProperty TextProxyProperty = DependencyProperty.RegisterAttached(
        "TextProxy",
        typeof(string),
        typeof(TextBlock),
        new PropertyMetadata(default(string), OnTextProxyChanged));

    private static readonly PropertyPath TextPropertyPath = new PropertyPath("Text");


    public static void SetCharacterCasing(DependencyObject element, CharacterCasing value)
    {
        element.SetValue(CharacterCasingProperty, value);
    }

    public static CharacterCasing GetCharacterCasing(DependencyObject element)
    {
        return (CharacterCasing)element.GetValue(CharacterCasingProperty);
    }

    private static void OnCharacterCasingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is System.Windows.Controls.TextBlock textBlock)
        {
            if (BindingOperations.GetBinding(textBlock, TextProxyProperty) == null)
            {
                BindingOperations.SetBinding(
                    textBlock,
                    TextProxyProperty,
                    new Binding
                    {
                        Path = TextPropertyPath,
                        RelativeSource = RelativeSource.Self,
                        Mode = BindingMode.OneWay,
                    });
            }
        }
    }

    private static void OnTextProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.SetCurrentValue(System.Windows.Controls.TextBlock.TextProperty, Format((string)e.NewValue, GetCharacterCasing(d)));

        string Format(string text, CharacterCasing casing)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            switch (casing)
            {
                case CharacterCasing.Normal:
                    return text;
                case CharacterCasing.Lower:
                    return text.ToLower();
                case CharacterCasing.Upper:
                    return text.ToUpper();
                default:
                    throw new ArgumentOutOfRangeException(nameof(casing), casing, null);
            }
        }
    }
}

Then usage in xaml will look like:

<StackPanel>
    <TextBox x:Name="TextBox" Text="abc" />
    <TextBlock local:TextBlock.CharacterCasing="Upper" Text="abc" />
    <TextBlock local:TextBlock.CharacterCasing="Upper" Text="{Binding ElementName=TextBox, Path=Text}" />
    <Button local:TextBlock.CharacterCasing="Upper" Content="abc" />
    <Button local:TextBlock.CharacterCasing="Upper" Content="{Binding ElementName=TextBox, Path=Text}" />
</StackPanel>
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
  • This is a great solution if the textblock in question is already using a converter. – goldenratio Apr 10 '18 at 21:32
  • Great solution as it doesn't imply you to change the content of the element. – Davide Cannizzo Oct 16 '18 at 17:55
  • This is a great solution because it can solely be controlled via external styles/property setters: – Alexander Gräf Oct 28 '19 at 20:13
  • BTW, you don't have to nest your `if` statements. You can use an `&&` operator to first check if it's a `TextBlock` and if so, check its text binding. Also, in newer versions of C#, they recommend using pattern matching against null instead of equality checking. (i.e. instead of `if(bla == null){ ... }` they suggest `if(bla is null){ ... }` (or `is not null` if that's what you need). Not sure if it's just for readability or if it even alters the IL, but that seems to be the direction of all their newest/latest code. – Mark A. Donohoe Feb 28 '21 at 03:29
4

If it's not a big deal you could use TextBox instead of TextBlock like this:

<TextBox CharacterCasing="Upper" IsReadOnly="True" />
RazvanR
  • 412
  • 5
  • 14
  • 6
    It's not really an acceptable answer though because the question asks about TextBlock. – Stephen Drew Sep 25 '14 at 14:12
  • 2
    @StephenDrew I was **not** asking him to accept my answer, just letting him know that he should accept one ( the one that helped him the most - that's all about this site, right?). In my opinion, the one with the converter is a good answer. (mine is just an alternative). – RazvanR Oct 07 '14 at 10:58
  • 3
    On TextBox, setting the character casing property to true only affects manually typed text. So setting IsReadOnly to true at the same time eliminates the CharacterCasing properties usefulness to this particular use-case. I didn't know the CharacterCasing property existed on TextBox though so I learned something here! – HDL_CinC_Dragon Sep 15 '16 at 18:30
  • 1
    @HDL_CinC_Dragon is correct, CharacterCasing="Upper" on the TextBox element will only apply to newly typed text into the element. It won't apply it to an already existing "text" value unfortunately – EdwardM May 15 '18 at 20:54
4

While there's already a great answer here that uses a converter, I'm providing an alternative implementation that simplifies the conversion to a single line (thanks to null coalescing), as well as making it a subclass of MarkupExtension so it's easier to use in XAML.

Here's the converter...

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace IntuoSoft.Wpf.Converters {

    [ValueConversion(typeof(string), typeof(string))]
    public class CapitalizationConverter : MarkupExtension, IValueConverter {

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            => (value as string)?.ToUpper() ?? value; // If it's a string, call ToUpper(), otherwise, pass it through as-is.

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            => throw new NotSupportedException();

        public override object ProvideValue(IServiceProvider serviceProvider) => this;
    }
}

And here's how you use it (Note: This assumes the above namespace is prefixed with is in your XAML):

<TextBlock Text={Binding SomeValue, Converter={is:CapitalizationConverter}}" />

Because it's a MarkupExtension subclass, you can simply use it right where/when it's needed. No need to define it in the resources first.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
2

I use a character casing value converter:

class CharacterCasingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var s = value as string;
        if (s == null)
            return value;

        CharacterCasing casing;
        if (!Enum.TryParse(parameter as string, out casing))
            casing = CharacterCasing.Upper;

        switch (casing)
        {
            case CharacterCasing.Lower:
                return s.ToLower(culture);
            case CharacterCasing.Upper:
                return s.ToUpper(culture);
            default:
                return s;
        }
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
Stephen Drew
  • 1,415
  • 19
  • 31