ngzaharias
Topic Author
Posts: 11
Joined: 10 Oct 2017, 06:02

How to make a custom TriggerAction

24 Aug 2025, 01:52

Hi there,

I've been trying to get a trigger action working without success.
(Custom Engine in C++, version 3.2.8-indie)

Using the samples as a reference I need to
1. Create a custom class that inherits from `NoesisApp::TriggerActionT<Noesis::DependencyObject>`
2. Implement the reflection in the .h and .cpp using `NS_DECLARE_REFLECTION` and `NS_IMPLEMENT_REFLECTION`
3. In the reflection, set the name and namespace for the action.
4. In the xaml, make an alias for the namespace using `xmlns:l="clr-namespace:..."`
5. Trigger the action using `<Button.Triggers> <EventTrigger ...> <l:Action ...>`
// UIAction.h
#pragma once

#include <NsApp/TriggerAction.h>
#include <NsGui/DependencyObject.h>

namespace eng
{
	class UIAction final : public NoesisApp::TriggerActionT<Noesis::DependencyObject>
	{
	protected:
		Noesis::Ptr<Noesis::Freezable> CreateInstanceCore() const override;

		void Invoke(Noesis::BaseComponent* parameter) override;

	private:
		NS_DECLARE_REFLECTION(UIAction, NoesisApp::TriggerActionT<Noesis::DependencyObject>)
	};
}
// UIAction.cpp
#include "EnginePCH.h"
#include "Engine/UIAction.h"

#include <NsCore/ReflectionImplement.h>
#include <NsGui/DependencyData.h>

Noesis::Ptr<Noesis::Freezable> eng::UIAction::CreateInstanceCore() const
{
	return *new eng::UIAction();
}

void eng::UIAction::Invoke(Noesis::BaseComponent*)
{
	Z_LOG(ELog::Debug, "UIAction");
}

NS_IMPLEMENT_REFLECTION(eng::UIAction, "eng.UIAction")
{
}

// MainWindow.xaml
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:local="clr-namespace:eng"
  Width="400" 
  Height="200">
  <Grid>
    <Button Content="Click Me" Width="120" Height="40" HorizontalAlignment="Center" VerticalAlignment="Center">
      <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
          <local:UIAction/>
        </EventTrigger>
      </Button.Triggers>
    </Button>
  </Grid>
</Page>
[NOESIS/INFO] Noesis Init v3.2.8 (Windows on x86_64 Profile)
[NOESIS/INFO] Inspector listening on port 17629
[NOESIS/INFO] 'Themes/Default.Brushes.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Fonts.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Styles.xaml' loaded
[NOESIS/INFO] 'Themes/Default.xaml' loaded
[NOESIS/ERROR] MainWindow.xaml(10,11): Unknown element type 'local:UIAction' (eng.UIAction)
[NOESIS/INFO] 'MainWindow.xaml' loaded
[NOESIS/INFO] 'pack://application:,,,/Noesis.GUI.Extensions;component/Theme/Fonts/#PT Root UI' loaded
[NOESIS/INFO] 'Themes/Default.Brushes.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Fonts.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Styles.xaml' loaded
[NOESIS/INFO] 'Themes/Default.xaml' loaded
[NOESIS/INFO] 'pack://application:,,,/Noesis.GUI.Extensions;component/Theme/Fonts/#PT Root UI' loaded
 
ngzaharias
Topic Author
Posts: 11
Joined: 10 Oct 2017, 06:02

Re: How to make a custom TriggerAction

24 Aug 2025, 13:03

I've also tried using the example from the Samples with PlaySoundAction
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
  xmlns:local="clr-namespace:eng"
  Width="400" 
  Height="200">
  <b:Interaction.Triggers>
    <b:EventTrigger EventName="Loaded">
      <b:PlaySoundAction Source="Login.mp3" Volume="0.5"/>
    </b:EventTrigger>
  </b:Interaction.Triggers>
  <Grid>
    <Button Content="Click Me" Width="120" Height="40" HorizontalAlignment="Center" VerticalAlignment="Center">
    </Button>
  </Grid>
