众所周知,AvaloniaUI的ItemsRepeater是一个用于显示数据集合的控件,能够很方便地从一个ObservableCollection中的数据生成多个子控件。

又众所周知,在AvaloniaUI中,实现自定义控件的最佳实践是使用模板控件(TemplatedControl)。

然而,当你想要在TemplatedControl中使用ItemsRepeater时,你会发现,ItemsRepeater内部DataTemplateDataType并不会正确使用ObservableCollection内部的类。例如,以下这种情况会直接报编译错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Styles xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="using:MyApp.Controls">

    <Style Selector="controls|ScrollBox">
        <Setter Property="Template">
            <ControlTemplate>
                <ScrollViewer>
                    <ItemsRepeater ItemsSource="{TemplateBinding ItemsSource}">
                        <ItemsRepeater.ItemTemplate>
                            <DataTemplate>
                                <SelectableTextBlock IsEnabled="True" Text="{Binding Line}" Foreground="{Binding LineColor}"/>
                            </DataTemplate>
                        </ItemsRepeater.ItemTemplate>
                    </ItemsRepeater>
                </ScrollViewer>
            </ControlTemplate>
        </Setter>
    </Style>
</Styles>
1
2
3
4
5
6
7
8
9
10
public class ScrollBox : TemplatedControl
{
    public static readonly StyledProperty<ObservableCollection<ColoredLine>> ItemsSourceProperty =
        AvaloniaProperty.Register<MyTerminal, ObservableCollection<ColoredLine>>(nameof(ItemsSource));
    public ObservableCollection<ColoredLine> ItemsSource
    {
        get => GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }
}

出现的问题是无法解析某某某类型的数据上下文中的字段或属性 ‘Line’。

如果你尝试将DataTemplate中的Binding改为TemplateBinding,残念,它的DataType会变成整个ScrollBox控件。

正解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<Styles xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="using:MyApp.Controls"
        xmlns:models="using:MyApp.Models"><!--引入ObservableCollection中使用的数据类型-->

    <Style Selector="controls|ScrollBox">
        <Setter Property="Template">
            <ControlTemplate>
                <ScrollViewer>
                    <ItemsRepeater ItemsSource="{TemplateBinding ItemsSource}">
                        <ItemsRepeater.ItemTemplate>
                            <DataTemplate DataType="models:ColoredLine"><!--手动设置DataType-->
                                <SelectableTextBlock IsEnabled="True" Text="{Binding Line}" Foreground="{Binding LineColor}"/>
                            </DataTemplate>
                        </ItemsRepeater.ItemTemplate>
                    </ItemsRepeater>
                </ScrollViewer>
            </ControlTemplate>
        </Setter>
    </Style>
</Styles>

与直接在实例控件中使用ItemsRepeater不同,在TemplatedControl中使用ItemsRepeater时,分析器无法直接确定DataTemplate的类型,需要手动指定DataType

为了应用程序的性能,我个人是一直开着编译时绑定的,不知道不使用编译时绑定的应用有没有这个问题。