locked
Why does adding 2nd level subclassed Button controls to a Grid give E_INVALIDARG?

    Question

  • I've come across the this problem dealing with subclasses of the Windows.UI.Xaml.Button class in C++/CX, and I'd like to know what's going on.

     

    If I add a control instance to a grid, everything works as expected.

    If I subclass the control and add an instance of the subclass, everything works as expected.

    But if I subclass my subclassed control and add an instance of it to the grid I get E_INVALIDARG thrown during Grid::Children::Append(). What gives?

     

    My code looks roughly like this (LayoutRoot is a Grid in MainPage.xaml, this sample has been tested in an empty simple metro application):



    // Scenario 1: This works (duh!)
    LayoutRoot->Children->Append(ref new Button());
    
    // Scenario 2: This works
    LayoutRoot->Children->Append(ref new MyButton1());
    
    // Scenario 3: This doesn't work, it will cause an E_INVALIDARG to be thrown by the collection
    LayoutRoot->Children->Append(ref new MyButton2());
    
    // This is how MyButton1 and MyButton2 is defined
    public ref class MyButton1 : public Button { 
      public:
        MyButton1() {};
        ~MyButton1() {};
    };
    
    public ref class MyButton2 : public MyButton1 { 
      public:
        MyButton2() {};
        ~MyButton2() {};
    };





    Tuesday, February 14, 2012 8:47 AM

All replies

  • I think you should write your MyButton class name as:

    public ref class Button1 : public Button { 
      public:
        Button1() {};
        ~Button1() {};
    };
    
    public ref class Button2 : public Button1 { 
      public:
        Button2() {};
        ~Button2() {};
    };

    And where do you put the Button1 and 2 definition code. I just test it in one header file, it can work in my simple C++ Metro App.

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Bob_Bao Wednesday, February 15, 2012 8:11 AM
    Wednesday, February 15, 2012 8:11 AM
  • (I've fixed the typo in the sample so that ctor/dtor have the correct names)

    The only thing I need to repeat the problem I described, is the two classes in my sample. No further definition code is neccessary. I have tested this on Visual Studio 2011 Professional Developer Preview (from MSDN Subscriptions) and on Visual Studio 2011 Express Edition (from the Windows 8 Developer Preview).

    I ran both tests on different physical machines, but both were actually Window 8 Developer Preview 64-bit running in Parallels VM:s.

    So unless it's related to the fact that Windows is running in a [Parallels] VM, I believe it should be repeatable for anybody using the code I have provided.

     

    I suspect that there's a limitation in WinRT regarding inheritance that I haven't understood, which is why I am asking about this.

     

     

    Here is the full MainPage.xaml.cpp that I've been using in a new project created by selecting Templates>>Visual C++>>Windows Metro Style>>Application, with the name "Application3":

    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage.xaml class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    
    using namespace Windows::UI::Xaml::Controls;
    using namespace Application3;
    
    MainPage::MainPage()
    {
        InitializeComponent();
    
    #define TEST_SCENARIO 3
    
    #if TEST_SCENARIO==1
        LayoutRoot->Children->Append(ref new Button()); // Works
    #elif TEST_SCENARIO==2
        LayoutRoot->Children->Append(ref new MyButton1()); // Works
    #elif TEST_SCENARIO==3
        LayoutRoot->Children->Append(ref new MyButton2()); // Crashes
    #endif
    }
    
    MainPage::~MainPage()
    {
    }

     

    And here's the MainPage.xaml.h: 

    //
    // MainPage.xaml.h
    // Declaration of the MainPage.xaml class.
    //
    
    #pragma once
    
    #include "pch.h"
    #include "MainPage.g.h"
    
    using namespace Windows::UI::Xaml::Controls;
    
    
    namespace Application3
    {
        public ref class MyButton1 : public Button
        {
        public:
          MyButton1() {};
          ~MyButton1() {};
        };
    
        public ref class MyButton2 : public MyButton1
        {
        public:
          MyButton2() {};
          ~MyButton2() {};
        };
    
        public ref class MainPage
        {
        public:
          MainPage();
          ~MainPage();
        };
    }

    Just change the #define TEST_SCENARIO to 3 and it will crash on execution with E_INVALIDARG being thrown. Defining TEST_SCENARIO to 1 or 2 works.

    Ludvig




    Wednesday, February 15, 2012 12:59 PM
  • UPDATE: I think I'm on the right track understanding this problem after reading this article by Ian Griffiths, but I need to know more regarding the behavior of the specific example given in my previous post. 

    From what I've learned so far, not all WinRT types support inheritance. I have no reliable source references for this, but I've read that the Windows.UI.Xaml classes should support inheritance, but other WinRT types won't. The Windows.UI.Xaml.Controls.Button class obviously does, while my own MyButton1 doesn't. I'd like to know what I'd have to do to make MyButton1 'inheritable' the way the Button class is.

     

    I've found that replacing the Windows.UI.Xaml.Controls.Button class with Windows.UI.Xaml.Controls.ProgressBar will make scenario 2 fail, which tells me that the ProgressBar class isn't (yet) possible to subclass. This observation is what makes me believe that a class need to do something explicit in order for it to be inheritable. In my real-world scenario, my problem is that I can't subclass the ProgressBar control to get references to UIElements in a template during OnApplyTemplateCore(), because I can't override OnApplyTemplateCore (because I can't subclass ProgressBar).

    Wednesday, February 15, 2012 3:50 PM