You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Avalonia根据集合值切换DataTemplate时抛出ArgumentOutOfRangeException

解决Avalonia ItemsControl根据对象属性切换DataTemplate的编译错误

你遇到的编译错误:

Error AVLN:0004 Avalonia: Internal compiler error while transforming node XamlX.Ast.XamlAstObjectNode:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

核心原因是ItemTemplate的写法和TemplateSelector的逻辑不符合Avalonia规范——你试图将ItemTemplate绑定到IsBool属性后传入Selector,这种嵌套绑定的写法会导致XAML编译器解析失败。下面是具体的修正方案:

1. 修正ParameterTemplateSelector

让Selector直接接收Parameter对象,根据其IsBool属性选择对应模板,替代原来处理bool值的逻辑:

public class ParameterTemplateSelector : IDataTemplate
{
    // 定义两个可绑定的模板属性,替代原字典实现
    public IDataTemplate BoolTemplate { get; set; } = default!;
    public IDataTemplate DefaultTemplate { get; set; } = default!;

    public Control Build(object? param)
    {
        if (param is Parameter parameter)
        {
            return parameter.IsBool ? BoolTemplate.Build(param) : DefaultTemplate.Build(param);
        }
        throw new ArgumentException("Expected Parameter object", nameof(param));
    }

    public bool Match(object? data)
    {
        return data is Parameter;
    }
}

2. 修改ItemsControl的XAML配置

移除原来包裹Selector的Binding节点,直接将ItemTemplate设置为Selector实例,并通过属性指定对应模板:

<ItemsControl Grid.IsSharedSizeScope="True" ItemsSource="{Binding Parameters}">
    <ItemsControl.ItemTemplate>
        <resources:ParameterTemplateSelector>
            <resources:ParameterTemplateSelector.BoolTemplate>
                <DataTemplate>
                    <Grid Margin="20 10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
                            <ColumnDefinition Width="10" />
                            <ColumnDefinition Width="*" MaxWidth="600" />
                        </Grid.ColumnDefinitions>
                        <Label Content="{Binding Name}" VerticalContentAlignment="Center"
                               HorizontalContentAlignment="Right" />
                        <ToggleSwitch Grid.Column="2"/>
                    </Grid>
                </DataTemplate>
            </resources:ParameterTemplateSelector.BoolTemplate>
            
            <resources:ParameterTemplateSelector.DefaultTemplate>
                <DataTemplate>
                    <Grid Margin="20 10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
                            <ColumnDefinition Width="10" />
                            <ColumnDefinition Width="*" MaxWidth="600" />
                        </Grid.ColumnDefinitions>
                        <Label Content="{Binding Name}" VerticalContentAlignment="Center"
                               HorizontalContentAlignment="Right" />
                        <AutoCompleteBox Grid.Column="2" HorizontalAlignment="Stretch"/>
                    </Grid>
                </DataTemplate>
            </resources:ParameterTemplateSelector.DefaultTemplate>
        </resources:ParameterTemplateSelector>
    </ItemsControl.ItemTemplate>
</ItemsControl>

关键修改点:

  • 移除了<Binding Path="IsBool">嵌套,ItemTemplate的Selector默认会接收ItemsSource中的每个Parameter对象
  • 用Selector的BoolTemplate和DefaultTemplate属性指定对应模板,替代原x:Key方式
  • 将ReflectionBinding改为普通Binding,如果需要动态更新UI(比如修改IsBool后自动切换模板),需给Parameter类添加INotifyPropertyChanged实现

3. 可选:添加属性变更通知(支持动态模板切换)

如果需要修改IsBool值时自动更新UI,让Parameter实现INotifyPropertyChanged:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Parameter : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public Parameter(string value = "")
    {
        if (value.ToLower() == "switch")
        {
            IsBool = true;
        }
    }

    private string _name = string.Empty;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    private bool _isBool;
    public bool IsBool
    {
        get => _isBool;
        set
        {
            _isBool = value;
            OnPropertyChanged();
        }
    }

    private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

内容的提问来源于stack exchange,提问作者electroshock777

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:01:11