</Page>
[NOESIS/INFO] Noesis Init v3.2.8 (Windows on x86_64 Profile)
[NOESIS/INFO] Inspector listening on port 17629
[NOESIS/INFO] 'Themes/Default.Brushes.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Fonts.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Styles.xaml' loaded
[NOESIS/INFO] 'Themes/Default.xaml' loaded
[NOESIS/ERROR] MainWindow.xaml(1,1): Unknown property type 'b:Interaction' (NoesisApp.Interaction)
[NOESIS/ERROR] MainWindow.xaml(7,3): Unknown property type 'b:Interaction' (NoesisApp.Interaction)
[NOESIS/INFO] 'MainWindow.xaml' loaded
[NOESIS/INFO] 'pack://application:,,,/Noesis.GUI.Extensions;component/Theme/Fonts/#PT Root UI' loaded
[NOESIS/INFO] 'Themes/Default.Brushes.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Fonts.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Styles.xaml' loaded
[NOESIS/INFO] 'Themes/Default.xaml' loaded
[NOESIS/INFO] 'pack://application:,,,/Noesis.GUI.Extensions;component/Theme/Fonts/#PT Root UI' loaded
 
User avatar
jsantos
Site Admin
Posts: 4822
Joined: 20 Jan 2012, 17:18
Contact:

Re: How to make a custom TriggerAction

25 Aug 2025, 16:25

Our C++ SDK includes the source code for the App Framework. It comes with many built-in actions, including the one you mentioned: PlaySoundAction. You can see how it is used in the Buttons sample.

Regarding the error you encountered:
[NOESIS/ERROR] MainWindow.xaml(10,11): Unknown element type 'local:UIAction' (eng.UIAction)
This usually happens because your class hasn’t been registered in the factory. Every extension class must be registered before it can be used in XAML. Please check our samples to see the correct way to register your class.
 
ngzaharias
Topic Author
Posts: 11
Joined: 10 Oct 2017, 06:02

Re: How to make a custom TriggerAction

25 Aug 2025, 23:50

Hi jsantos, thanks for taking the time to reply!

I'm trying to integrate with Noesis without using the App Framework since I would like to maintain control over windows. I've been trying to use the samples as a reference but I run into blockers that I assume means I've missed some implementation steps, however it is hard to understand exactly what was missed.

