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
相关产品推荐
相关产品推荐

