Events in NoesisGUI

NoesisGUI is an event driven framework where all controls expose a range of events that you may subscribe to. You can subscribe to these events, which means that your application will be notified when they occur and you may react to that.

There are many types of events, but some of the most commonly used are there to respond to the user's interaction. On most controls you will find events like KeyDown, KeyUp, MouseDown, MouseUp, TouchDown, TouchUp. For example, in the events section of the UIElement documentation you can find a list of all the events exposed by that class. The same for the rest of classes.

Subscription

Two different ways can be used to subscribe to events in NoesisGUI: with and without code-behind. There is also a third alternative using Commands that totally decouples the view from the model.

Direct subscription

The easiest way to subscribe to an event is by directly adding a callback to it by using a delegate. The object is reached using FindName, so you need to use the x:Name keyword to set the name of the desired instance. For example:

Ptr<Grid>root=Noesis::GUI::LoadXaml<Grid>("Grid.xaml");Button*button=root->FindName<Button>("button");button->Click()+=[](BaseComponent*sender,constRoutedEventArgs&args){printf("Button was clicked");};

C#

Gridroot=(Grid)Noesis.GUI.LoadXaml("Grid.xaml");Buttonbutton=(Button)root.FindName("button");button.Click+=(objectsender,RoutedEventArgsargs)=>{System.Console.WriteLine("Button was clicked");};

Code-behind subscription

The alternative to direct subscription is using a code-behind class and connecting events in the XAML by using method names. These method names need to be implemented in the code-behind class using the correct event signature. For example:

Read the Extending Noesis tutorial to know more about implementing code-behind classes

FrameworkElement exposes the virtual function ConnectEvent that is invoked for each hooked event when the XAML is loaded. You need to override that function accordingly. In C++ the macro NS_CONNECT_EVENT is provided as a helper to easily implement ConnectEvent. You must use if for each event you want to connect to.

C++

classMyGrid:publicGrid{public:MyGrid(){InitializeComponent();}private:voidInitializeComponent(){Noesis::GUI::LoadComponent(this,"Grid.xaml");}boolConnectEvent(BaseComponent*source,constchar*event,constchar*handler)override{NS_CONNECT_EVENT(Button,Click,OnButton1Click);NS_CONNECT_EVENT(Button,Click,OnButton2Click);returnfalse;}voidOnButton1Click(BaseComponent*sender,constRoutedEventArgs&args){printf("Button1 was clicked");}voidOnButton2Click(BaseComponent*sender,constRoutedEventArgs&args){printf("Button2 was clicked");}NS_IMPLEMENT_INLINE_REFLECTION(MyGrid,Grid){NsMeta<TypeId>("MyGrid");}};

C#

publicclassMyGrid:Grid{publicMyGrid(){InitializeComponent();}privatevoidInitializeComponent(){Noesis.GUI.LoadComponent(this,"Grid.xaml");}protectedoverrideboolConnectEvent(objectsource,stringeventName,stringhandlerName){if(eventName=="Click"&&handlerName=="OnButton1Click"){((Button)source).Click+=this.OnButton1Click;returntrue;}if(eventName=="Click"&&handlerName=="OnButton2Click"){((Button)source).Click+=this.OnButton2Click;returntrue;}returnfalse;}privatevoidOnButton1Click(objectsender,RoutedEventArgsargs){System.Console.WriteLine("Button1 was clicked");}privatevoidOnButton2Click(objectsender,RoutedEventArgsargs){System.Console.WriteLine("Button2 was clicked");}}

Note that sometimes using FindName is not a valid option and using code-behind member functions is the only way to connect to events. For example when using a DataTemplate the named elements in the visual tree cannot be accessed using FindName because the data template is replicated for each item.

Loaded and the Initialized event

In Noesis, controls have both an Initialized event and a Loaded event. Understanding when those events happen is very important to properly initialize any control class. Here is some background on how these events work.

Initialized Event

The Initialized event says just that an element has been created and its properties have all been set, and as a consequence this usually fires on children before their parent. So when Initialized is raised on an element, its whole sub-tree is likely initialized, but its parent is not. The Initialized event is typically fired when the XAML for a sub-tree is loaded. This event corresponds to the IsInitialized property.

Instead of having code in the constructor of the class, you should always use the Initialized event.

Loaded Event

Sometimes the Initialized event is not enough. For example, you may want to know the ActualWidth of an element, but when Initialized is fired, the ActualWidth value hasn’t been calculated yet. Or you may want to look at the value of a data-bound property, but that hasn’t been calculated yet either.

To deal with this, the Loaded event says that the element is not only built and initialized, but layout has run on it, data has been bound, it's connected to a View and you're on the verge of being rendered. When that point is reached, the Loaded event is broadcasted, starting at the root of the tree. This event corresponds to the IsLoaded property.

Symmetrically to the Loaded event, the Unloaded event occurs when the element is removed from within an element tree of loaded elements.

Note

If you’re not sure which event to use, use the Loaded event; it’s more often the right choice.