How do I define a constant in C# and use it in XAML?

Viewed 1159

I try to define constants for string values whenever necessary to avoid burying "magic strings" in the code. I recently found myself needing to define a custom format string for a converter, so I defined public const string MyFormat = "a"; in the converter. When using the converter, I need to pass in "a" as the ConverterParameter, but rather than typing ConverterParameter=a, I would like to refer to the named constant.

<TextBlock Text="{x:Bind SomeProperty, Converter={StaticResource MyConverter}, ConverterParameter=???}" />

I've tried ConverterParameter=local:MyConverter.MyFormat, but it just passes in the string "local:MyConverter.MyFormat" rather than "a".

I've tried ConverterParameter={x:Bind local:MyConverter.MyFormat} but I get an error Nested x:Bind expressions are not supported.

I've tried ConverterParameter={StaticResource MyFormat} and adding it as a resource, but that just moves the problem and introduces a new problem:

  1. I don't know how to reference the constant in the resources either. I can add <x:String x:Key="MyFormat">a</x:String>, but then I've defined the constant in two different places and if it changes I have to remember to update it in two different places.
  2. Even if I do this, the string is not passed as the parameter as expected. The parameter is null.

Is there any way to refer to a named constant in XAML? (Note: this is UWP, not WPF.)

Here's a simple repro:

MainPage.xaml

<Page
    x:Class="XamlConstantReference.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:XamlConstantReference"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">
    <Page.Resources>
        <local:MyConverter x:Key="MyConverter" />
        <!--  How do I refer to MyConverter.MyFormat rather than defining it a second time?  -->
        <x:String x:Key="MyFormat">a</x:String>
    </Page.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--  How do I refer to MyConverter.MyFormat rather than passing in ConverterParameter=a?  -->
        <TextBlock Grid.Row="0" Text="{x:Bind SomeProperty, Converter={StaticResource MyConverter}, ConverterParameter=a}" />
        <TextBlock Grid.Row="1" Text="{x:Bind SomeProperty, Converter={StaticResource MyConverter}, ConverterParameter=local:MyConverter.MyFormat}" />
        <!-- <TextBlock Grid.Row="2" Text="{x:Bind SomeProperty, Converter={StaticResource MyConverter}, ConverterParameter={x:Bind local:MyConverter.MyFormat}}" /> -->
        <TextBlock Grid.Row="3" Text="{x:Bind SomeProperty, Converter={StaticResource MyConverter}, ConverterParameter={StaticResource MyFormat}}" />
    </Grid>
</Page>

MainPage.xaml.cs

using System.ComponentModel;
using Windows.UI.Xaml.Controls;

namespace XamlConstantReference
{
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _someProperty = "Hello World!";

        public string SomeProperty
        {
            get => _someProperty;
            set
            {
                if (_someProperty != value)
                {
                    _someProperty = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeProperty)));
                }
            }
        }

        public MainPage()
        {
            InitializeComponent();
        }
    }
}

MyConverter.cs

using System;
using Windows.UI.Xaml.Data;

namespace XamlConstantReference
{
    public class MyConverter : IValueConverter
    {
        public const string MyFormat = "a";

        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if (parameter == null)
            {
                return $"Fail. Formatted '{value}' with null parameter.";
            }
            else if (parameter is string parameterString)
            {
                switch (parameterString)
                {
                    case MyFormat:
                        return $"Success! Formatted '{value}' with parameter '{parameterString}'.";

                    default:
                        return $"Fail. Formatted '{value}' with parameter '{parameterString}'.";
                }
            }

            throw new ArgumentException();
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}
0 Answers
Related