Avalonia切换ContentControl时ListBox抛出锚点控件异常
使用Avalonia开发时,ListBox初始绑定视图模型的MyItems填充数据一切正常:
<ListBox ItemsSource="{Binding MyItems}" Height="500" HorizontalAlignment="Stretch"/>
但通过ContentControl切换LeftPanel内容(先换成其他控件,再切回ListBox)时,会抛出异常:
System.InvalidOperationException: 'An anchor control must be a visual descendent of the ScrollContentPresenter.'
仅在初始为空的列表手动异步填充时触发该问题,推测是空列表未触发滚动初始化导致。填充数据的代码如下:
void FillListBox(string[] files) { Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => { CreateListboxItems(files); }); } private void CreateListboxItems(string[] files) { MyItems.Clear(); int length = files.Length; for (int i = 0; i < length; i++) { ListBoxItem item = CreateListboxItem(files[i]); MyItems.Add(item); } } ListBoxItem CreateListboxItem(string filePath) { Avalonia.Media.FontStyle fontStyle = File.Exists(filePath) ? Avalonia.Media.FontStyle.Normal : Avalonia.Media.FontStyle.Italic; ListBoxItem response = new ListBoxItem() { Content = filePath, FontStyle = fontStyle, }; return response; }
已尝试将ListBox放入独立滚动视图,问题依旧;改用ObservableCollection<string>替代ObservableCollection<ListBoxItem>可正常运行,但无法实现文件不存在时的斜体样式需求。
问题核心是在视图模型中直接创建ListBoxItem视觉元素违背了Avalonia的MVVM设计逻辑。ListBoxItem属于视觉控件,应由ListBox自身负责实例化与视觉树管理,手动创建的元素在ContentControl切换导致视觉树销毁重建时,会出现滚动锚点的层级绑定异常。
正确做法是用**数据模板(DataTemplate)**定义Item样式,视图模型仅处理数据:
- 定义数据实体类
public class FileItem { public string FilePath { get; set; } public Avalonia.Media.FontStyle FontStyle { get; set; } public FileItem(string filePath) { FilePath = filePath; FontStyle = File.Exists(filePath) ? Avalonia.Media.FontStyle.Normal : Avalonia.Media.FontStyle.Italic; } } // 视图模型中的集合修改为数据实体类型 public ObservableCollection<FileItem> MyItems { get; } = new ObservableCollection<FileItem>(); // 填充方法调整为添加数据实体 private void CreateListboxItems(string[] files) { MyItems.Clear(); foreach (var file in files) { MyItems.Add(new FileItem(file)); } }
- 修改ListBox的XAML,添加DataTemplate
<ListBox ItemsSource="{Binding MyItems}" Height="500" HorizontalAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem FontStyle="{Binding FontStyle}" Content="{Binding FilePath}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
这种方式既保留了字体样式的自定义需求,又让ListBox完全掌控视觉元素的生命周期,切换ContentControl内容时不会再触发滚动锚点相关异常。如果需要更复杂的Item样式,还可以通过ItemContainerStyle进一步定制。
内容的提问来源于stack exchange,提问作者Frederik Steinmetz

