xr280xr
xr280xr

Reputation: 13302

Ampersand in WPF Binding StringFormat

I'm trying to create a string format that has a literal string containing an ampersand. I've tried & and & amp; (without the space) but both cause an exception in the VS2010 designer. Here's what I'm working with:

Text="{Binding Path=LearningOpportunities, StringFormat='Sharing & Learning Opportunitites:\{0\}'}"

If I use '&', it tells me "Entity references or sequences beginning with an ampersand '&' must be terminated with a semicolon ';'". If I use & amp;, it gets a FormatException: "Index (zero based) must be greater than or equal to zero and less than the size of the argument list." I've been doing web development away from WPF for a while so I'm feeling a little rusty. How can I get this working?

Edit: There's been a lot of speculation regarding escaping being the culprit and from what I can tell, it's all incorrect. So to get that out of the way here's what works and doesn't work, taking the ampersand out of the equation:

Formats that work:

StringFormat=Sharing and learning opportunitites:\{0\}
StringFormat='Sharing and learning opportunitites:\{0\}'
StringFormat={}Sharing and learning opportunitites:{0}
StringFormat='{}Sharing and learning opportunitites:{0}'
StringFormat=Sharing and learning opportunitites:{0}
StringFormat='Sharing and learning opportunitites:{0}'

Formats that don't work:

StringFormat=Sharing and learning opportunitites:{}{0}
StringFormat=Sharing and learning opportunitites:{{0}}  (Outputs literal "{0}")

So to summarize, you can escape all braces in the string by placing opening and closing braces {} at the beginning of the string, or you can escape individual braces using backslashes. Surprisingly, it still worked without escaping the braces, but the syntax coloring was pretty ugly in Visual Studio. To avoid ugly syntax coloring, you can use single quotes around the format string.

So with escaping put to rest, the problem is that adding an ampersand to the format string causes an exception, whether it is escaped or not. The only difference between escaped and not escaped is it gives a different exception.

Upvotes: 2

Views: 4262

Answers (3)

JerKimball
JerKimball

Reputation: 16914

EDITEDITEDIT:

Aha! Turns out to be a bloody bug with the Cider design surface!

Proof:

Try this sequence of XAML lines:

<StackPanel>
    <!-- should show '$YoMamma' -->
    <TextBlock Text="{Binding Path=Value, StringFormat=&#x24;{0}}"/>
    <!-- should show '%YoMamma' -->
    <TextBlock Text="{Binding Path=Value, StringFormat=&#x25;{0}}"/>
    <!-- should show '&YoMamma', but crashes the designer -->
    <!--<TextBlock Text="{Binding Path=Value, StringFormat=&#x26;{0}}"/>-->
    <!-- should show '"YoMamma', but crashes the designer -->
    <!--<TextBlock Text="{Binding Path=Value, StringFormat=&#x27;{0}}"/>-->
    <!-- should show '(YoMamma' -->
    <TextBlock Text="{Binding Path=Value, StringFormat=&#x28;{0}}"/>
    <!-- should show ')YoMamma' -->
    <TextBlock Text="{Binding Path=Value, StringFormat=&#x29;{0}}"/>

</StackPanel>

I submitted a bug report to connect, we'll see if anyone responds: https://connect.microsoft.com/VisualStudio/feedback/details/782059/cider-vs2010-designer-bug-with-binding-using-escaped-entities-in-stringformat

Rest of this answer is semi-moot, albeit potentially useful, since the "bug" is on the designer.

The thing to remember here is that XAML is XML, so you need to encode ampersands accordingly:

&amp;

should work, as well as:

&#38;

EDIT:

Ah, yes - so as discussed in the back-and-forth in the comments, the problem lay not with the ampersand per se, but the "escaping" of the replacement markers within the surrounding braces of a Binding - to fix this, you've actually got three options:

EDIT 2: Bah, I think markdown might not like my post (and I was wrong on one point anyway) - let's see if I can cobble a full example together:

Just in case, here's a pastebin link as well: http://pastebin.com/yfrpvxs1

