WPF/XAML:如何使TextBlock中的所有文本都变成大写?

18

我希望将TextBlock中的所有字符都显示为大写字母

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

字符串通过绑定进行处理。我不想在字典本身中将字符串转换为大写。


6个回答

47

或者使用

Typography.Capitals="AllSmallCaps"

在您的TextBlock定义中。

请参见:MSDN - Typography.Capitals

编辑:

这在Windows Phone 8.1中不起作用,只适用于Windows 8.1...


7
无效,大写字母将变成大写字母,小写字母将变成小型大写字母。 - Wouter
3
使用自定义字体时出现错误。 - Starwave

17

实现自定义转换器。

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;
    }
}

然后将其包含在您的XAML资源中:

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

并将其添加到绑定中:

Converter={StaticResource StringToUpperConverter}

1
你可能不想在那里使用“value is string”。尝试使用以下代码: if (value != null) { return value.ToString().ToUpper(culture); } - brenth
@brenth - 我更喜欢只与特定类型一起使用的转换器。如果它没有得到那种类型,就返回原始值。当它期望一个字符串时,这可能有点谨慎,但这是我喜欢的模式。并不是说你的建议不能完美地工作。 - kidshaw
1
友情提示:如果valuenull,那么value is string将等于false,因此您可以删除空值检查。您还可以删除强制转换并改用以下语法:if(value is string stringValue){ return stringValue.ToUpper(); }。我还添加了自己的答案,使用单行空值合并方法,并使转换器成为MarkupExtension的子类,因此您不需要将转换器添加到资源中。您可以直接在绑定中使用它,随时随地进行检查! :) - Mark A. Donohoe
@MarkA.Donohoe - 非常感谢友好的改进,我也会考虑在未来的应用中使用标记扩展。感谢您提供的信息,对您的答案点赞。 - kidshaw

10
您可以像这样使用附加属性:

您可以像这样使用附加属性:

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);
            }
        }
    }
}

然后在XAML中使用的样子如下:
<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>

如果待处理的文本块已经使用转换器,那么这是一个很好的解决方案。 - goldenratio
很棒的解决方案,因为它不需要改变元素的内容。 - Davide Cannizzo
这是一个很棒的解决方案,因为它可以完全通过外部样式/属性设置器进行控制:<Setter Property="local:TextBlock.CharacterCasing" Value="Upper" />。 - Alexander Gräf
顺便提一下,您不必嵌套 if 语句。 您可以使用 && 运算符先检查它是否为 TextBlock,如果是,则检查其文本绑定。 此外,在较新的 C# 版本中,他们建议针对 null 使用模式匹配而不是等式检查。 (即,他们建议使用 if(bla is null){...} 而不是 if(bla == null){...}(或者如果需要的话是 is not null)。 不确定它是否仅用于可读性,还是甚至会改变 IL,但这似乎是所有最新代码的方向。 - Mark A. Donohoe
出色的解决方案。谢谢。 - undefined

4
如果不是很重要的话,您可以像这样使用TextBox而不是TextBlock
<TextBox CharacterCasing="Upper" IsReadOnly="True" />

1
@user3876923 http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work - RazvanR
6
这个答案并不算是可接受的,因为问题是关于TextBlock的。 - Stephen Drew
2
@StephenDrew 我并不是要求他接受我的答案,只是让他知道他应该接受一个(帮助他最多的那个 - 这就是这个网站的全部意义吧?)。在我看来,带有转换器的那个答案是很好的(我的只是一个替代方案)。 - RazvanR
3
在TextBox中,将字符大小写属性设置为true只影响手动输入的文本。因此,在同时将IsReadOnly设置为true时,会消除CharacterCasing属性在此特定用例中的有用性。然而,我之前不知道TextBox上存在CharacterCasing属性,所以现在学到了新知识! - HDL_CinC_Dragon
1
@HDL_CinC_Dragon 是正确的,TextBox 元素上的 CharacterCasing="Upper" 属性只会应用于元素中新输入的文本。不幸的是,它不会应用于已经存在的“文本”值。 - EdwardM

4

虽然这里已经有一个使用转换器的很好的答案,但我提供了一个替代实现,它简化了转换成一行(由于空合并运算符),并将其作为MarkupExtension的子类,这样在XAML中使用更容易。

这就是转换器...

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;
    }
}

以下是如何使用它的方法(注意:这假定你在 XAML 中将上面的命名空间前缀为 is):

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

由于它是 MarkupExtension 的子类,因此您可以在需要时直接使用它,无需先在资源中定义。


2

我使用字符大小写值转换器:

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();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接