2

これまでのところ、WPF は通常、バインドまたはその他の方法で取得したオブジェクトの実際の型を調べて、使用するテンプレート、スタイル、および表現を決定しているという印象を受けました。しかし、私は今、何らかの理由で WPF (も?) が宣言されたプロパティ タイプを参照しているように見える状況に直面しています。

これは典型的なビューモデルです:

using System;
using System.Windows.Input;

public class SimpleViewModel
{
    private class MyExampleCommand : ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
        }

        public override string ToString()
        {
            return "test";
        }
    }

    private ICommand exampleCommand;

    public ICommand ExampleCommand
    {
        get
        {
            if (exampleCommand == null)
            {
                exampleCommand = new MyExampleCommand();
            }
            return exampleCommand;
        }
    }
}

そのクラスのインスタンスをウィンドウ内のデータ コンテキストとして使用し、次のボタンを追加します。

<Button>
    <TextBlock Text="{Binding ExampleCommand}"/>
</Button>

実行中のアプリケーションでは、ボタンは空になります。の代わりにtoとSimpleViewModel.ExampleCommand入力すると、期待どおりにボタンのラベルとして表示されます。objectICommandtest

ここで何が問題なのですか?WPF は、オブジェクトを返したプロパティの宣言された型に基づいて、オブジェクトを実際に異なる方法で処理しますか? これを回避することはできますか? また、影響を受ける他のタイプはありますICommandか?

4

1 に答える 1

4

ToString()は on で宣言されてobjectおり、インターフェイスではICommandありません。objectにのみ割り当て可能ですobject

あなたがすでに言ったように、バインディングシステムは宣言された型を区別しません。ただし、IValueConverterへの変換の場合に使用されるデフォルトは行いますstring

フレームワークは内部的DefaultValueConverterに、ユーザー定義のコンバーターが指定されていない場合に を使用します。このCreateメソッドでは、インターフェイスがオブジェクトとは異なる動作をする理由を確認できます (特定のチェックを探してくださいsourceType.IsInterface)。

internal static IValueConverter Create(Type sourceType,
                                    Type targetType, 
                                    bool targetToSource,
                                    DataBindEngine engine)
{
    TypeConverter typeConverter; 
    Type innerType;
    bool canConvertTo, canConvertFrom; 
    bool sourceIsNullable = false; 
    bool targetIsNullable = false;

    // sometimes, no conversion is necessary
    if (sourceType == targetType ||
        (!targetToSource && targetType.IsAssignableFrom(sourceType)))
    { 
        return ValueConverterNotNeeded;
    } 

    // the type convert for System.Object is useless.  It claims it can
    // convert from string, but then throws an exception when asked to do 
    // so.  So we work around it.
    if (targetType == typeof(object))
    {
        // The sourceType here might be a Nullable type: consider using 
        // NullableConverter when appropriate. (uncomment following lines)
        //Type innerType = Nullable.GetUnderlyingType(sourceType); 
        //if (innerType != null) 
        //{
        //    return new NullableConverter(new ObjectTargetConverter(innerType), 
        //                                 innerType, targetType, true, false);
        //}

        // 
        return new ObjectTargetConverter(sourceType, engine);
    } 
    else if (sourceType == typeof(object)) 
    {
        // The targetType here might be a Nullable type: consider using 
        // NullableConverter when appropriate. (uncomment following lines)
        //Type innerType = Nullable.GetUnderlyingType(targetType);
        // if (innerType != null)
        // { 
        //     return new NullableConverter(new ObjectSourceConverter(innerType),
        //                                  sourceType, innerType, false, true); 
        // } 

        // 
        return new ObjectSourceConverter(targetType, engine);
    }

    // use System.Convert for well-known base types 
    if (SystemConvertConverter.CanConvert(sourceType, targetType))
    { 
        return new SystemConvertConverter(sourceType, targetType); 
    }

    // Need to check for nullable types first, since NullableConverter is a bit over-eager;
    // TypeConverter for Nullable can convert e.g. Nullable<DateTime> to string
    // but it ends up doing a different conversion than the TypeConverter for the
    // generic's inner type, e.g. bug 1361977 
    innerType = Nullable.GetUnderlyingType(sourceType);
    if (innerType != null) 
    { 
        sourceType = innerType;
        sourceIsNullable = true; 
    }
    innerType = Nullable.GetUnderlyingType(targetType);
    if (innerType != null)
    { 
        targetType = innerType;
        targetIsNullable = true; 
    } 
    if (sourceIsNullable || targetIsNullable)
    { 
        // single-level recursive call to try to find a converter for basic value types
        return Create(sourceType, targetType, targetToSource, engine);
    }

    // special case for converting IListSource to IList
    if (typeof(IListSource).IsAssignableFrom(sourceType) && 
        targetType.IsAssignableFrom(typeof(IList))) 
    {
        return new ListSourceConverter(); 
    }

    // Interfaces are best handled on a per-instance basis.  The type may
    // not implement the interface, but an instance of a derived type may. 
    if (sourceType.IsInterface || targetType.IsInterface)
    { 
        return new InterfaceConverter(sourceType, targetType); 
    }

    // try using the source's type converter
    typeConverter = GetConverter(sourceType);
    canConvertTo = (typeConverter != null) ? typeConverter.CanConvertTo(targetType) : false;
    canConvertFrom = (typeConverter != null) ? typeConverter.CanConvertFrom(targetType) : false; 

    if ((canConvertTo || targetType.IsAssignableFrom(sourceType)) && 
        (!targetToSource || canConvertFrom || sourceType.IsAssignableFrom(targetType))) 
    {
        return new SourceDefaultValueConverter(typeConverter, sourceType, targetType, 
                                               targetToSource && canConvertFrom, canConvertTo, engine);
    }

    // if that doesn't work, try using the target's type converter 
    typeConverter = GetConverter(targetType);
    canConvertTo = (typeConverter != null) ? typeConverter.CanConvertTo(sourceType) : false; 
    canConvertFrom = (typeConverter != null) ? typeConverter.CanConvertFrom(sourceType) : false; 

    if ((canConvertFrom || targetType.IsAssignableFrom(sourceType)) && 
        (!targetToSource || canConvertTo || sourceType.IsAssignableFrom(targetType)))
    {
        return new TargetDefaultValueConverter(typeConverter, sourceType, targetType,
                                               canConvertFrom, targetToSource && canConvertTo, engine); 
    }

    // nothing worked, give up 
    return null;
} 

ドキュメントによると、呼び出されることIValueConverterに依存しているのは、フレームワークのデフォルト変換メカニズムの実装の詳細であるため、バインドしている依存関係プロパティとは異なるタイプのプロパティにバインドするときに定義されたユーザーを提供する必要がToStringあります(私が知る限り文書化されていません) 、明確に定義された状況のデフォルト値とフォールバック値のみを示しており、いつでも変更される可能性があります。

于 2013-07-18T16:04:42.313 に答える