9

I have a class in a UWP Project that derives from Windows.UI.Xaml.Shapes.Shape:

public class Hex : Windows.UI.Xaml.Shapes.Shape
{
    public Hex()
    {

    }
}

When I tried to instantiate a new memeber of this class in mainPage.cs I get an InvalidCastException as following:

System.InvalidCastException: Specified cast is not valid.
       at Windows.UI.Xaml.Shapes.Shape..ctor()
       at App1.Hex..ctor()
       at App1.MainPage.Button_Click(Object sender, RoutedEventArgs e)

Here is the code from mainPage.cs

private void Button_Click(object sender, RoutedEventArgs e)
{
    var h = new Hex();
    h.Width = 20;
}

but doesn't work neither. As I understand the protected constructor of Shape should be able to be accessed from a derived class, so what's happening? Doing the same thing with deriving from Windows.UI.Xaml.Frameworkelemnt works without problems.

Nico Zhu
  • 32,367
  • 2
  • 15
  • 36
Mansly
  • 91
  • 3
  • 3
    We are going to need to see the code, for both classes constructors at least. – Zohar Peled Jun 24 '18 at 11:56
  • What is the constructor for `Shape`? – Rotem Jun 24 '18 at 12:03
  • The Shape class is the one Windows.UI.Xaml.Shapes.Shape, – Mansly Jun 24 '18 at 12:03
  • Are you sure you are not deriving your class accidentally from `System.Windows.Shapes.Shape` rather than `System.UI.Xaml.Shapes.Shape`? Change your class declaration to be fully qualified with the namespace and see if it works: `public class Hex : System.UI.Xaml.Shapes.Shape { ... }`. – blins Jun 24 '18 at 12:59
  • Please provide a [mcve] and the stack trace. I can't see how `Hex hex = new Hex();` could ever throw an `InvalidCastException` as there are no casts involved in any of the code you've shown. – Jon Skeet Jun 24 '18 at 13:00
  • Yes, I just rechecked. It UI.Xaml. I made it totally simple to find the problem, but stuck. Its just the derived class in an UWP Project and I try to instantiate a new parameterless Hex from the mainPage.cs. As said it works if I I take another class with no proteced constructer ro derive... So this seems to be the problem, but its callint the constructor from shape though – Mansly Jun 24 '18 at 13:09
  • I updated the sample above. I just made the sample as simple as possible. Its just the derived class and something (a button in mainPage.cs in this case) that should create a new instance of the class. – Mansly Jun 24 '18 at 13:23
  • 1
    Okay, I've reproduced the problem now. Very odd. Looking into it a bit... I can only think at the moment that it's trying to fetch some property and cast the result. It's hard to tell what though. – Jon Skeet Jun 25 '18 at 07:02
  • The stack trace says the code in the constrcutor [`protected Shape()` (docs link)](https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.shapes.shape.-ctor) (which is necesaarily chained since it is the only visible instance constructor) attempts an invalid cast (possibly a bad unboxing into a value type, I do not know). So we need to look at the source code for this member of `Windows.UI.Xaml.Shapes.dll` (?) before we can answer. Is this source code open? – Jeppe Stig Nielsen Jun 28 '18 at 14:27

3 Answers3

1

TLDR; It's not possible without some serious hurt. But... IT IS POSSIBLE.

I started out by reproducing the problem, here's the stack trace to prove it:

   at Windows.UI.Xaml.Shapes.Shape..ctor()
   at App1.Hex..ctor()
   at App1.App1_XamlTypeInfo.XamlTypeInfoProvider.Activate_0_Hex()
   at App1.App1_XamlTypeInfo.XamlUserType.ActivateInstance()

You can see that the type providers for the xaml elements (App1_XamlTypeInfo) are generated at run-time, thus you cannot override those [Factory|Instance Creation|Builder] methods with customised code.

Not even an attempt to create an implicit conversion in the Hex class to the Shape type could fix it, apparently a limitation of C# that you cannot make a custom implicit conversion from sub-class to base-class, because "it changes already compiled code" or whatever :(

Another route could have been to favour encapsulation over inheritance, you'd be required to re-implement/expose the Shape class in all its glory and manipulate an internal Shape instance, however there'll be a limitation in that you cannot implement the IShape and IShape2 interfaces from WPF, because they're marked internal.

As far as I can tell, the only option left is to modify the WPF source code, and distribute that... I have no idea on licensing issues related to doing such things. See https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Shapes/Shape.cs for the class that you'd need to change, or just include your own derived type (like Ellipse), and https://referencesource.microsoft.com/#PresentationFramework/PresentationFramework.csproj just to point out that it's quite easy to build citation needed.

Hopefully someone else comes along and proves me wrong...

Dan Rayson
  • 1,315
  • 1
  • 14
  • 37
1

I don't understand this either; it's quite annoying but apparently you can't derive from Shape as you'd expect (and should be able to do.) Instead you have to inherit from Path and add the paths that way. I'll link another answer here on SO that explains it better rather than duplicate.

Answered already on SO

Michael Puckett II
  • 6,586
  • 5
  • 26
  • 46
  • 1
    Thank you for your answers. So I will do it with Path class... At least I know now that I'm not the only one who cannot inherit from class shape – Mansly Jun 27 '18 at 13:16
1

When I tried to instantiate a new memeber of this class in mainPage.cs I get an InvalidCastException as following:

System.InvalidCastException: Specified cast is not valid.
       at Windows.UI.Xaml.Shapes.Shape..ctor()
       at App1.Hex..ctor()
       at App1.MainPage.Button_Click(Object sender, RoutedEventArgs e)

Thank you for your feedback. We fully understand that this exception may cause confusion.

The fact is, even though Shape is unsealed, creating a custom shape that way isn't supported.

To draw custom shapes, please use the existing Shape types.

For example, a hex shape can be easily created using a Polygon with 6 vertices:

<Polygon
    Points="143,75 143,125 100,150 57,125 57,75 100,50"
    Stroke="Blue"                
    Fill="LightBlue" />

For more complicated shapes, customers can instead use the Path class as described here: https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/shapes#path

If desired you can then wrap any shape in a custom control, for example using a UserControl: https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.usercontrol

Franklin Chen - MSFT
  • 4,845
  • 2
  • 17
  • 28
  • Thank you, Franklin. Polygon would have been first choice you are right, but I need to add some custom dp (and there is no way to do to a existing class without deriving, right?) and as I need a lot of Hexes my try with ContentControl and Polygon as content made everything to slow. So first idea was to take the closest to Polygon for deriving... – Mansly Jul 05 '18 at 07:37