Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Portable Class Library ICommand compilation error in WPF -- Not sure how to resolve this appropriately?

Some background information:

  • I am using VS 2013
  • I created a Portable Class Library (Targets: .NET 4.5/Silverlight 5/Win Phone 8/Win Store 8)
  • I implemented the ICommand interface in my class named MyCommand.

MyCommand.cs:

public class MyCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        throw new NotImplementedException();
    }

    public void Execute(object parameter)
    {
        throw new NotImplementedException();
    }

    public event EventHandler CanExecuteChanged;
}

I found that when I attempted to reference use the MyCommand class in a WPF 4.5 application, I get the following error:

The type 'System.Windows.Input.ICommand' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes'.

I'm not really sure why this is happening, or what I can do to resolve it appropriately. After scouring the internet, I found answers talking about adding a reference to System.Windows.dll 4.0.0.0 in my WPF application. Doing this allows me to compile and run the application, but my IDE complains that:

Interface member 'void System.Windows.Markup.IComponentConnector.Connect(in, object)' is not implemented.

This occurs for my MainWindow.cs class. Is there a better fix than to just deal with this bug so I'm not adding a reference to System.Windows.dll in a WPF application which really doesn't need it? And I say that it doesn't really need it because if I copy/paste the MyCommand class into the Wpf app it works fine. It only has an issue because I am attempting to use it as a portable class library.


UPDATE:

It seems that there is more to this than meets the eye. I created a new Wpf Application and used NuGet to reference the MvvmCross solution (since it has an MvxCommand object). When I do this, I get the same error.

David Kean's answer to a similar question suggests that this should be fixed via VS2012/VS2013 and if not, just reinstall/repair. I fully removed my VS installation and reinstalled it from scratch (VS2013) and still have this issue. So, is this truly a bug or did I do something wrong here?


UPDATE 2:

I attempted to see if I could assign an instance of the MyCommand class to ICommand: ICommand cmd = new MyCommand(); and received the following error:

Cannot implicitly convert type 'PortableClassLibrary1.MyCommand' to 'System.Windows.Input.ICommand'. An explicit conversion exists (are you missing a cast?)

For some reason the PCL does not seem to type forward the System.Windows.dll [2.0.5.0] ICommand to System.dll [4.0.0.0] ICommand for my desktop application... Ugh!


Update 3:

I uploaded my solution so that it is more visible as to what the problem is.


Update 4:

I've opened a Microsoft connect ticket on this issue.


Update 5:

After calming down from being upset about this not working... and others telling me that they don't get the same "interface" error. I realize that the "side-effect" of adding System.Windows.dll was a ReSharper 8.1 error. I guess it's time to complain to ReSharper instead of Microsoft. sigh

like image 574
myermian Avatar asked Dec 09 '13 18:12

myermian


2 Answers

You must add a reference to assembly 'System.Windows, Version=2.0.5.0

The advice is excellent, the message is not that great. Clearly you got baffled by the version number. Right-click your WPF project, Add Reference and tick System.Windows. Which solves your problem.

The missing magic is this line in the System.Windows.dll reference assembly:

  [assembly: TypeForwardedTo(typeof(ICommand))]

Which gets ICommand forward from System.Windows.dll to System.dll

Keep in mind that System.Windows.dll from the c:\program files (x86)\reference assemblies subdirectory is just a reference assembly. It doesn't contain any code or types at all, just [TypeForwardedTo] attributes. It acts as an "adapter", bridging the gap between different .NET framework platforms. The final executable doesn't actually have a dependency on System.Windows.dll at all.

like image 146
Hans Passant Avatar answered Nov 15 '22 04:11

Hans Passant


This is not exactly a bug, it is more about what's included in the .NET Portable Subset. MSBuild is correct in complaining.

If you look at the manifest of PortableClassLibrary, you will see that an extern reference to System.Windows is actually made.

.assembly extern retargetable System.Windows
{
  .publickeytoken = (7C EC 85 D7 BE A7 79 8E )                         // |.....y.
  .ver 2:0:5:0
}

And if you check what is actually contained in .NETPortable, you'll find that the included System assembly is v2.0.5.0

You can find it here -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile158\

...as is System.Windows at v2.0.5.0

So technically speaking, MSBuild is correct in asking for System.Windows, since this is not included explicitly in a build for WPF.

Although the TypeForwarding argument is interesting, it would still be moot, since at v2.0.5.0, there was no type forwarding to be done.

Now, if you actually look at System.Windows v4.0.0.0, there IS a TypeForwardedTo rule for ICommand (use reflector)

So the question here is more of a design issue, more than anything. System.Windows conflicts with WPF, yet this is only due to the other implementation of Window.

So to fix this, reference the v2.0.5.0 System.Windows in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile158\System.Windows.dll (I would take a copy of it for this project).

Your last step is to resolve the type ambiguity that occurs because of this... you'll get a type ambiguity between System v4.0.0.0 and System.Windows v2.0.5.0 for the use of ICommand

You can solve this by removing System.Windows from the 'global' alias (hit F4 on the reference), and add in a custom alias, ex. SomeAlias

Then just reference it via it's alias.

extern alias SomeAlias;

public partial class MainWindow : Window
{
    private SomeAlias::System.Windows.Input.ICommand _command;
    ...etc...

Voila.. this was really interesting, so I've detailed it a bit more here, where I could include screenshots (and talk about the ReSharper hiccup)

like image 38
Stefan Z Camilleri Avatar answered Nov 15 '22 05:11

Stefan Z Camilleri