为什么ICommand的属性被绑定特殊处理? [英] Why Are ICommand Properties Treated Specially by Bindings?

查看:112
本文介绍了为什么ICommand的属性被绑定特殊处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我有即时pression该WPF通常着眼于实际类型的对象其经由结合或以任何其他方式获得的,以确定哪些模板,样式和重新presentation使用。但是,我现在面对的情况,这使得它看起来像WPF(也?)着眼于声明的属性类型的某些原因。

So far, I had the impression that WPF generally looks at the actual type of an object it gets via a binding or in any other way to determine what templates, styles and representation to use. However, I am now confronted with a situation which makes it seem like WPF (also?) looks at the declared property type for some reason.

这是一个示范性视图模型:

This is an exemplary view model:

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

使用这个类的一个实例作为窗口的数据上下文,并添加此按钮:

Use an instance of that class as a data context in a window and add this button:

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

在运行的应用程序,该按钮将是空的。如果 SimpleViewModel.ExampleCommand 键入到的对象而不是的ICommand 测试将显示为对预期的按钮上的标签。

In the running application, the button will be empty. If SimpleViewModel.ExampleCommand is typed to object instead of ICommand, test will be shown as the label on the button as expected.

什么是错在这里?难道真的WPF区别对待的基础上的声明的对象的类型归还他们的财产?可这周围的任何其他类型的可工作,并且是的ICommand 影响?旁边

What is wrong here? Does WPF really treat objects differently based on the declared type of the property that returned them? Can this be worked around, and are any other types beside ICommand affected?

推荐答案

的ToString()对象被声明的ICommand 不是对象这是一个接口。只有分配对象

ToString() is declared on object and ICommand is not an object it is an interface. It is only assignable to object.

绑定系统不会,因为你已经说了,上区分声明的类型。但默认的IValueConverter 在转换的情况下使用字符串一样。

The binding system does not, as you already said, differentiate on the declared type. But the default IValueConverter used in the case of conversion to string does.

在内部框架采用了 DefaultValueConverter 当没有用户定义的转换器给出。在创建方法,你可以看到为什么接口都采取不同的那么这里的对象(找 sourceType.IsInterface )

Internally the framework uses a DefaultValueConverter when no user defined converter is given. In the Create method you can see why interfaces will act differently then objects here (look for the specific check of 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;
} 

据你应该提供用户定义的文档绑定到不同类型的比你绑定到依赖属性为依托<$ C $物业时的IValueConverter C>的ToString 被称为是框架默认转换机制的实现细节(并且据我知道无证,它只是指出默认值和后备值以及确定的情况下),并可以在任何时刻改变

According to the documentation you should provide a user defined IValueConverter when binding to a property of different type than the dependency property you are binding to as relying on ToString being called is an implementation detail of the frameworks default conversion mechanism (and is undocumented as far as i know, it only states default and fallback values for well defined circumstances) and could change at any moment.

这篇关于为什么ICommand的属性被绑定特殊处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