views:

40

answers:

2

I'm getting an exception in my C#/.NET application that reads:

'CommandCoverter' is unable to convert 'MyNamespace.MyDerivedFromICommandSubclass' to 'System.String'.

What I'm doing is fairly straight forward, as described by the MSDN ICommand documentation:

public class MyDerivedFromICommandSubclass : ICommand
{
  // Implement interface
  ...
}

I have a FlowDocument with a Hyperlink on it. The Hyperlink is allowed to have a Command Property, which I set to my derived ICommand so that when the link is clicked, my custom action gets performed.

That part works.

Here's where I get into trouble: if I select the hyperlink and right-click Copy (or press Control-C).

Instantly the .NET framework throws a System.NotSupportedException with the exception detail above. The stack trace shows:

at System.ComponentModel.TypeConverter.GetConvertToException(Object value, Type destinationType)

at System.Windows.Input.CommandConverter.ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType)

At this point I resorted to Red Gate's free .NET Reflector and looked at the source code to ConvertTo:

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
  if (destinationType == null) // We know it isn't, it's System.String
  {
    throw new ArgumentNullException("destinationType"); // We don't see this exception
  }
  if (destinationType == typeof(string)) // It is, so control passes in
  {
    if (value == null) // It isn't, so this condition is skipped
    {
      return string.Empty;  // Confirmed we don't get this return value
    }
    RoutedCommand command = value as RoutedCommand;
    if (((command != null) && (command.OwnerType != null) && IsKnownType(command.OwnerType))
    { // Is a user-defined ICommand a known type? Doubtful. This gets skipped.
      return command.Name;  // Confirmed we don't get this return value
    }
    // It has to fall through then if no return is done!
  }
  throw base.GetConvertToException(value, destinationType); // BOOM!
  // value is my custom ICommand and destinationType is System.String
}

So the question then becomes, as all of this happens inside of .NET, am I doing something wrong, and if so, what? Or, is this a .NET bug, and if so, is there a work around?

Thanks for any help.

+1  A: 

Intuitively this feels wrong; copying the hyperlink should copy the text, regardless of what the command does. However, you can work around the problem by implementing your own TypeConverter for your command class (How to Implement a Type Converter). Have it delegate to CommandConverter, except for CanConvertTo: return false from that method to tell the framework that your command cannot be converted to a string (or delegate CanConvertTo to CommandConverter also, then return a representative string from ConvertTo.

Thanks, and double thanks for the link, I'll have to give that a shot. Agreed that this feels wrong; if one does a Google search for the error message, you'll see it's happening all over the place to a lot of people -- none who seem to be getting a reasonable answer (this by far is the most informative response I've seen on the topic). I'm guessing that it's trying to put a representation of not just the text but the ICommand in the clipboard, should it be pasted to somewhere else that can deal with it. That said, I don't know why it's trying to convert it to text by default. Thanks again.
Walt Stoneburner
Ran into problems. CommandConverter is sealed, and TypeConverter when overridden wasn't firing any methods. I managed to get another solution working, I'll add that in a moment -- but thank you, your ideas led me to much closer.
Walt Stoneburner
A: 

A fantastic description of ICommand resides in this blog entry by SkySigal, though I needed Google's Cache due to blog configuration issues at the time. Unfortunately, the end of the article where this problem is addressed is a little ambiguous in its wording about whether the ICommand should be static or non-static.

Turns out, however, there was an article on dotnet mania talking about how copying a hyperlink with a custom command will crash an application.

Seems this bug has been in .NET since 2007, at least, and that the problem is the code explicitly checking for "known commands," just as the Reflector analysis above showed.

.NET wants to serialize the command along with its parent object, and that's where the problem comes in. The article's solution involves creating a helper object, which is ignored by the serialization process, which does the same thing as the command.

<Hyperlink Command="{x:Static myns:MyCommands.CustomCommand1}" .../>

becomes

<Hyperlink myns:HyperlinkHelper.Command="{x:Static myns:MyCommands.CustomCommand1}" .../>

with some backing code inside the myns namespace's HyperlinkHelper class as a property named Command. It's clever trickery, and ought to be shamefully unnecessary.

Hats off to Eric Burke for figuring this one out.

Walt Stoneburner
To do this in C#, rather than XAML, one does this: hyperlink.SetValue( HyperlinkHelper.CommandProperty, CustomCommand1 );
Walt Stoneburner
Required reading. http://blog.hackedbrain.com/articles/UnderstandingDependencyObjectAndDependencyProperty.aspx
Walt Stoneburner
And this. Attached Properties -- http://msdn.microsoft.com/en-us/library/ms749011.aspx
Walt Stoneburner