So say we've got a context object like so:

public class Foo : INotifyPropertyChanged
{
    private string _value;
    public string Value
    {
        get { return _value; }
        set { _value = value; FireNpc("Value"); }
    }
    private decimal _numberValue;
    public decimal NumberValue
    {
        get { return _numberValue; }
        set { _numberValue = value; FireNpc("NumberValue"); }
    }
    private DateTime _dateValue;
    public DateTime DateValue
    {
        get { return _dateValue; }
        set { _dateValue = value; FireNpc("DateValue"); }
    }
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void FireNpc(string name)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

And a window codebehind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new Foo()
            {
                Value = "YoMamma", 
                DateValue = DateTime.Now, 
                NumberValue = 3.14m
            };
    }
}

Let's XAML!

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <!-- should show '#1:Foo & Bar:YoMamma' -->
        <TextBlock Text="{Binding Path=Value, StringFormat=#1:Foo &amp; Bar:{0}}"/>

        <!-- should show '#2:Foo & Bar:YoMamma' -->
        <TextBlock>
            <TextBlock.Text>
                <Binding Path="Value" StringFormat="#2:Foo &amp; Bar:{0}"/>
            </TextBlock.Text>
        </TextBlock>

        <!-- will actually show the '{' and '}', so 'Foo & Bar:{0}' -->
        <TextBlock Text="{Binding Path=Value, StringFormat=Foo &amp; Bar:{{0}}}"/>

        <!-- default 'custom' (there's a fun oxymoron) format - should be '$3.14' -->
        <TextBlock Text="{Binding Path=NumberValue, StringFormat=c}"/>

        <!-- custom 'custom' (bear with me) format -->
        <TextBlock Text="{Binding Path=DateValue, StringFormat=MM/dd/yyyy}"/>

        <!-- multi parameter formatting-->
        <TextBlock>
            <TextBlock.Text>
                <MultiBinding StringFormat="As of {2:MM/dd/yyyy}, {0} cost {1:c}">
                    <Binding Path="Value"/>
                    <Binding Path="NumberValue"/>
                    <Binding Path="DateValue"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </StackPanel>
</Window>

Now, piggybacking on that MultiBinding, we can do something stupid - let's add this to our context:

    // Heh...don't actually do this, naturally...
    private string _ampersandValue;
    public string AmpersandValue
    {
        get { return _ampersandValue; }
        set { _ampersandValue = value; FireNpc("AmpersandValue"); }
    }


    this.DataContext = new Foo()
        {
            Value = "YoMamma", 
            DateValue = DateTime.Now, 
            NumberValue = 3.14m,
            AmpersandValue = "&"
        };

And add this XAML:

    <!-- And a crazy-person's method, using multi parameter formatting-->
    <TextBlock>
        <TextBlock.Text>
            <MultiBinding StringFormat="{}{0} {1} {0} = {0}">
                <Binding Path="Value"/>
                <Binding Path="AmpersandValue"/>
            </MultiBinding>
        </TextBlock.Text>
    </TextBlock>

Upvotes: 2

Rachel
Rachel

Reputation: 132588

Anytime I want to use StringFormat in the binding, and my format contains additional text or spaces, I put an extra set of curley braces at the front like this:

StringFormat={}Sharing and learning opportunitites:{0}

I know I've seen references online using other methods, but they've all failed me at one time or another. I think different versions of WPF support different StringFormat syntaxes, so depending on what version you're using, other syntax may work as well, however the above is the only one I've found to be fairly universal.

Also, you'll want to use &amp; to escape the & character

StringFormat={}Sharing &amp; learning opportunitites:{0}

Upvotes: 0

Reed Copsey
Reed Copsey

Reputation: 564631

You need to use &amp; for the ampersand, and remove the \ which are acting as escape characters. You also have extra ' characters which aren't required. Try the following:

Text="{Binding Path=LearningOpportunities, StringFormat=Sharing &amp; Learning Opportunitites:{0}}"

Upvotes: 0

Related Questions