NoesisGUI

Blend Integration Tutorial

zip Tutorial Data

Note

For a quick and first-hand tutorial on Blend we recommend reading the document found in the Unity section

What is Blend?

Microsoft Expression Blend is a visual tool for creating eye candy, vector based, graphical user interfaces. You build an interface by drawing shapes and paths, drawing controls such as buttons and list boxes, making the pieces of your application respond to mouse clicks and other user input, and styling everything to look uniquely your own.

What you see on the design surface in Expression Blend while you create your interface is what users will see when they run your application.

In Expression Blend, you work on real pieces of a working application, but you can still draw and style everything as easily as you can in other illustration software, or event import your designs from other popular vector graphics tools like Adobe Illustrator. When you want to draw something that represents an interactive control, you can actually select an available control (such as a button or a list box) and then style it, or create your control from scratch; in any case, the powerful skinning mechanism will allow to fully customize the visual appearance.

What does Blend produce?

Expression Blend produces a visual design that is represented by a text file in XAML format. Just as HTML is the markup language for web applications, XAML is the markup language for Blend applications. These XAML files, together with other resources such as images or fonts, will be the input of NoesisGui applications.

This tutorial is based on Microsoft Expression Blend 4, and will show you how to use it to design application user interfaces to be imported and executed by NoesisGui Framework.

Which parts are supported by NoesisGUI?

The following sections describe the functionality provided by Blend that is implemented by NoesisGUI. As NoesisGUI becomes more mature this subset will be increased with new releases.

Project Types

When creating a new project, Blend shows the following dialog:

ProjectType.png

There are two types of technologies supported by Blend: WPF and Silverlight. Microsoft designed WPF to provide a consistent programming model for the development of desktop applications. Silverlight was created later to bring the power of WPF to the web. Silverlight defines a simpler runtime but respect to WPF it has few differences: it implements less controls, it has more limited styling capabilities (triggers), however includes a new property that simulates 3D transformations applied to graphic elements (the Projection property). Currently NoesisGui supports the creation of WPF and Silverlight projects from Blend.

After selecting the WPF technology, you should choose from Application or Control Library, which are the two project types supported by NoesisGui.

For this tutorial we will create a fully functional application as described in the application tutorial

Creating Main Window Interface

Use Expression Blend to add and to organize controls and graphical elements within the application main window (if you are not familiar with this tool, you should read Blend user guide to learn the basics on how to use it). Controls and other elements can be selected in Blend from the Assets tab or directly from the appropriate buttons on the left tool bar.

After some work we ended with something similar to this:

MainWindowAero.png

NOTE: When adding a TextBlock to the window or when double-clicking in an existing TextBlock, Expression Blend by default tries to set TextBlock content as inline elements. Inline and similar elements are not supported yet by NoesisGui, so currently only plain text can be placed inside the Text property of the TextBlock. The correct way to edit Text property is to select the TextBlock control in the window (one-click), and modify the Text property in the Properties tab panel (usually in the right side of Expression Blend).

TextBlockNote.png

Control Styling

It's important to understand the different techniques to change the visual appearance of an element. Style, Template, Skin and Theme are the base concepts to unveil and master the customization capabilities that XAML has to offer. We recommend reading this brief introduction to Styles and Templates, along as the Blend documentation about the matter.

For our application we can add a personal look by adding our custom control styles. If you already have a XAML file which defines a set of resources and styles, you can add it to the project:

AddExistingItem.png SimpleStyleAdded.png

NOTE: This action will create a local copy of SimpleStyle.xaml if the source file is outside project directory or subdirectories. So this method is not usually recommended.

<Application
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="BlendTutorial.App"
  StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="SimpleStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

An alternative is to reference an existing assembly that contains shared resources:

AddReference.png GuiThemesAssemblyAdded.png

NOTE: This is the prefered method to share resources amongst projects.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="BlendTutorial.App"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <!-- Resources scoped at the Application level should be defined here. -->
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Gui.Themes;component/SimpleStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Or we can create a new style for that particular application. To organize resources we will create a new ResourceDictionary and place all style definitions there:

AddNewItem.png NewResourceDictionary.png

We will begin style edition by selecting one of the window controls, the AddButton for example, and then go to the Object menu -> Edit Style -> Create Empty...

CreateButtonStyle.png

Then we customize button style by setting some properties. And finally we create the button template that will give our personal appearance to the button.

