Search code examples
uwpwinrt-xamluwp-xamlc++-winrt

XamlReader fails to parse DataTemplate with DataType property


I'm trying to create a DataTemplate for a ListView dynamically using cpp/winrt:

auto template_src = R"(
    <DataTemplate 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyNamespace"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        x:DataType="local:MyListItem"
    >
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{x:Bind MyProperty}" HorizontalAlignment="Left"/>
        </StackPanel>
    </DataTemplate>
)";
auto tmpl = winrt::Windows::UI::Xaml::Markup::XamlReader::Load(winrt::to_hstring(template_src)).as<winrt::Windows::UI::Xaml::DataTemplate>();

An exception is thrown at the Load call:

The property 'DataType' was not found in type 'DataTemplate'. [Line: 8 Position: 17]

The type MyListItem is a custom winrt type. The DataType property is required when using x:Bind. If I remove the property and replace the binding as below it doesn't crash but doesn't render either:

auto template_src = R"(
    <DataTemplate 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyNamespace"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
    >
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding MyProperty}" HorizontalAlignment="Left"/>
        </StackPanel>
    </DataTemplate>
)";
auto tmpl = winrt::Windows::UI::Xaml::Markup::XamlReader::Load(winrt::to_hstring(template_src)).as<winrt::Windows::UI::Xaml::DataTemplate>();

If I declare the template in Xaml:

<Page.Resources>
    <DataTemplate x:Key="ListTemplate" x:DataType="local:MyListItem">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{x:Bind MyProperty, Mode=OneWay}" HorizontalAlignment="Left"/>
        </StackPanel>
    </DataTemplate>
</Page.Resources>

and load it

Resources().Lookup(winrt::box_value(L"ListTemplate")).as<winrt::Windows::UI::Xaml::DataTemplate>());

the list renders correctly.


Solution

  • The property 'DataType' was not found in type 'DataTemplate'. [Line: 8 Position: 17]

    Based on the document, it mentions that we can’t create {x:Bind} bindings in code, so the usage of {x:Bind} in the code about template_src causes the exception. You can use {Binding} to replace {x:Bind}.

    If I remove the property and replace the binding as below it doesn't crash but doesn't render either:

    If you use {Binding} extension in C++/WinRT, you should add the BindableAttribute attribute to any runtime class that you want to use the {Binding} markup extension with. For more details about this, you can refer to this document. In this case, you need to add the [bindable] in your .idl file in order to use Binding. For example:

    Model.idl:

    [bindable]
    runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        String Title;
    }
    

    To render the list, please check the following code:

    Page.cpp

    auto template_src = R"(
        <DataTemplate 
        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:local="using:MyNamespace"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        >
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Title}" HorizontalAlignment="Left"/>
            </StackPanel>
        </DataTemplate>
    )";
    hstring str = winrt::to_hstring(template_src);
    auto tmpl = winrt::Windows::UI::Xaml::Markup::XamlReader::Load(str);
    
    myListView().ItemsSource(MyListItem());
    myListView().ItemTemplate(tmpl.try_as<DataTemplate>());