Implementing Custom Markup Extension in C++
Hi, sorry, I am stucked again when trying to translate the following WPF Markup extension code:
#original code is here: http://www.mobilemotion.eu/?p=1837 XAML
I tried to create an EventBindingExtension class inheriting from BaseComponent and IMarkupExtension as follows (still empty).
When I ran the code (just to make sure NoesisGUI can find EventBindingExtension class and instatiate it), NoesisGUI complained with the following error:
If possible, could you show me the full C++ version of the EventBindingExtension ?
Thanks.
#original code is here: http://www.mobilemotion.eu/?p=1837 XAML
Code: Select all
<Grid Background="White" MouseDown="{local:EventBinding MouseDownCommand}">
</Grid>
Code: Select all
class EventBindingExtension : public BaseComponent, public IMarkupExtension
{
public:
EventBindingExtension() {
}
EventBindingExtension(const NsChar * command) {
}
virtual Ptr<Core::BaseComponent> ProvideValue(const void* context) override {
return nullptr;
}
NS_IMPLEMENT_INTERFACE_FIXUP
NS_IMPLEMENT_INLINE_REFLECTION(EventBindingExtension, BaseComponent)
{
NsMeta<TypeId>("EventBinding");
}
};
Debugging showed that NoesisGUI instantiated EventBindingExtension using the default constructor, while I am expecting it used the second constructor. Do I have to register the second constructor ? How should I do that ? I have tried to comb through the reflection related headers to find APIs to register constructor but couldn't find ones. And documentation is still showing old (pre 2.0) APis (NsCtr).Gui\Core\Src\XamlNode.cpp:573:Can't convert 'MouseDownCommand' into a 'EventBinding' object (@10,4)
If possible, could you show me the full C++ version of the EventBindingExtension ?
Thanks.
-
-
sfernandez
Site Admin
- Posts: 3203
- Joined:
Re: Implementing Custom Markup Extension in C++
Our architecture only permits creating objects specified in a XAML using a default constructor (it is how our ComponentFactory works).
What you want to do is solved by exposing the command as a property of the markup extension and specify that property as the content property of that type (using the ContentPropertyMetaData in the reflection).
You also missed to specify the interface implementation in the reflection block by using NsImpl<T> macro.
Another thing to take into account is that we don't support directyly assigning delegates in xaml to events, but you can do the connection inside ProvideValue (similar to the code I suggested in viewtopic.php?f=3&t=1147&sid=bc63861558 ... 7208ee4fd7).
Find the corresponding code here:
What you want to do is solved by exposing the command as a property of the markup extension and specify that property as the content property of that type (using the ContentPropertyMetaData in the reflection).
You also missed to specify the interface implementation in the reflection block by using NsImpl<T> macro.
Another thing to take into account is that we don't support directyly assigning delegates in xaml to events, but you can do the connection inside ProvideValue (similar to the code I suggested in viewtopic.php?f=3&t=1147&sid=bc63861558 ... 7208ee4fd7).
Find the corresponding code here:
Code: Select all
struct EventBindingExtension: public BaseComponent, public IMarkupExtension
{
EventBindingExtension() { }
const NsChar* GetCommandName() const { return commandName.c_str(); }
void SetCommandName(const NsChar* name) { commandName = name; }
Ptr<BaseComponent> ProvideValue(const void* context) override
{
auto provider = (const XamlReaderProvider*)context;
auto element = NsDynamicCast<UIElement*>(provider->GetTargetObject());
if (element != 0)
{
auto eventId = provider->GetPropertyName();
auto type = element->GetClassType();
// look in UIElementData for the routed event
auto event = FindRoutedEvent(type, eventId);
if (event != 0)
{
// TODO: connect event with a delegate that could resolve
// commandName from element DataContext
//element->AddHandler(event, ...);
}
}
return nullptr;
}
NS_IMPLEMENT_INTERFACE_FIXUP
private:
// ...
NsString commandName;
NS_IMPLEMENT_INLINE_REFLECTION(EventBindingExtension, BaseComponent)
{
NsMeta<TypeId>("EventBinding");
NsMeta<ContentPropertyMetaData>("CommandName");
NsImpl<IMarkupExtension>();
NsProp("CommandName", &EventBindingExtension::GetCommandName,
&EventBindingExtension::SetCommandName);
}
};
Re: Implementing Custom Markup Extension in C++
Thanks for the code.
If ProvideValue returns nullptr, Noesis complains that the object to assign is incompatible. Is there any way to avoid this error message ?
If ProvideValue returns nullptr, Noesis complains that the object to assign is incompatible. Is there any way to avoid this error message ?
-
-
sfernandez
Site Admin
- Posts: 3203
- Joined:
Re: Implementing Custom Markup Extension in C++
The problem is in our xaml parser, that is considering the event as another property and is trying to set the value. You can ignore that error right now.
Anyway, could you please report it in our bugtracker, we will solve it as soon as possible.
Anyway, could you please report it in our bugtracker, we will solve it as soon as possible.
Re: Implementing Custom Markup Extension in C++
Ok, I submitted a bug here: #1136
Re: Implementing Custom Markup Extension in C++
Sorry, an additional question on the details.
I implemented a routine to find the command object inside the data context as follows:
# I intentionaly stripped out null checking to shorten the code
Is it already correct ? Is there any helper API that I can use to shorten the code (especially the one that iterate through type's hierarchy)
Thanks.
I implemented a routine to find the command object inside the data context as follows:
# I intentionaly stripped out null checking to shorten the code
Is it already correct ? Is there any helper API that I can use to shorten the code (especially the one that iterate through type's hierarchy)
Thanks.
-
-
sfernandez
Site Admin
- Posts: 3203
- Joined:
Re: Implementing Custom Markup Extension in C++
Yes, your code seems to be correct. To simplify it you can use Reflection::FindProperty, it returns the TypeProperty and TypeClass where the property was found.
But maybe you can define an interface that your viewmodels can use to optimize that:
And then you can use it like this (null checks removed for clarity):
But maybe you can define an interface that your viewmodels can use to optimize that:
Code: Select all
NS_INTERFACE ICommandFinder: public Noesis::Interface
{
virtual ICommand* FindCommand(const NsChar* commandName) const = 0;
NS_IMPLEMENT_INLINE_REFLECTION_(ICommandFinder, Noesis::Interface)
};
Code: Select all
static ICommand* GetCommand(const FrameworkElement* element, const NsChar* commandName) {
auto context = element->GetDataContext();
return NsDynamicCast<ICommandFinder*>(context)->FindCommand(commandName);
}
Re: Implementing Custom Markup Extension in C++
Great. Thanks for the tips !
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 8 guests