CreateButtonTemplate.png ButtonTemplate.png

After creating and editing the styles and templates of all the controls we are using, our main window looks like this:

MainWindowCustom.png

User Controls

Sometimes we need to create controls that can be reused in other applications. In this case it is best to create a user control consisting of standard controls, so that control can assimilate the appearance of the application where it is used, while maintaining the logic needed to operate unchanged. Now we will add a user control for our application to define a color selector:

AddNewItem.png NewUserControl.png

The user control appearance is defined by the composition of standard controls. Then we add the logic to manage the color selector, and the control is ready to use in our application:

MainWindowColorSelector.png

More detailed information is available in the user controls tutorial.

More Customization: Fonts

NoesisGui Framework does not work with operating system fonts, but local resource fonts. So if your want to use a particular font in your application, you must use the Embed option located in the Text properties of the Properties panel:

EmbedFont.png

Blend places under Fonts folder (inside project directory) all the embedded fonts. So the font can be referenced in the XAML as a local resource:

<Window FontFamily="/BlendTutorial;component/Fonts/#FrancophilSans">

Or we can use fonts defined in referenced assemblies:

<Window FontFamily="/Gui.Fonts;component/#Roboto">

C++ Implementation

Now that all design and artwork is completed we have to implement the logic. Blend creates two types of files for every item that is added to the project: a XAML file and a C# (or VB if selected when creating the project) file that is usually called code-behind. In the latter, a matching class to the element defined in the XAML is created along with the methods that will respond to the desired events. The user is free to modify this code and add the logic to be executed as response to the events, or even add any other method or property that is required by the application. This powerful feature of separating visual definition from logic code allows a efficient workflow because programmers and designers can work separately on each part.

For NoesisGui to work, we need to translate that C# code-behind to C++, and the first step will be to create a Noesis component that matches each class created by Blend.

Application

In our tutorial we have an Application derived class that contains the App.xaml code behind. That file was created automatically by Expression Blend and can be modified using Blend code editor:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Windows;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
    }
}

The App.xaml refers to this class using the x:Class extension. And, as we can see, it follows the form Namespace.ClassName:

 <Application
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   x:Class="BlendTutorial.App"
   StartupUri="MainWindow.xaml">
     <Application.Resources>
         <ResourceDictionary>
             <ResourceDictionary.MergedDictionaries>
                 <ResourceDictionary Source="Resources.xaml"/>
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
     </Application.Resources>
 </Application>

To match that class we have to create a C++ component that derives from Application and that has BlendTutorial.App as TypeId.

#pragma warning(disable: 4275)
#pragma warning(disable: 4251)


#include <Noesis.h>
#include <NsGui/Application.h>
#include <NsCore/ReflectionImplement.h>
#include <NsCore/TypeId.h>
#include <NsCore/Package.h>


using namespace Noesis;
using namespace Noesis::Core;
using namespace Noesis::Gui;


////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialApp: public Application
{
    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialApp, Application)
    {
        NsMeta<TypeId>("BlendTutorial.App");
    }
};

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

MainWindow

