2013-07-18 2 views
2

지금까지 WPF는 일반적으로 바인딩을 통해 또는 다른 방법으로 어떤 템플릿, 스타일 및 표현을 사용할 것인지를 결정하는 객체의 실제 유형을 살펴 봅니다. 그러나 WPF (또는?)와 같이 보이게하는 상황에 직면하여 어떤 이유로 든 선언 된 속성 유형을 살펴 봅니다.ICommand 속성은 특별히 바인딩을 통해 처리되는 이유는 무엇입니까?

이 예시도 모델이다 :

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> 

을 비어 있습니다. SimpleViewModel.ExampleCommandICommand 대신 object으로 입력하면 test이 예상대로 단추의 레이블로 표시됩니다.

무엇이 잘못 되었나요? WPF는 실제로 을 기반으로 객체를 다르게 처리합니까? 유형이 반환 된 객체 유형입니까? 이 문제를 해결할 수 있습니까? ICommand 옆의 다른 유형은 영향을 받습니까?

답변

4

ToString()objectICommand에 선언하는 것은 인터페이스 인 object 없습니다. 부터 object까지만 지정할 수 있습니다.

바인딩 시스템은 이미 말했듯이 선언 된 유형을 구분하지 않습니다. 그러나 string으로 변환 할 때 기본값 인 IValueConverter이 사용됩니다.

내부적으로 프레임 워크는 사용자 정의 변환기가 제공되지 않을 때 DefaultValueConverter을 사용합니다. 다른의 속성에 바인딩 할 때 사용자가 IValueConverter 정의 제공해야 문서에 따르면

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

: 인터페이스는 다르게 행동 할 것이다 왜 Create 방법 당신은 여기에 (sourceType.IsInterface의 특정 검사에 대한보고) 개체를 볼 수 있습니다 형식으로 당신이 바인딩되는 종속성 속성보다 호출되는 ToString 프레임 워크의 기본 변환 메커니즘의 구현 세부 사항입니다 (그리고 내가 아는 한 문서화되지 않은, 잘 정의 된 상황에 대한 기본 및 대체 값을 나타냅니다) 변경할 수 있습니다 언제든지.

+0

사실입니다.하지만 WPF가 먼저 선언 된 속성 유형을 조사하는 이유는 무엇입니까? 실제로 반환되는 것을 보지 않아야합니까? –

+0

수정 됨. InterfaceConverter는 다른 것들과 다르게 작동합니다. 이것은 C# 4.0 소스에서 온 것입니다. 그래서 초기의 대답은 조금 벗어났습니다. 왜냐하면 Type.IsInterface는 다른 선언 된 속성 유형에 대해 다른 값을 반환하기 때문에 더 많은 것입니다. – MrDosu

+0

뭔가 흥미로운 점은 속성을 MyExampleCommand (미리 public으로 설정)로 선언하면 버튼이 여전히 비어있는 것입니다. MyExampleCommand가 더 이상 ICommand를 구현하지 않으면 ToString이 제대로 호출된다. 이것이 어떻게 관련이 있습니까? 즉, propery 선언에서 직접 언급되지 않은 일부 인터페이스를 구현하면 아무 것도 변경되지 않는 이유는 무엇입니까? –

관련 문제