9

我最近从 Blend 中发现了非常有用的 WPF 组件设计时属性,它(除其他外)允许您仅在设计时设置 DataContext。惊人的!

结合 DesignInstance 属性,您可以设置在设计时自动创建和绑定的类型,允许您使用 Visual Studio 设计器和一些上下文来了解您的 WPF 组件在运行时的实际外观。它真的很好,我希望它没有花我这么长时间才发现。

显然,因为我在这里而不是在程序员的天堂里生活,我在使用这些设计时属性时遇到了问题。

我围绕我的 ViewModel 创建了一个设计时包装器,它有一个无参数的构造函数(因此它可以由设计者创建)。在它的构造函数中,它使用 NSubstitute 来模拟注入到它继承的 ViewModel 中的所有依赖项。

在设计器中使用此设计时类会导致错误,如下所示:

Unable to cast object of type 'Castle.Proxies.XProxy' to type 'X'.

(将 X 替换为我注入的依赖项之一)。

您可以使用以下最少的代码集来重现该问题。

在 VS2013 中创建一个针对 .NET Framework 4.5.1 的 WPF 应用程序(它也可能发生在以前的版本中,我不知道),其中包含以下文件。

查看.xaml

<Page 
    x:Class="DesignTimeNSubstituteIssue.Views.View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:DesignTimeNSubstituteIssue_Views_DesignTime="clr-namespace:DesignTimeNSubstituteIssue.Views.DesignTime"
    mc:Ignorable="d" 
    d:DataContext="{d:DesignInstance Type=DesignTimeNSubstituteIssue_Views_DesignTime:DesignTimeViewModel, IsDesignTimeCreatable=True}">
    <Grid>
        <TextBlock Text="{Binding Message, FallbackValue=Design_Time_Message_Failed_Using_Fallback}"/>
    </Grid>
</Page>

视图模型.cs

using DesignTimeNSubstituteIssue.Services;

namespace DesignTimeNSubstituteIssue.ViewModels
{
    public class ViewModel
    {
        public ViewModel(XDependency dependency)
        {
            _Dependency = dependency;
        }

        private readonly XDependency _Dependency;
        public string Message { get; protected set; }
    }
}

设计时间视图模型.cs

using DesignTimeNSubstituteIssue.Services;
using DesignTimeNSubstituteIssue.ViewModels;
using NSubstitute;

namespace DesignTimeNSubstituteIssue.Views.DesignTime
{
    public class DesignTimeViewModel : ViewModel
    {
        public DesignTimeViewModel()
            : base(Substitute.For<XDependency>())
        {
            Message = "This is a Design Time message.";
        }
    }
}

XDependency.cs

namespace DesignTimeNSubstituteIssue.Services
{
    public interface XDependency
    {

    }
}

编译、关闭并重新打开解决方案并在设计器中打开 View.xaml。它会工作得很好。然后,关闭设计器,重新构建解决方案,再次在设计器中打开 View.xaml,会出现以下错误:

Unable to cast object of type 'Castle.Proxies.XDependencyProxy_1' to type 'DesignTimeNSubstituteIssue.Services.XDependency'.

发生此错误时,设计器将停止使用指定的 DesignTimeViewModel,并退回到根本没有 DataContext。

解决此问题的唯一方法是关闭并重新打开解决方案。

我怀疑我知道发生了什么,但我不知道为什么会发生或如何解决它。

我认为在第一次编译时,设计者正在获取对程序集的引用并将其缓存。当第二次编译发生时,程序集被重建并且大部分是相同的,但是 NSubstitute 代理重新生成了一个Castle.Proxies.XDependencyProxy_2不在第一个程序集中的新后缀(比如或其他东西),所以设计者不知道那个代理实际上实现了 XDependency 接口。这纯粹是我的猜测。

我可以通过不使用 NSubstitute 并手动模拟依赖项来创建一种解决方法,但我很想看看是否有人可以对这个主题有所了解。

4

1 回答 1

5

看起来我的原始项目和最小复制项目都没有正确地对程序集进行版本控制,这意味着设计者不知道它需要重新加载程序集(因为新版本与旧版本完全相同)。更改程序集版本以包含自动生成的版本号似乎可以解决问题。

于 2014-01-01T05:44:47.030 回答