microsoft-ui-xaml icon indicating copy to clipboard operation
microsoft-ui-xaml copied to clipboard

Can't specify `x:DataType` of DataTemplate in ResourceDictionary

Open HO-COOH opened this issue 6 months ago • 13 comments

Describe the bug

I have a templated control that has a property of type Windows.Foundation.Collections.IObservableVector<Int32> Items{ get; }; that's bound to a ListView.ItemsSource and I can't specify the x:DataType in the DataTemplate. It gives

Themes\Generic.xaml(12,43): XamlCompiler error WMC0612: The XAML Binary Format (XBF) generator reported syntax error '0x09C4' : Property Not Found

Steps to reproduce the bug

  1. Create a new C++WinUI3 project
  2. Create a new templated control with this idl, write a dummy code-behind
    [bindable]
    [default_interface]
    runtimeclass MyControl : Microsoft.UI.Xaml.Controls.Control
    {
        MyControl();
        Windows.Foundation.Collections.IObservableVector<Int32> Items{ get; };
    }
  1. In Themes/Generc.xaml, add this style
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:_28_CannotUseInt32AsDataType">
    <Style TargetType="local:MyControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <ListView ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=TemplatedParent}}">
                        <ListView.ItemTemplate>
                            <DataTemplate x:DataType="x:Int32"/>
                        </ListView.ItemTemplate>
                    </ListView>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Expected behavior

Builds fine

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.7.1: 1.7.250401001

Windows version

Windows 11 (24H2): Build 26100

Additional context

Repro

HO-COOH avatar May 19 '25 14:05 HO-COOH

I think this issue is rather about having an empty DataTemplate tag. This error also occurred when using any other data type; if the DataTemplate tag is empty (either immediately closed or not having any children), the XAML compiler returns an XBF error. @HO-COOH can you confirm the issue does not occur if you have content within the datatemplate?

marcelwgn avatar May 21 '25 10:05 marcelwgn

@marcelwgn Nope. Even if I put random content in DataTemplate gives me same error.

 <DataTemplate x:DataType="x:Int32">
     <TextBlock Text="Me"/>
 </DataTemplate>

HO-COOH avatar May 21 '25 16:05 HO-COOH

UPDATE: The issue seems to be that I cannot specify any x:DataType in a resource dictionary that does not have a code-behind. And this is quite ridiculous, since this is the files created by adding a templated control.

HO-COOH avatar May 22 '25 06:05 HO-COOH

x:Bind relies on code-generation and needs a backing file to generate in. I'm doing this extensively in one of our libraries. Example: https://github.com/Esri/arcgis-maps-sdk-dotnet-toolkit/tree/main/src/Toolkit/Toolkit.WinUI/UI/Controls/BookmarksView

dotMorten avatar May 22 '25 21:05 dotMorten

x:Bind relies on code-generation and needs a backing file to generate in. I'm doing this extensively in one of our libraries. Example: https://github.com/Esri/arcgis-maps-sdk-dotnet-toolkit/tree/main/src/Toolkit/Toolkit.WinUI/UI/Controls/BookmarksView

x:Bind relies on code-behind, yes, but I didn't found x:DataType relies on code-behind documented anywhere. I can use it without x:Bind, something like this

<DataTemplate x:DataType="x:String>
    <TextBlock Text="{Binding}"/>
</DataTemplate>

HO-COOH avatar May 23 '25 02:05 HO-COOH

update your resource control to this and it will run.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
    x:Class="_28_CannotUseInt32AsDataType.MyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:_28_CannotUseInt32AsDataType">
    <Style TargetType="local:MyControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <ListView ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=TemplatedParent}}">
                        <ListView.ItemTemplate>
                            <DataTemplate x:DataType="x:Int32">
                                <TextBlock Text="{Binding}" />
                            </DataTemplate>

                        </ListView.ItemTemplate>
                    </ListView>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Needed to add an x:class and content inside the datatemplate. Both are required. If after changing it can't find the class, close and reopen the solution then retry.

Skittles2519 avatar May 23 '25 05:05 Skittles2519

@HO-COOH Why are you setting the datatype if you aren't going to be using x:bind? That sort of defeats the purpose. It exists for the purpose of x:bind.

dotMorten avatar May 23 '25 20:05 dotMorten

@Skittles2519 Thanks, this solves my issue. What if the resource dictionary contains multiple templates for templated control? Like the default visual studio file template created a Theme/Generic.xaml, which seems to suggest it "should" contain multiple control templates.

HO-COOH avatar May 26 '25 10:05 HO-COOH

@HO-COOH thats fine: they are all in the same resource dictionary class

dotMorten avatar May 26 '25 13:05 dotMorten

@dotMorten So I can conclude that the default generated Theme/Generic.xaml (when I add a new Templated Control) should contain x:Class for the first templated control I added?

HO-COOH avatar Jun 05 '25 08:06 HO-COOH

x:class goes in the first xml tag (the ResourceDictionary one, not in the individual styles).

dotMorten avatar Jun 05 '25 12:06 dotMorten

x:class goes in the first xml tag (the ResourceDictionary one, not in the individual styles).

Yes I know that already. I mean currently, when I add a new templated control for the first time, the Generic.xaml looks like this

Image

But it should really add a x:Class, like this:

<ResourceDictionary
    x:Class="winui3cpp.MyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:winui3cpp">

    <Style TargetType="local:MyControl" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

HO-COOH avatar Jun 05 '25 15:06 HO-COOH

Yes except don't use the MyControl class - use a class for the resources in general. Here's what I do:

<ResourceDictionary
    x:Class="winui3cpp.ThemeResources"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:winui3cpp">

    <Style TargetType="local:MyControl" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

And then also add the partial C# file:


namespace winui3cpp;

internal sealed partial class ThemeResources : ResourceDictionary
{
    public ThemeResources()
    {
        InitializeComponent();
    }
}

You can also add methods in here to do method binding to which is useful instead of using converters.

dotMorten avatar Jun 05 '25 15:06 dotMorten