【问题标题】:How to return strongly typed object from MarkupExtension?如何从 MarkupExtension 返回强类型对象?
【发布时间】:2023-11-10 23:13:02
【问题描述】:

尝试将我的第一个 MarkupExtension 作为服务定位器并使用它在我的 XAML 中获取 DataContext:

视图模型和界面

public interface IMainViewModel
{
    ICommand OpenProjectCommand { get; }
}

public class MainViewModel : IMainViewModel
{
    public ICommand OpenProjectCommand { get; private set; }
    ...
}

服务定位器:

public static class ServiceLocator
{
    public static void Map<T>(object concreteType)
    {
        // store type
    }

    public static T GetInstance<T>() where T : class
    {
        // get, instantiate and return
    }
}

App.xaml

protected override void OnStartup(StartupEventArgs e)
{
    ServiceLocator.Map<IMainViewModel>(typeof(MainViewModel));
    base.OnStartup(e);  
}

标记扩展:

public class ServiceLocatorExtension : MarkupExtension
{
    public Type ServiceType { get; set; }

    public ServiceLocatorExtension(Type type)
    {
        ServiceType = type;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (ServiceType == null)
            throw new ArgumentException("Type argument is not specified");

        var instance = ServiceLocator.GetInstance<IMainViewModel>(); // how to use this.ServiceType?
        return instance;
    }
}

XAML:

<Window ... DataContext="{loc:ServiceLocator {x:Type loc:IMainViewModel}}">
...
<Button ... Command="{Binding OpenProjectCommand}"/> // problem complains cannot resolve in datacontext type of "object"

问题:

1) 如何在 MarkupExtension 中使用 this.ServiceType 属性而不是显式接口?

2) XAML 中按钮上的命令绑定抱怨它无法从对象类型的数据上下文中解析,所以我收到了一个我不想要的警告。如何让它知道它的正确类型?

【问题讨论】:

  • 看起来创建一个非泛型的GetInstance(Type type) 方法会让你的生活更轻松,这取决于GetInstance 的具体实现方式。在这种情况下,您不需要通用变体,因为ProvideValue 无论如何都会返回object。至于第二个问题,如果您收到该警告,则数据上下文不是MainViewModel。绑定系统查看对象的实际运行时类型。

标签: c# wpf xaml service-locator markup-extensions


【解决方案1】:

不确定这是否是最佳解决方案,仍在寻找替代方案。

1:

使用反射:

public override object ProvideValue(IServiceProvider serviceProvider)
{
    if (ServiceType == null)
        throw new ArgumentException("Type argument is not specified");

    var serviceLocatorMethod = typeof(ServiceLocator).GetMethod("GetInstance").MakeGenericMethod(ServiceType);
    return serviceLocatorMethod.Invoke(null, null);
}

2:

作为设计师的问题,这解决了问题:

d:DataContext="{d:DesignInstance loc:MainViewModel}"

【讨论】: