c++-cxc++-winrt

Converting the C++/CX "delegate" object over to its equivalent in C++/WinRT


I'm new to WinRT. I'm converting my Windows UWP app written C++/CX over to C++/WinRT. I have a C++/CX ref class that basically does the same thing as the Microsoft.VisualStudio.PlatformUI.DelegateCommand class does in C#. My C++ class implements ICommand where the Execute and CanExecute callbacks are handled by delegates. The abbreviated code for the header file looks like this:

public delegate void ExecuteDelegate(Platform::Object^ parameter);
public delegate bool CanExecuteDelegate(Platform::Object^ parameter);

public ref class DelegateCommand sealed :
    Windows::UI::Xaml::DependencyObject,
    Windows::UI::Xaml::Data::INotifyPropertyChanged,
    public Windows::UI::Xaml::Input::ICommand
{
public:
    DelegateCommand(ExecuteDelegate^ execute, CanExecuteDelegate^ canExecute);
    .
    .
    .
private:
    ExecuteDelegate^       m_executeDelegate = nullptr;
    CanExecuteDelegate^    m_canExecuteDelegate = nullptr;
    .
    .
    .
};

And a typical instantiation of my DelegateCommand class passes a weak pointer and a function pointer into the constructor from a class that has the implementation of the Execute and CanExecute methods:

Commands::Instance->UndoCommand = ref new DelegateCommand(
    ref new ExecuteDelegate(this, &SVGDocumentUserControl::ExecuteUndoCommand),
    ref new CanExecuteDelegate(this, &SVGDocumentUserControl::CanExecuteUndoCommand));

I'm sure this is a simple question, but it is unclear to me from what I have read, so exactly how would one define the two C++/CX delegates in C++/WinRT?

public delegate void ExecuteDelegate(Platform::Object^ parameter);
public delegate bool CanExecuteDelegate(Platform::Object^ parameter);

[Edit: I am also scratching my head on how to define the constructor's function pointers in the *.idl file.]

Thank you for your patience.


Solution

  • I see that your code has the delegates as public types (not internal). So, assuming that you really do need them to be part of the public API of your component, you simply define your delegates in MIDL3 (along with a test class with a constructor accepting them like this:

    namespace BlankApp1
    {
        delegate void ExecuteDelegate(Object param);
        delegate Boolean CanExecuteDelegate(Object param);
    
        [default_interface]
        runtimeclass TestClass
        {
            TestClass(ExecuteDelegate execute, CanExecuteDelegate canExecute);
        }
    }
    

    From there, you have projected types for these delegates, along with all the rich support provided by C++/WinRT for delegates. For instance, the implementation of TestClass is simple:

    struct TestClass : TestClassT<TestClass>
    {
        TestClass() = default;
        TestClass(BlankApp1::ExecuteDelegate const& execute, BlankApp1::CanExecuteDelegate const& canExecute)
            : m_execute(execute)
            , m_canExecute(canExecute)
        {}
    
    private:
        ExecuteDelegate m_execute;
        CanExecuteDelegate m_canExecute;
    };
    

    And instantiating that type, using C++/WinRT's rich delegate support is easy, and will give you the same lifetime safety semantics that you had in C++/CX (i.e., storing a weak ref to the object along with its member function pointer).

    
    struct MyClass : winrt::implements<MyClass, winrt::Windows::Foundation::IInspectable>
    {
        MyClass() = default;
    
        void Execute(winrt::Windows::Foundation::IInspectable const&)
        {
        }
    
        bool CanExecute(winrt::Windows::Foundation::IInspectable const&)
        {
            return true;
        }
    
        TestClass CreateCommand()
        {
            return TestClass{
                ExecuteDelegate{ get_weak(), &MyClass::Execute },
                CanExecuteDelegate{ get_strong(), &MyClass::CanExecute }
            };
        }
    };