While working in data binding in C++ WinUI3 app (and I 've learned a bit thanks to SimonMourier's demo I am also looking at the {CustomResource} data binding method.
I create the WinUI Packaged project
In MainWindow.xaml I 'd put:
<TextBlock Text="{CustomResource Test1}"></TextBlock>
I will create a descentant of winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoader
:
class CLX : public winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoader
{
winrt::Windows::Foundation::IInspectable GetResource(const winrt::param::hstring& resourceId, const winrt::param::hstring& objectType, const winrt::param::hstring& propertyName, const winrt::param::hstring& propertyType) const
{
return winrt::box_value(L"hello");
}
};
In the implementation I 'd add a variable:
CLX clx;
When the app is run, I get an expected exception:
Exception thrown at 0x00007FFE9E97567C (KernelBase.dll) in app1.exe: WinRT originate error - 0x80004005 : 'No custom resource loader set.'.
Microsoft.UI.Xaml.dll!00007FFE3F265DD3: 80004005 - Unspecified error
Exception thrown at 0x00007FFE9E97567C (KernelBase.dll) in app1.exe: WinRT originate error - 0x802B000A : 'No custom resource loader set. [Line: 12 Position: 22]'.
Now, in OnLaunched(),:
winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoader::Current(clx);
This causes a different exception:
Exception thrown at 0x00007FFE9E97567C (KernelBase.dll) in app1.exe: WinRT originate error - 0x802B000A : 'Markup extension could not provide value. [Line: 12 Position: 22]'.
So it sees that handler because it seems that it attemps to call it but the breakpoint there is not hit nor can be placed. It seems that the function is never compiled. Isn't that weird, isn't that a runtime feature?
Is my implementation of GetResource() correct? I don't know what to do from here on.
Although this works at compile time:
class CLX : public winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoader
{
winrt::Windows::Foundation::IInspectable GetResource(const winrt::param::hstring& resourceId, const winrt::param::hstring& objectType, const winrt::param::hstring& propertyName, const winrt::param::hstring& propertyType) const
{
return winrt::box_value(L"hello");
}
};
The GetResource
method won't be called as it's "just" a C++ method (that's why you get "Markup extension could not provide value" the error, because the method is never called) while the built-in CustomXamlResourceLoader
implements this method as a method of the ICustomXamlResourceLoaderOverrides
COM interface (which CustomXamlResourceLoader
implements).
C++/WinRT provides a generated utility class for all WinRT types called [Type]T
to wrap all this COM plumbing, ie: it will make sure your class implements GetResource
at COM level so it's visible by external (to your project) pieces of code that only know COM exposure. This [Type]T
is used everywhere in C++/WinRT samples for example in Author APIs with C++/WinRT but it would deserve its own chapter IMHO...
So it will work if you declare it like this instead:
struct CLX : winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoaderT<CLX>
{
IInspectable GetResource(const hstring& resourceId, const hstring& objectType, const hstring& propertyName, const hstring& propertyType) const
{
return winrt::box_value(L"hello");
}
};
And instantiate like this:
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
auto clx = make<CLX>();
winrt::Microsoft::UI::Xaml::Resources::CustomXamlResourceLoader::Current(clx);
...
}