Windows 8 XAML Files and C++ Header Files
December 19, 2012
New York, N.Y.
Programmers returning to C++ after spending some years with C# are likely to have forgotten a few basics, or might not quite grasp how XAML fits in with the big picture. That's certainly the case for me as I'm becoming multilingual in Windows 8 programming! Here's an example of something that has gotten me several times.
Perhaps you want a Slider in the center of your Windows 8 page, so you put it in the XAML file:
<Page x:Class="SliderDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SliderDemo">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Slider VerticalAlignment="Center"
Margin="50 0" />
</Grid>
</Page>
This is the same for C# or C++. But as you're experimenting with this Slider, you realize that the tooltip is not right. The tooltip displays the value of the Slider between 0 and 100, but within your code you're actually going to be using the base-10 logarithm of the Slider value, and it would be great if the tooltip displayed that value.
No problem. All you need is a value converter, which is a class that implements the IValueConverter interface. This class can be as generalized or as ad hoc as you'd like. If you're doing it in C++, here's the header file for a LogConverter class:
#pragma once
namespace SliderDemo
{
public ref class LogConverter sealed : Windows::UI::Xaml::Data::IValueConverter
{
public:
virtual Object^ Convert(Platform::Object^ value,
Windows::UI::Xaml::Interop::TypeName targetType,
Platform::Object^ parameter,
Platform::String^ language);
virtual Object^ ConvertBack(Platform::Object^ value,
Windows::UI::Xaml::Interop::TypeName targetType,
Platform::Object^ parameter,
Platform::String^ language);
};
}
And here's the code:
#include "pch.h"
#include "LogConverter.h"
using namespace SliderDemo;
using namespace Platform;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Interop;
Object^ LogConverter::Convert(Object^ value, TypeName targetType,
Object^ parameter, String^ langauge)
{
wchar_t result[10];
swprintf(result, 10, L"%0.2f", log10((double)value));
return ref new String(result);
}
Object^ LogConverter::ConvertBack(Object^ value, TypeName targetType,
Object^ parameter, String^ language)
{
return value;
}
To persuade the Slider to use this value converter for displaying the value in the tooltip, you set the ThumbToolTipValueConverter property to an instance of this class. Often the value converter is instantiated as a XAML resource, but you can alternatively reference it directly in the Slider markup. Notice the local XML namespace prefix:
<Page x:Class="SliderDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SliderDemo">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Slider VerticalAlignment="Center"
Margin="50 0"
Minimum="1">
<Slider.ThumbToolTipValueConverter>
<local:LogConverter />
</Slider.ThumbToolTipValueConverter>
</Slider>
</Grid>
</Page>
If you're coding in C#, you're done. But when you try to build this C++ program, you get two errors:
-
error C2039: 'LogConverter' : is not a member of 'SliderDemo'
error C2061: syntax error : identifier 'LogConverter'
The first error is apt to cause you to believe that something is wrong with the definition of the LogConverter class. But that's not the case. Both these errors reference a statement in the xamltypeinfo.g.cpp file, which is a statement that instantiates LogConverter. That "g" in the filename means this file is "generated" during the build process, and that's apt to be also baffling, because there's nothing you can do with this file directly. But obviously xamltypeinfo.g.cpp has no knowledge of this LogConverter class.
Fortunately, xamltypeinfo.g.cpp contains #include statements for both App.xaml.h and MainPage.xaml.h, so the simple solution is to put an #include statement for LogConverter.h in one of these two header files:
#include "LogConverter.h"
I often put such an #include statement in MainPage.xaml.h if the class is referenced only in the MainPage.xaml file, but if you're referencing such a class in multiple XAML files, putting it in App.xaml.h would make more sense.
At any rate, that's the lesson: If a custom class is referenced only in a XAML file and not in a code file, an #include statement for the class's header file is still required to accomodate the code generated during the build process.