NoesisGUI

Extending NoesisGUI

github Tutorial Data

Being based in XAML, noesisGUI is a framework that can be extended in many ways. For example, you can create your own Converters, Commands, CustomControls, UserControls or Code-Behind classes. This tutorial is focused in the steps that must be performed to extend noesisGUI using your own classes. Obviously the steps are different depending on the language you want to use.

First thing we are going to do is writing a very simple XAML with a TextBox where you can write text and a TextBlock that outputs the text you write filtered through a converter. The converter we are going to implement transforms the input to upper case.

<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:Sample">
    <Grid.Resources>
        <local:UppercaseConverter x:Key="Converter"/>
    </Grid.Resources>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Background="Blue">
        <TextBox x:Name="Input" Width="100"/>
        <TextBlock Text="{Binding Text, ElementName=Input, Converter={StaticResource Converter}}" Margin="5"/>
    </StackPanel>
</Grid>

The highlighted line remarks where the extended class, in this case under the namespace Sample, is being used. If this XAML is compiled as it is, we will receive a warning indicating that the type Sample.UppercaseConverter is not known. We need to implement a new class registered with this name. Let's see how to do this.

C++

#include "pch.h"


using namespace Noesis;


class UppercaseConverter: public BaseValueConverter
{
public:
    NsBool TryConvert(BaseComponent* value, const Type* type, BaseComponent* parameter, Ptr<BaseComponent>& result)
    {
        if (targetType == TypeOf<NsString>() && Boxing::CanUnbox<NsString>(value))
        {
            NsString text = Boxing::Unbox<NsString>(value);
            text.make_upper();
            result = Boxing::Box<NsString>(text);
            return true;
        }

        return false;
    }

private:
    NS_IMPLEMENT_INLINE_REFLECTION(UppercaseConverter, BaseValueConverter)
    {
        NsMeta<TypeId>("Sample.UppercaseConverter");
    }
};

extern "C" NS_DLL_EXPORT void NsRegisterReflection(ComponentFactory* factory, NsBool registerComponents)
{
    NS_REGISTER_COMPONENT(UppercaseConverter)
}

The code shown above illustrates the key points that are needed to implement a new class:

  • New classes must always derive from their corresponding base class. In this case the base class is BaseValueConverter because we are interested in the IValueConverter interface.
  • The new class must implement the required methods. BaseValueConverter requires us to implement TryConvert and TryConvertBack, but in this specific sample, conversion in only one direction, we only need implementing TryConvert. It is implemented by unboxing the text, converting to uppercase and returning the boxed object.
  • And last, reflection tags must be added to our new class:
NS_IMPLEMENT_INLINE_REFLECTION(UppercaseConverter, BaseValueConverter)
{
    NsMeta<TypeId>("Sample.UppercaseConverter");
}

Here we are indicating that our class UppercaseConverter derives from BaseValueConverter and is registered with the name "UppercaseConverter" under the namespace Sample. This is the identifier that XAMLs will be using to reference this native class. Normally this identifier is the name of the class.

Classes that extend noesisGUI are packaged into dynamic libraries. Each dynamic library can be seen as a plugin that extends the functionality of noesisGUI. These plugins register the list of components that they implement. This registration is normally done in a single function named NsRegisterReflection(). Each component is registered using the macro NS_REGISTER_COMPONENT. For this sample, where only one class is implemented, the registration method is:

extern "C" NS_DLL_EXPORT void NsRegisterReflection(ComponentFactory* factory, NsBool registerComponents)
{
    NS_REGISTER_COMPONENT(UppercaseConverter)
}

Note that this function is only invoked automatically by NoesisGUI SDK tools, like BuildTool and XamlPlayer. If you are integrating noesisGUI into your own application you must manually invoke this function after noesisGUI initialization.

// NoesisGUI initialization
Noesis::GUI::InitDirectX9(DXUTGetD3D9Device(), ErrorHandler);

// Register own classes
NsRegisterReflection(0, true);

NOTE

NoesisGUI SDK tools like BuildTool and XamlPlayer automatically load dynamic libraries found in the current working directory.

And now that our class is properly registered, we can launch XamlPlayer (from the directory where our library is located) and test the XAML that makes use of our converter.

ExtendingTutorialImg1.jpg

C#

Everything is easier in C# because the registration is automatically done internally. You just need to create the class UppercaseConverter under the namespace Sample and implement the IValueConverter interface.

using System;
using System.Globalization;
using Noesis;

namespace Sample
{
    public class UppercaseConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return ((string)value).ToUpper();
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}
© 2017 Noesis Technologies