For the MainWindow.xaml file, Expression Blend created a Window derived class and added an event handler for each event managed by the class:

 <Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
   xmlns:local="clr-namespace:BlendTutorial"
   x:Class="BlendTutorial.MainWindow"
   x:Name="Window"
   Title="Noesis - Getting started with Blend"
   Width="640" Height="480" Background="Gray" UseLayoutRounding="True" FontFamily="/BlendTutorial;component/Fonts/#FrancophilSans">

     <Grid x:Name="LayoutRoot">
         <Grid.ColumnDefinitions>
             <ColumnDefinition/>
             <ColumnDefinition Width="Auto"/>
         </Grid.ColumnDefinitions>
         <Border x:Name="ContainerBorder" BorderBrush="DimGray" BorderThickness="1" Margin="10" CornerRadius="2" Background="White" Padding="1" ClipToBounds="True"
           PreviewMouseLeftButtonDown="ContainerBorder_PreviewMouseLeftButtonDown">
             <Canvas x:Name="ContainerCanvas"/>
         </Border>
         <StackPanel HorizontalAlignment="Right" Orientation="Vertical" VerticalAlignment="Top" Grid.Column="1" Margin="0,10,10,10" Width="150">
             <Button x:Name="AddButton" Content="Add" Margin="0,0,0,3" Click="AddButton_Click"/>
             <Button x:Name="RemoveButton" Content="Remove" Margin="0,0,0,10" Click="RemoveButton_Click"/>
             <Expander Header="Position" Margin="0,0,0,10" IsExpanded="True">
                 <Grid Margin="5">
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition/>
                         <ColumnDefinition Width="2*"/>
                     </Grid.ColumnDefinitions>
                     <Grid.RowDefinitions>
                         <RowDefinition/>
                         <RowDefinition/>
                     </Grid.RowDefinitions>
                     <TextBlock Text="Left " VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0"/>
                     <Slider x:Name="LeftValue" Margin="0,0,0,3" Grid.Column="1" Maximum="{Binding ActualWidth, ElementName=ContainerCanvas}"
                       LargeChange="10" SmallChange="1"/>
                     <TextBlock Text="Top " VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="1"/>
                     <Slider x:Name="TopValue" Margin="0,0,0,3" Grid.Column="1" Grid.Row="1" Maximum="{Binding ActualHeight, ElementName=ContainerCanvas}"
                       LargeChange="10" SmallChange="1"/>
                 </Grid>
             </Expander>
             <Expander Header="Size" Margin="0,0,0,10" IsExpanded="True">
                 <Grid Margin="5">
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition/>
                         <ColumnDefinition Width="2*"/>
                     </Grid.ColumnDefinitions>
                     <Grid.RowDefinitions>
                         <RowDefinition/>
                         <RowDefinition/>
                     </Grid.RowDefinitions>
                     <TextBlock Text="Width " VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0"/>
                     <Slider x:Name="WidthValue" Grid.Column="2" Margin="0,0,0,3" LargeChange="10" SmallChange="1" Maximum="200" Minimum="10"/>
                     <TextBlock Text="Height " VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="1"/>
                     <Slider x:Name="HeightValue" Grid.Column="2" Grid.Row="1" LargeChange="10" SmallChange="1" Maximum="200" Minimum="10"/>
                 </Grid>
             </Expander>
             <Expander Header="Color" Margin="0,0,0,10" IsExpanded="True">
                 <StackPanel Margin="5" Orientation="Vertical" ToggleButton.Checked="RadioButton_Checked">
                     <RadioButton x:Name="FillRadioButton" Content="Fill" IsChecked="True"/>
                     <RadioButton x:Name="StrokeRadioButton" Content="Stroke" Margin="0,0,0,3"/>
                     <local:ColorSelector x:Name="ColorSelectorControl"/>
                 </StackPanel>
             </Expander>
         </StackPanel>
     </Grid>
 </Window>
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
        }

        private void RemoveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
        }

        private void ContainerBorder_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
        }

        private void RadioButton_Checked(object sender, System.Windows.RoutedEventArgs e)
        {
        }
    }
}

To match that class we have to create a C++ component that derives from Window and that has BlendTutorial.MainWindow as TypeId. In addition, we must add to its reflection type the functions that match the event handlers defined in MainWindow.xaml file:

#pragma warning(disable: 4275)
#pragma warning(disable: 4251)


#include <Noesis.h>
#include <NsGui/Application.h>
#include <NsGui/Window.h>
#include <NsGui/UIElementEvents.h>
#include <NsCore/ReflectionImplement.h>
#include <NsCore/TypeId.h>
#include <NsCore/Package.h>


using namespace Noesis;
using namespace Noesis::Core;
using namespace Noesis::Gui;


