13

この質問を最小限に減らして、この MarkupExtension クラスを検討してください...

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

この Xaml が宣言されると...

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

...期待どおり、TextBlock に「 helloworld 」が表示されます。ここまでは順調です。

しかし、コンストラクターのパラメーターをこれに変更します...

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

...そしてこれに関連するXaml...

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

結果のエラーメッセージは...

No constructor for type 'ProblemStatement' has 1 parameters.

このステートメントをクラスに追加してコンストラクターをチェーンするという回避策があります...

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

これにより、TextBlock に「 hellonot provided 」が表示されます。ただし、これは MarkupExtension のセマンティクスも変更するため、より大きな「現実世界」のケースでは望ましくありません。また、より複雑な型が使用されているか、コンストラクターの引数が「動的」型である場合、オーバーロードの複雑さは劇的に増加します。また、たとえば、新しい「発信者情報」属性の使用は完全にブロックされます。

問題は、Xaml パーサーが既定のコンストラクター引数を受け入れるように Xaml を宣言する方法です。

4

1 に答える 1

11

これを試してください:

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

    private readonly string _mandatory;

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

使用法:

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

別:

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

結果:

  • XAML 解析エラーなし
  • オプションのパラメーターのコンストラクターをオーバーロードする必要はありません
  • 必須パラメータはコンストラクタ パラメータです。
  • オプションのパラメーターはプロパティです。
于 2016-06-23T20:56:51.517 に答える