如何将DataContext转换为WPF中的另一种类型?

时间:2018-02-21 22:53:13

标签: .net wpf xaml

例如我有两个类:

class SomeClass {
     public decimal Value1 => ...;
     public decimal Value2 => ...;
}

class SomeClassWrapper {
     private readonly SomeClass someClass;
     public decimal Value1 => someClass.Value1;
     public decimal Value2 => someClass.Value2;
     public decimal Delta => someClass.Value2 - someClass.Value1;
     public SomeClassWrapper(SomeClass someClass) {...}
}

SomeClass通过第三方代码传递给我的DataTemplate。但我需要使用SomeClassWrapper

那么,如何在xaml中将SomeClass转换为SomeClassWrapper

3 个答案:

答案 0 :(得分:0)

您可以使用ObjectDataProvider创建ObjectType="{x:Type local:SomeClassWrapper}。然后将此ObjectDataProvider设置为DataContext

不幸的是,没有简单的方法将DataContext传递给此ObjectDataProvider。有一个黑客描述here

您无法将DataContext传递给ObjectDataProvider,但您可以通过特殊的代理类进行此操作。

public class Proxy : Freezable {

    public static readonly DependencyProperty DataProperty = DependencyProperty.Register( "Data", typeof( object ), typeof( Proxy ), new UIPropertyMetadata() );

    public object Data {
        get { return GetValue( DataProperty ); }
        set { SetValue( DataProperty, value ); }
    }

    protected override Freezable CreateInstanceCore() {
        return new Proxy();
    }

}

public class ProxyStaticResource : StaticResourceExtension {

    public override object ProvideValue(IServiceProvider serviceProvider) {
        var proxy = (Proxy) base.ProvideValue( serviceProvider );
        return proxy.Data;
    }

}


<local:Proxy x:Key="DataContextProxy" Data="{Binding}" />

<Grid.Resources>
    <ObjectDataProvider x:Key="TooltipInfo" ObjectType="{x:Type local:TooltipInfo}">
        <ObjectDataProvider.ConstructorParameters>
            <local:ProxyStaticResource ResourceKey="DataContextProxy" />
        </ObjectDataProvider.ConstructorParameters>
    </ObjectDataProvider>
</Grid.Resources>

<Grid.DataContext>
    <StaticResource ResourceKey="TooltipInfo" />
</Grid.DataContext>

也许有一种更简单的方法?

答案 1 :(得分:0)

可能最简单的方法是使用IValueConverter

[ValueConversion( typeof( SomeClass ), typeof( SomeClassWrapper ) )]
public class SomeClassWrapperConverter : IValueConverter {

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        if (value == null) throw new ArgumentNullException( nameof( value ) );
        if (value is SomeClass == false) throw new ArgumentException( $"Value ({value?.GetType().Name}) is invalid" );
        if (targetType != typeof( object )) throw new ArgumentException( $"Target type ({targetType.Name}) is invalid" );

        return new SomeClassWrapper ( (SomeClass) value );
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        throw new NotImplementedException();
    }
}


<SomeClassWrapperConverter x:Key="SomeClassWrapperConverter" />

<Grid DataContext="{Binding Converter={StaticResource SomeClassWrapperConverter}}" />

答案 2 :(得分:0)

更方便的方法是使用MarkupExtension

public class DataContextConverter : MarkupExtension {

    public Type TargetType { get; set; }


    public override object ProvideValue(IServiceProvider serviceProvider) {
        if (TargetType == null) throw new ArgumentNullException( nameof( TargetType ) );
        if (serviceProvider == null) throw new ArgumentNullException( nameof( serviceProvider ) );

        var targetProvider = (IProvideValueTarget) serviceProvider.GetService( typeof( IProvideValueTarget ) );
        if (targetProvider == null) throw new ArgumentNullException( nameof( targetProvider ) );


        if (targetProvider.TargetObject is DependencyObject == false) return this; // deferral mechanism


        var @object = (FrameworkElement) targetProvider.TargetObject;
        return Activator.CreateInstance( TargetType, @object.DataContext );
    }


}


<Grid DataContext="{local:DataContextConverter TargetType='local:SomeClassWrapper'}" />