Is there something that the App Framework does that I will need to also ensure I do (so far I've followed the integration guide and sample).

On a separate note I'm currently getting the following errors.
[NOESIS/ERROR] MainWindow.xaml(11,11): Cannot add a 'eng.UIAction' to a 'TriggerActionCollection'
// MainWindow.xaml
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
  xmlns:local="clr-namespace:eng"
  Width="400" 
  Height="200">
  <Grid>
    <Button Content="Click Me" Width="120" Height="40" HorizontalAlignment="Center" VerticalAlignment="Center">
      <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
          <local:UIAction/>
        </EventTrigger>
      </Button.Triggers>
    </Button>
  </Grid>
</Page>
The only things I can think of are
1. I'm using the wrong register function and I should be registering this with an Application like you do in the Samples.
(Looking at the macro behind this though, it is just registering it regularly).
2. I need an module + package to make use of these things.
3. I'm not inheriting from TriggerAction correctly.
4. I need to reflect a property or something else.

In the case of 3, below is my source code and can see that I do inherit from TriggerAction the same was that the PlaySoundAction does. The only difference is I don't make use of any properties.
// UIAction.h
#include <NsApp/TriggerAction.h>
#include <NsGui/DependencyObject.h>

namespace eng
{
	class UIAction final : public NoesisApp::TriggerActionT<Noesis::DependencyObject>
	{
	public:
		UIAction();

	protected:
		Noesis::Ptr<Noesis::Freezable> CreateInstanceCore() const override;

		void Invoke(Noesis::BaseComponent* parameter) override;

	private:
		NS_DECLARE_REFLECTION(UIAction, NoesisApp::TriggerActionT<Noesis::DependencyObject>)
	};
}
// UIAction.cpp
#include "EnginePCH.h"
#include "Engine/UIAction.h"

#include <NsCore/ReflectionImplement.h>
#include <NsGui/DependencyData.h>

eng::UIAction::UIAction()
{
}

Noesis::Ptr<Noesis::Freezable> eng::UIAction::CreateInstanceCore() const
{
	return *new eng::UIAction();
}

void eng::UIAction::Invoke(Noesis::BaseComponent*)
{
	Z_LOG(ELog::Debug, "UIAction");
}

NS_IMPLEMENT_REFLECTION(eng::UIAction, "eng.UIAction")
{
	NsMeta<Noesis::DependencyData>(Noesis::TypeOf<SelfClass>());
}
 
User avatar
jsantos
Site Admin
Posts: 4822
Joined: 20 Jan 2012, 17:18
Contact:

Re: How to make a custom TriggerAction

26 Aug 2025, 15:38

Before analyzing new problems, please try using PlaySoundAction first. Copy it from the app framework into your codebase, register it, and use it in a XAML file. This should work without any issues.

Regarding this error:
[NOESIS/ERROR] MainWindow.xaml(11,11): Cannot add a 'eng.UIAction' to a 'TriggerActionCollection'
You are mixing core triggers with interactivity triggers. This distinction is explained in the Behaviors Tutorial.

But let’s first get PlaySoundAction working. It will be easier to tackle the issues one at a time.
 
ngzaharias
Topic Author
Posts: 11
Joined: 10 Oct 2017, 06:02

Re: How to make a custom TriggerAction

26 Aug 2025, 19:13

[NOESIS/INFO] Noesis Init v3.2.8 (Windows on x86_64 Profile)
[NOESIS/INFO] Inspector listening on port 17629
[NOESIS/INFO] 'Themes/Default.Brushes.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Fonts.xaml' loaded
[NOESIS/INFO] 'Themes/Default.Styles.xaml' loaded
[NOESIS/INFO] 'Themes/Default.xaml' loaded
[NOESIS/ERROR] MainWindow.xaml(9,5): Unknown property type 'b:Interaction' (NoesisApp.Interaction)
[NOESIS/ERROR] MainWindow.xaml(10,7): Unknown property type 'b:Interaction' (NoesisApp.Interaction)
[NOESIS/INFO] 'MainWindow.xaml' loaded
[NOESIS/INFO] 'pack://application:,,,/Noesis.GUI.Extensions;component/Theme/Fonts/#PT Root UI' loaded
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
  xmlns:local="clr-namespace:eng"
  Width="400" 
  Height="200">
  <Grid>
    <Button Content="Click Me" Width="120" Height="40" HorizontalAlignment="Center" VerticalAlignment="Center">
      <b:Interaction.Triggers>
        <b:EventTrigger EventName="Click">
          <b:PlaySoundAction Source="Sounds/ExitButton.wav"/>
        </b:EventTrigger>
      </b:Interaction.Triggers>
    </Button>
  </Grid>
</Page>
 
User avatar
jsantos
Site Admin
Posts: 4822
Joined: 20 Jan 2012, 17:18
Contact:

Re: How to make a custom TriggerAction

26 Aug 2025, 21:13

If you are selectively picking things from the Application Framework, make you are registering and initializing everything yourself.

For example, make sure to copy the registration from App.Interactivity.cpp:
NS_REGISTER_REFLECTION(App, Interactivity)
{
    // Force creation of metadata
    Noesis::TypeOf<NoesisApp::Interaction>();
    Noesis::TypeOf<NoesisApp::StyleInteraction>();

    NS_REGISTER_COMPONENT(NoesisApp::BehaviorCollection)
    NS_REGISTER_COMPONENT(NoesisApp::TriggerCollection)
    NS_REGISTER_COMPONENT(NoesisApp::StyleBehaviorCollection)
    NS_REGISTER_COMPONENT(NoesisApp::StyleTriggerCollection)
    NS_REGISTER_COMPONENT(NoesisApp::EventTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::PropertyChangedTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::DataTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::DataEventTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::KeyTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::GamepadTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::StoryboardCompletedTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::TimerTrigger)
    NS_REGISTER_COMPONENT(NoesisApp::LoadContentAction)
    NS_REGISTER_COMPONENT(NoesisApp::MouseDragElementBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::TranslateZoomRotateBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::ConditionBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::ConditionalExpression)
    NS_REGISTER_COMPONENT(NoesisApp::ComparisonCondition)
    NS_REGISTER_COMPONENT(NoesisApp::GoToStateAction)
    NS_REGISTER_COMPONENT(NoesisApp::InvokeCommandAction)
    NS_REGISTER_COMPONENT(NoesisApp::ChangePropertyAction)
    NS_REGISTER_COMPONENT(NoesisApp::ControlStoryboardAction)
    NS_REGISTER_COMPONENT(NoesisApp::RemoveElementAction)
    NS_REGISTER_COMPONENT(NoesisApp::LaunchUriOrFileAction)
    NS_REGISTER_COMPONENT(NoesisApp::PlaySoundAction)
    NS_REGISTER_COMPONENT(NoesisApp::PlayMediaAction)
    NS_REGISTER_COMPONENT(NoesisApp::PauseMediaAction)
    NS_REGISTER_COMPONENT(NoesisApp::RewindMediaAction)
    NS_REGISTER_COMPONENT(NoesisApp::StopMediaAction)
    NS_REGISTER_COMPONENT(NoesisApp::SetFocusAction)
    NS_REGISTER_COMPONENT(NoesisApp::MoveFocusAction)
    NS_REGISTER_COMPONENT(NoesisApp::SelectAction)
    NS_REGISTER_COMPONENT(NoesisApp::SelectAllAction)
    NS_REGISTER_COMPONENT(NoesisApp::CollectionFilterBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::CollectionSortBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::BackgroundEffectBehavior)
    NS_REGISTER_COMPONENT(NoesisApp::LineDecorationBehavior)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::ComparisonConditionType>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::ForwardChaining>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::KeyTriggerFiredOn>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::GamepadTriggerFiredOn>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::GamepadButton>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::ControlStoryboardOption>)
    NS_REGISTER_COMPONENT(Noesis::EnumConverter<NoesisApp::FocusDirection>)
}

 
ngzaharias
Topic Author
Posts: 11
Joined: 10 Oct 2017, 06:02

