After decades of writing MFC apps, I'm trying to learn C++/WinRT and WinUI 3. To do this, I'm working through Petzold's "Programming Windows" 6th edition, converting the code from C# to C++/WinRT. I'm in Chapter 2, working on the PathMarkupSyntaxCode example. Here's the C# code:
public MainPage()
{
this.InitializeComponent();
Path path = new Path
{
Stroke = new SolidColorBrush(Colors.Red),
StrokeThickness = 12,
StrokeLineJoin = PenLineJoin.Round,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = PathMarkupToGeometry(
"M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 " +
"M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 " +
"M 150 0 L 150 100, 200 100 " +
"M 225 0 L 225 100, 275 100 " +
"M 300 50 A 25 50 0 1 0 300 49.9")
};
(this.Content as Grid).Children.Add(path);
}
Geometry PathMarkupToGeometry(string pathMarkup)
{
string xaml =
"<Path " +
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
"<Path.Data>" + pathMarkup + "</Path.Data></Path>";
Path path = XamlReader.Load(xaml) as Path;
// Detach the PathGeometry from the Path
Geometry geometry = path.Data;
path.Data = null;
return geometry;
}
and here's my (non-functional) C++/WinRT code:
MainWindow::MainWindow()
{
InitializeComponent();
std::string str;
str = "M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 ";
str += "M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 ";
str += "M 150 0 L 150 100, 200 100 ";
str += "M 225 0 L 225 100, 275 100 ";
str += "M 300 50 A 25 50 0 1 0 300 49.9";
Path path;
path.Stroke(SolidColorBrush(Colors::Red()));
path.StrokeThickness(12);
path.StrokeLineJoin(PenLineJoin::Round);
path.HorizontalAlignment(HorizontalAlignment::Center);
path.VerticalAlignment(VerticalAlignment::Center);
path.Data(PathMarkupToGeometry(str));
Grid().Children().Append(path);
}
Geometry MainWindow::PathMarkupToGeometry(const std::string& pathMarkup)
{
std::string xaml = "<Path ";
xaml += "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>";
xaml += "<Path.Data>";
xaml += pathMarkup;
xaml += "</Path.Data></Path>";
hstring str = winrt::to_hstring(xaml);
auto tmpl = XamlReader::Load(str);
Path path(tmpl.try_as<Path>());
Geometry geometry = path.Data();
return geometry;
}
The code in the first function that sets path.Data results in an error being thrown in Microsoft.UI.Xaml.h, in the function OnLaunched in the struct produce(). The reason for the error is that the parameter 'args' is 0.
It took me several hours to get this to even compile. (Thanks to IInspectable for the reference to try_as in a comment to another question.) I'm hoping someone who knows C++/WinRT can easily see my error. Any help would be greatly appreciated.
I was hoping the above code would print HELLO on the main window. Everything seems to be going fine in the second function until tmpl gets turned into a Path. After that, I don't know how to tell if the data is being passed correctly or not.
You forgot to detach the geometry from the path, and the constructor just creates a new grid, it doesn't gets the one from current content.
The strict equivalent would be this (note it's better to use std::wstring
to benefit from natural hstring
conversions - Windows is all Unicode for a long time now):
using namespace winrt;
using namespace Windows::Foundation;
using namespace Microsoft::UI;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Controls;
using namespace Microsoft::UI::Xaml::Media;
using namespace Microsoft::UI::Xaml::Markup;
using namespace Microsoft::UI::Xaml::Shapes;
...
MainWindow::MainWindow()
{
InitializeComponent();
std::wstring str;
str = L"M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 ";
str += L"M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 ";
str += L"M 150 0 L 150 100, 200 100 ";
str += L"M 225 0 L 225 100, 275 100 ";
str += L"M 300 50 A 25 50 0 1 0 300 49.9";
Path path;
path.Stroke(SolidColorBrush(Colors::Red()));
path.StrokeThickness(12);
path.StrokeLineJoin(PenLineJoin::Round);
path.HorizontalAlignment(HorizontalAlignment::Center);
path.VerticalAlignment(VerticalAlignment::Center);
path.Data(PathMarkupToGeometry(str));
Content().try_as<Grid>().Children().Append(path);
}
Geometry MainWindow::PathMarkupToGeometry(const std::wstring& pathMarkup)
{
std::wstring xaml = L"<Path ";
xaml += L"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>";
xaml += L"<Path.Data>";
xaml += pathMarkup;
xaml += L"</Path.Data></Path>";
auto path = XamlReader::Load(xaml).try_as<Path>();
Geometry geometry = path.Data();
path.Data(nullptr);
return geometry;
}
It's generally better to use XAML, bindings, templates, controls, custom controls, etc. than to load and mangle raw XAML "manually", but I've kept your original code spirit. Also Petzold's book might lag a bit with respect to the all new WinUI3. And all this is so much easier in C# but that's another subject :-)