winrt-xamluwp-xamlwinui-3

How to create a ListBox item control and bind a complex type to


I have the following custom control,

public sealed class MediaFileItemControl : Control
{
    public static readonly DependencyProperty MediaFileProperty =
        DependencyProperty.Register(nameof(MediaFile), typeof(StorageFile), typeof(MediaFileItemControl), new PropertyMetadata(null));

    public MediaFileItemControl()
    {
        DefaultStyleKey = typeof(MediaFileItemControl);
    }

    public StorageFile MediaFile
    {
        get => (StorageFile)GetValue(MediaFileProperty);
        set => SetValue(MediaFileProperty, value);
    }
}

the following style for it,

<!-- Generic.xaml -->
<Style TargetType="controls:MediaFileItemControl" >
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="controls:MediaFileItemControl">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{TemplateBinding MediaFile.Name}" />
                    <TextBlock Text="{TemplateBinding MediaFile.Path}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and lastly, I want to use it within a ListBox, something like:

<ListBox ItemsSource="{x:Bind ViewModel.MediaFiles, Mode=OneWay}">
    <ListBox.ItemTemplate>
        <DataTemplate x:DataType="win_storage:StorageFile" xmlns:win_storage="using:Windows.Storage">
            <controls:MediaFileItemControl MediaFile="{x:Bind}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

However, when I build the project, I get the following error in Generic.xaml

The XAML Binary Format (XBF) generator reported syntax error '0x80004005'

Where is the syntax error and what is the right way to achieve what I want?


Solution

  • x:Bind has lots of issues that are quite difficult to overcome: https://github.com/microsoft/microsoft-ui-xaml/issues/2508

    But you can use a ContentControl that points to a DataTemplate. So in your case, you can write it like this:

    <ResourceDictionary
        x:Class="MyWinUIApp.ResourceDictionary1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyWinUIApp"
        xmlns:storage="using:Windows.Storage">
    
        <DataTemplate x:Key="StorageFileTemplate" x:DataType="storage:StorageFile">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{x:Bind Name}" />
                <TextBlock Text="{x:Bind Path}" />
            </StackPanel>
        </DataTemplate>
    
        <Style TargetType="local:MediaFileItemControl">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <ContentControl ContentTemplate="{StaticResource StorageFileTemplate}" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
    </ResourceDictionary>
    

    Note I've create a custom ResourceDictionary as it doesn't seen to work in the App.xaml one for another mysterious reason. Make sure you create one with a code-behind to support x:Bind as explained here: Resource dictionaries with {x:Bind}

    If you don't want that complexity, you can still use the ole Binding markup extension wich often works where the so-called "better" x:Bind fails:

    <Style TargetType="local:MediaFileItemControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text="{Binding Path}" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>