Re: How to make a custom TriggerAction

27 Aug 2025, 00:49

Alright awesome, that was what I was missing (plus calling the NsRegisterReflection and NsInitPackage functions for App + Interactivity).

For anyone else that ends up in the same situation.
If you've followed the integration guide/sample and have Noesis running, the last step that is needed is to reflect and register the App + Interactivity (package + module).

1. Copy App.Interactivity.cpp from the Application Framework.
2. Include the file where you call Noesis::Init
3. Call the functions that are created via the reflection call in init and shutdown.
*note: The Application Framework does this via macros inside the register.cpp.
// App.Interactivity.cpp
NS_REGISTER_REFLECTION(App, Interactivity)
{
...

// YourFile.cpp
{
void MyProgram::Init()
{
	Noesis::Init();	
	NsRegisterReflectionAppInteractivity();
	NsInitPackageAppInteractivity();
}

void MyProgram::Shutdown()
{
	NsShutdownPackageAppInteractivity();
}
Thanks for being patient with me jsantos!
 
User avatar
jsantos
Site Admin
Posts: 4822
Joined: 20 Jan 2012, 17:18
Contact:

Re: How to make a custom TriggerAction

28 Aug 2025, 18:24

Of course, I’m here to help.

Thank you for your feedback, and I’m glad to hear you were able to resolve the issue.

Who is online

Users browsing this forum: No registered users and 5 guests