I recently discovered some very useful development-time attributes from Blend for a WPF component that (among other things) allows you to set a DataContext only during development. Excellent!
In combination with the DesignInstance attribute, you can set the type to be automatically created and bound at design time, which allows you to use Visual Studio Designer with some context regarding what your WPF component will look like at runtime. This is really nice, and I'm sorry that it never occurred to me to find out for so long.
Obviously, since I do not live here in the skies of programmers, I ran into a problem when using these development-time attributes.
I created a temporary design wrapper around one of my ViewModels, which has a constructor without parameters (so it can be created by the designer). Inside his constructor, he uses NSubstitute to retrieve all the dependencies entered into the ViewModel that he inherits.
Using this design time class in the constructor results in an error similar to the following:
Unable to cast object of type 'Castle.Proxies.XProxy' to type 'X'.
(When replacing X with one of my nested dependencies).
You can use the following minimal code to reproduce the problem.
Create a WPF application in VS2013 focused on the .NET Framework 4.5.1 (this can happen in previous versions, I donβt know) with the following files.
View.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>
ViewModel.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; } } }
DesignTimeViewModel.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 { } }
Compile, close and reopen the solution and open View.xaml in the designer. It will work just fine. Then close the constructor, rebuild the solution and run View.xaml in the designer again and you will get the following error:
Unable to cast object of type 'Castle.Proxies.XDependencyProxy_1' to type 'DesignTimeNSubstituteIssue.Services.XDependency'.
When this error occurs, the designer stops using the specified DesignTimeViewModel and returns to the absence of a DataContext at all.
The only way to fix this is to close and reopen the solution.
I suspect that I know what is happening, but I do not know why this is happening or how to fix it.
I think that at the first compilation, the designer receives a link to the assembly and caches it. When the second compilation occurs, the assembly is rebuilt and basically the same, but the NSubstitute proxy is regenerated with a new suffix (for example, Castle.Proxies.XDependencyProxy_2 or something else), which was not in the first assembly, so the developer does not know that this proxy -server actually implements the XDependency interface. This is purely a hypothesis on my part.
I can create a workaround without using NSubstitute and manually mock the dependencies, but I am interested to know if anyone can shed some light on this topic.