////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialApp: public Application
{
    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialApp, Application)
    {
        NsMeta<TypeId>("BlendTutorial.App");
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialMainWindow: public Window
{
private:
    void ContainerBorder_PreviewMouseLeftButtonDown(const Ptr<BaseComponent>& sender,
        const MouseButtonEventArgs& e)
    {
    }

    void AddButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RemoveButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RadioButton_Checked(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialMainWindow, Window)
    {
        NsMeta<TypeId>("BlendTutorial.MainWindow");

        NsFunc("ContainerBorder_PreviewMouseLeftButtonDown",
            &BlendTutorialMainWindow::ContainerBorder_PreviewMouseLeftButtonDown);
        NsFunc("AddButton_Click", &BlendTutorialMainWindow::AddButton_Click);
        NsFunc("RemoveButton_Click", &BlendTutorialMainWindow::RemoveButton_Click);
        NsFunc("RadioButton_Checked", &BlendTutorialMainWindow::RadioButton_Checked);
    }
};

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

Named elements of the XAML file can be stored during element initialization to facilitate logic implementation:

#pragma warning(disable: 4275)
#pragma warning(disable: 4251)


#include <Noesis.h>
#include <NsGui/Application.h>
#include <NsGui/Window.h>
#include <NsGui/UIElementEvents.h>
#include <NsGui/Border.h>
#include <NsGui/Canvas.h>
#include <NsGui/Slider.h>
#include <NsGui/RadioButton.h>
#include <NsCore/ReflectionImplement.h>
#include <NsCore/TypeId.h>
#include <NsCore/Package.h>


using namespace Noesis;
using namespace Noesis::Core;
using namespace Noesis::Gui;


////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialApp: public Application
{
    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialApp, Application)
    {
        NsMeta<TypeId>("BlendTutorial.App");
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialMainWindow: public Window
{
protected:
    /// From DependencyObject
    //@{
    void OnInit()
    {
        ParentClass::OnInit();

        mContainerBorder.Reset(NsStaticCast<Border*>(FindName("ContainerBorder")));
        NS_ASSERT(mContainerBorder);

        mContainerCanvas.Reset(NsStaticCast<Canvas*>(FindName("ContainerCanvas")));
        NS_ASSERT(mContainerCanvas);

        mLeft.Reset(NsStaticCast<Slider*>(FindName("LeftValue")));
        NS_ASSERT(mLeft);

        mTop.Reset(NsStaticCast<Slider*>(FindName("TopValue")));
        NS_ASSERT(mTop);

        mWidth.Reset(NsStaticCast<Slider*>(FindName("WidthValue")));
        NS_ASSERT(mWidth);

        mHeight.Reset(NsStaticCast<Slider*>(FindName("HeightValue")));
        NS_ASSERT(mHeight);

        mFill.Reset(NsStaticCast<RadioButton*>(FindName("FillRadioButton")));
        NS_ASSERT(mFill);
    }
    //@}

private:
    void ContainerBorder_PreviewMouseLeftButtonDown(BaseComponent* sender,
        const MouseButtonEventArgs& e)
    {
    }

    void AddButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RemoveButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RadioButton_Checked(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

private:
    Border* mContainerBorder;
    Canvas* mContainerCanvas;

    Slider* mLeft;
    Slider* mTop;
    Slider* mWidth;
    Slider* mHeight;

    RadioButton* mFill;

    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialMainWindow, Window)
    {
        NsMeta<TypeId>("BlendTutorial.MainWindow");

        NsFunc("ContainerBorder_PreviewMouseLeftButtonDown",
            &BlendTutorialMainWindow::ContainerBorder_PreviewMouseLeftButtonDown);
        NsFunc("AddButton_Click", &BlendTutorialMainWindow::AddButton_Click);
        NsFunc("RemoveButton_Click", &BlendTutorialMainWindow::RemoveButton_Click);
        NsFunc("RadioButton_Checked", &BlendTutorialMainWindow::RadioButton_Checked);
    }
};

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

ColorSelector

The last class created by Expression Blend is the one associated with our ColorSelector user control.

 <UserControl
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     mc:Ignorable="d"
     x:Class="BlendTutorial.ColorSelector"
     x:Name="ColorSelectorRoot"
     d:DesignWidth="200" d:DesignHeight="100" Foreground="{DynamicResource BlendTutorialForeground}"
     Slider.ValueChanged="Slider_ValueChanged">

     <Grid x:Name="LayoutRoot">
         <Grid.RowDefinitions>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="1*"/>
             <ColumnDefinition Width="2*"/>
         </Grid.ColumnDefinitions>
         <Rectangle RadiusX="2" RadiusY="2" Grid.RowSpan="4" Margin="0,0,5,0">
             <Rectangle.Fill>
                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,15" MappingMode="Absolute" SpreadMethod="Repeat">
                     <GradientStop Color="Gray" Offset="0"/>
                     <GradientStop Color="Silver" Offset="0.5"/>
                     <GradientStop Color="White" Offset="0.5001"/>
                     <GradientStop Color="Gainsboro" Offset="1"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="ColorRect" Fill="{Binding Color, ElementName=ColorSelectorRoot}" Stroke="{DynamicResource BlendTutorialBorderBrush}" RadiusX="2" RadiusY="2" Grid.RowSpan="4" Margin="0,0,5,0"/>
         <Slider x:Name="R" Grid.Column="1" VerticalAlignment="Center" Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="G" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="B" Grid.Column="1" Grid.Row="2" VerticalAlignment="Center" Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="A" Grid.Column="1" Grid.Row="3" VerticalAlignment="Center" Margin="10,1,0,1" Maximum="255" SmallChange="1" LargeChange="10" Value="255"/>
         <TextBlock Text="R" d:LayoutOverrides="Width, Height" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"/>
         <TextBlock Text="G" d:LayoutOverrides="Width, Height" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="1"/>
         <TextBlock Text="B" d:LayoutOverrides="Width, Height" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="2"/>
         <TextBlock Text="A" d:LayoutOverrides="Width, Height" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="3"/>
     </Grid>
 </UserControl>
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for ColorSelector.xaml
    /// </summary>
    public partial class ColorSelector : UserControl
    {
        public ColorSelector()
        {
            this.InitializeComponent();
        }

        public static DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(SolidColorBrush),
            typeof(ColorSelector), new PropertyMetadata(null, new PropertyChangedCallback(OnColorChanged)));

        public SolidColorBrush Color
        {
            get { return (SolidColorBrush)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }

        private static void OnColorChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
        {
        }

        private void Slider_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<double> e)
        {
        }
    }
}

