Default Constructor Parameter in MarkupExtension declaration
Asked Answered
P

1

13

Reducing this question to the bare minimum, consider this MarkupExtension class...

public class ProblemStatement : MarkupExtension
{
    private readonly string _first;
    private readonly string _second;
    public ProblemStatement(string first, string second)
    {
        _first = first;
        _second = second;
    }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
    public override string ToString()
    {
        return _first + _second;
    }
}

When this Xaml is declared...

<Grid>
    <TextBlock Name="TextBlock1" Tag="{so:ProblemStatement 'hello', 'world'}"/>
    <TextBlock Text="{Binding ElementName=TextBlock1, Path=Tag}"/>
</Grid>

...you see 'helloworld' in the TextBlock as expected. All's well to this point.

But changing the constructor parameter to this...

public ProblemStatement(string first, string second = "nothing")

...and the relevant Xaml to this...

   <Grid>
        <TextBlock Name="TextBlock1" Tag="{so:ProblemStatement 'hello'}"/>
        <TextBlock Text="{Binding ElementName=TextBlock1, Path=Tag}"/>
    </Grid>

the resulting error message is...

No constructor for type 'ProblemStatement' has 1 parameters.

There is a work-around, which is to chain the constructor by adding this statement to the class...

public ProblemStatement(string first) : this(first, "not provided") { }

and this will show 'hellonot provided' in the TextBlock. However, this also changes the semantics of the MarkupExtension and is not desirable in the larger, 'real-world' case. Also the complexity of overloading increases dramatically when more complex types are used or the constructor arguments are of type 'dynamic'. Also, for example, use of the new 'Caller Information' attributes is blocked altogether.

So the question is: how to declare the Xaml so that the Xaml parser will honour a default constructor argument?

Phylum answered 17/5, 2014 at 13:36 Comment(0)
M
11

Try this out:

    public string Optional{ get; set; } = "DefaultValue";

    private readonly string _mandatory;

    public ProblemStatement(string mandatory)
    {
        _mandatory = mandatory;
    }

Usage:

<TextBlock Name="TextBlock1" Tag="{local:ProblemStatement 'hello', Optional=NotDefault}"/>

Alternative:

<TextBlock Name="TextBlock1" Tag="{local:ProblemStatement 'hello'}"/>

Result:

  • No XAML parsing errors
  • No need to overload the constructor for optional parameters
  • Mandatory parameters are constructor parameters.
  • Optional parameters are properties.
Myrticemyrtie answered 23/6, 2016 at 20:56 Comment(2)
What benefit does ConstructorArgumentAttribute provide in this case since the property named Optional isn't actually a constructor argument? I thought ConstructorArgumentAttribute was intended to better inform XAML Serialization about the relationship between an actual constructor arg and a property.Evertor
@AdamCaviness There is none. At the time I had the wrong impression of this attribute. answer edited accordingly.Myrticemyrtie

© 2022 - 2024 — McMap. All rights reserved.