To match that user control we have to create a C++ component that derives from UserControl and that has BlendTutorial.ColorSelector as TypeId. We must add to its reflection type the function that manages changes in the sliders, and register the dependency property defined by the ColorSelector. User controls have another peculiarity; their contents are specified by .xaml files. UserControl base class defines the Source property which should be used to specify the name of that file.

#pragma warning(disable: 4275)
#pragma warning(disable: 4251)


#include <Noesis.h>
#include <NsGui/Application.h>
#include <NsGui/Window.h>
#include <NsGui/UIElementEvents.h>
#include <NsGui/Border.h>
#include <NsGui/Canvas.h>
#include <NsGui/Slider.h>
#include <NsGui/RadioButton.h>
#include <NsGui/UserControl.h>
#include <NsGui/SolidColorBrush.h>
#include <NsGui/UIElementData.h>
#include <NsGui/FrameworkPropertyMetaData.h>
#include <NsCore/ReflectionImplement.h>
#include <NsCore/TypeId.h>
#include <NsCore/Package.h>


using namespace Noesis;
using namespace Noesis::Core;
using namespace Noesis::Gui;


////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialApp: public Application
{
    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialApp, Application)
    {
        NsMeta<TypeId>("BlendTutorial.App");
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialColorSelector: public UserControl
{
public:
    /// Gets or sets color selector current color
    //{
    SolidColorBrush* GetColor() const
    {
        return GetValue< Ptr<SolidColorBrush> >(ColorProperty).GetPtr();
    }
    void SetColor(SolidColorBrush* color)
    {
        SetValue< Ptr<SolidColorBrush> >(ColorProperty, color);
    }
    //@}

public:
    static const Gui::DependencyProperty* ColorProperty;

protected:
    /// From DependencyObject
    //@{
    void OnInit()
    {
        ParentClass::OnInit();

        mR = NsStaticCast<Slider*>(FindName("R"));
        NS_ASSERT(mR != 0);
        mG = NsStaticCast<Slider*>(FindName("G"));
        NS_ASSERT(mG != 0);
        mB = NsStaticCast<Slider*>(FindName("B"));
        NS_ASSERT(mB != 0);
        mA = NsStaticCast<Slider*>(FindName("A"));
        NS_ASSERT(mA != 0);
    }
    //@}

private:
    void Slider_ValueChanged(BaseComponent* sender,
        const RoutedPropertyChangedEventArgs<NsFloat32>& e)
    {
    }

private:
    Slider* mR;
    Slider* mG;
    Slider* mB;
    Slider* mA;

    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialColorSelector, UserControl)
    {
        NsMeta<TypeId>("BlendTutorial.ColorSelector");

        NsProp("Color", &BlendTutorialColorSelector::GetColor, &BlendTutorialColorSelector::SetColor);

        NsFunc("Slider_ValueChanged", &BlendTutorialColorSelector::Slider_ValueChanged);

        // this is the path to the xaml file that defines the ColorSelector content
        NsString source("Gui/BlendTutorial/ColorSelector.xaml");

        Ptr<UIElementData> data = NsMeta<UIElementData>(TypeOf<SelfClass>());
        data->RegisterProperty<Ptr<SolidColorBrush> >(ColorProperty, "Color",
            FrameworkPropertyMetadata::Create(Ptr<SolidColorBrush>::Null(), FrameworkOptions_None));
        data->OverrideMetadata<NsString>(UserControl::SourceProperty, "Source",
            FrameworkPropertyMetadata::Create(source, FrameworkOptions_None));
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
class BlendTutorialMainWindow: public Window
{
protected:
    /// From DependencyObject
    //@{
    void OnInit()
    {
        ParentClass::OnInit();

        mContainerBorder = NsStaticCast<Border*>(FindName("ContainerBorder"));
        NS_ASSERT(mContainerBorder != 0);

        mContainerCanvas = NsStaticCast<Canvas*>(FindName("ContainerCanvas"));
        NS_ASSERT(mContainerCanvas != 0);

        mLeft = NsStaticCast<Slider*>(FindName("LeftValue"));
        NS_ASSERT(mLeft != 0);

        mTop = NsStaticCast<Slider*>(FindName("TopValue"));
        NS_ASSERT(mTop != 0);

        mWidth = NsStaticCast<Slider*>(FindName("WidthValue"));
        NS_ASSERT(mWidth != 0);

        mHeight = NsStaticCast<Slider*>(FindName("HeightValue"));
        NS_ASSERT(mHeight != 0);

        mFill = NsStaticCast<RadioButton*>(FindName("FillRadioButton"));
        NS_ASSERT(mFill != 0);

        mColorSelector = NsStaticCast<BlendTutorialColorSelector*>(FindName("ColorSelectorControl"));
        NS_ASSERT(mColorSelector != 0);
    }
    //@}

private:
    void ContainerBorder_PreviewMouseLeftButtonDown(BaseComponent* sender,
        const MouseButtonEventArgs& e)
    {
    }

    void AddButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RemoveButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

    void RadioButton_Checked(BaseComponent* sender, const RoutedEventArgs& e)
    {
    }

private:
    Border* mContainerBorder;
    Canvas* mContainerCanvas;

    Slider* mLeft;
    Slider* mTop;
    Slider* mWidth;
    Slider* mHeight;

    RadioButton* mFill;

    BlendTutorialColorSelector* mColorSelectorControl;

    NS_IMPLEMENT_INLINE_REFLECTION(BlendTutorialMainWindow, Window)
    {
        NsMeta<TypeId>("BlendTutorial.MainWindow");

        NsFunc("ContainerBorder_PreviewMouseLeftButtonDown",
            &BlendTutorialMainWindow::ContainerBorder_PreviewMouseLeftButtonDown);
        NsFunc("AddButton_Click", &BlendTutorialMainWindow::AddButton_Click);
        NsFunc("RemoveButton_Click", &BlendTutorialMainWindow::RemoveButton_Click);
        NsFunc("RadioButton_Checked", &BlendTutorialMainWindow::RadioButton_Checked);
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
extern "C" NS_DLL_EXPORT void NsRegisterReflection(ComponentFactory* factory,
    NsBool registerComponents)
{
    NS_REGISTER_COMPONENT(BlendTutorialApp)
    NS_REGISTER_COMPONENT(BlendTutorialMainWindow)
    NS_REGISTER_COMPONENT(BlendTutorialColorSelector)
}

The full project, with a Blend 4 workspace, XAMLs and native code can be found in this Tutorial Data

Building XAML Resources

Before executing the build tool we must create a font resource for each embedded font used by the application (this process will probably be automatic in the future). Our tutorial has one embedded font, FrancophilSans, and Expression Blend placed all the typeface files under the Fonts folder. Noesis font resources are just text files named as FamilyName.font that lists all the typeface files. In our case we will create a file called FrancophilSans.font that contains the following:

FrancophilSans.ttf
FrancophilSans-Bold.ttf

Now we are prepared to build all the resources used by our application.

Running Application

To launch our application we will use the Gui.Launcher tool as described in this tutorial. This command line tool receives a unique argument, an app.xaml file path:

> Gui.Launcher.exe Gui/BlendTutorial/App.xaml

Now we should have a window rendering in real time the interface designed totally from Expression Blend.

© 2017 Noesis Technologies