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

如何在代码中将ViewModel集合绑定到动态创建的ItemsControl?

问题:Avalonia动态创建ItemsControl绑定后显示类型名称而非数据

我在Avalonia应用里有个用户控件(UC),包含数据绑定控件和可动态添加控件的StackPanel,这个UC会被动态加到外部用户控件中。我想在代码里创建该UC实例,添加ItemsControl并把它的ItemsSource绑定到UC的DataContext(ViewModel类)的ObservableCollection属性。

为排查问题做了示例:书架UC显示ViewModel里的4本书籍,UC已有绑定VM的Category属性的TextBlock,还有StackPanel用来放书籍的ItemsControl和书架的TextBlock。UC会和下方的安装支架TextBlock一起动态加到主窗口,预期像XAML定义的ItemsControl一样显示,但实际每个书籍控件组都显示类型名称,不是书籍信息。

相关代码

BookVM.cs

public partial class BookVM : ViewModelBase {
    [ObservableProperty]
    private string _title = "Read This";
    [ObservableProperty]
    private string _author = "A. Writer";
    [ObservableProperty]
    private int _yearPublished = 2025;
}

BookshelfVM.cs

public partial class BookshelfVM : ObservableObject {
    [ObservableProperty]
    private string _category;
    public ObservableCollection<BookVM> Books { get; set; } = [];

    public BookshelfVM() {
      Category = "Science Fiction";
      Books.Add(new BookVM { Title = "Fahrenheit 451", Author = "Ray Bradbury", YearPublished = 1953 });
      Books.Add(new BookVM { Title = "Dune", Author = "Frank Herbert", YearPublished = 1965 });
      Books.Add(new BookVM { Title = "Neuromancer", Author = "William Gibson", YearPublished = 1984 });
      Books.Add(new BookVM { Title = "Ender’s Game", Author = "Orson Scott Card", YearPublished = 1985 });
    }
}

BookshelfUC.axaml

<Grid>
    <TextBlock Text="{Binding Category}" FontSize="18" FontWeight="Bold" />
    <StackPanel x:Name="stpBooksPlusShelf" Margin="0,25,0,0" />
</Grid>

MainView.axaml

<DockPanel HorizontalAlignment="Left" VerticalAlignment="Stretch">
    <TextBlock DockPanel.Dock="Top" Text="This should show a stack of books." Margin="0,0,0,10" />
    <StackPanel x:Name="stpDynamicControls" />
</DockPanel>

MainView.axaml.cs

public partial class MainView : UserControl {
  public MainView() {
    InitializeComponent();
    var dynamicBookshelf = new BookshelfUC();// { DataContext = new BookshelfVM() };
    var itemsCtlToAdd = new ItemsControl {
      [!ItemsControl.ItemsSourceProperty] = new Binding("Books")
    };
    var dataTmplt = new StackPanel();
    var borderedStack = new StackPanel();
    var bookBorder = new Border {
      BorderBrush = new SolidColorBrush(Colors.Brown),
      BorderThickness = new Avalonia.Thickness(2)
    };
    var title = new TextBlock {
      Background = new SolidColorBrush(Color.Parse("#BC8F6F")),
      Foreground = new SolidColorBrush(Colors.White),
      [!TextBlock.TextProperty] = new Binding("Title")
    };
    var authorAndYear = new TextBlock {
      [!TextBlock.TextProperty] = new Binding("YearPublished")
    };
    var shelfAfterBooks = new TextBlock {
      Background = new SolidColorBrush(Colors.Black),
      Foreground = new SolidColorBrush(Colors.White),
      Text = "Shelf on which the stack of book sits"
    };
    var bracket = new TextBlock {
      Background = new SolidColorBrush(Colors.LightGray),
      Text = "Mounting brackets"
    };
    borderedStack.Children.Add(title);
    borderedStack.Children.Add(authorAndYear);
    bookBorder.Child = borderedStack;
    dataTmplt.Children.Add(bookBorder);
    itemsCtlToAdd.ItemTemplate = new FuncDataTemplate<string>((item, _) =>
    {
      return dataTmplt;
    });

    dynamicBookshelf.stpBooksPlusShelf.Children.Add(itemsCtlToAdd);
    dynamicBookshelf.stpBooksPlusShelf.Children.Add(shelfAfterBooks);
    dynamicBookshelf.DataContext = new BookshelfVM();
    stpDynamicControls.Children.Add(dynamicBookshelf);
    stpDynamicControls.Children.Add(bracket);
  }
}

错误原因分析

  1. FuncDataTemplate类型不匹配:你使用了FuncDataTemplate<string>,但ItemsSource的实际项类型是BookVM,而非string。当模板类型与项类型不匹配时,Avalonia会回退到默认模板,显示项的ToString()结果(即类的完整名称)。
  2. 复用同一个UI元素实例:你提前创建了dataTmplt并在模板委托中返回它,导致所有列表项共享同一个UI元素实例,这会引发运行时异常或显示异常(因为一个UI元素不能同时属于多个父容器)。

修复后的代码(MainView.axaml.cs)

public partial class MainView : UserControl {
  public MainView() {
    InitializeComponent();
    var dynamicBookshelf = new BookshelfUC();
    var itemsCtlToAdd = new ItemsControl {
      [!ItemsControl.ItemsSourceProperty] = new Binding("Books")
    };

    // 修正:将模板类型改为BookVM,且每次创建新的UI元素实例
    itemsCtlToAdd.ItemTemplate = new FuncDataTemplate<BookVM>((item, _) =>
    {
      var borderedStack = new StackPanel();
      var bookBorder = new Border {
        BorderBrush = new SolidColorBrush(Colors.Brown),
        BorderThickness = new Avalonia.Thickness(2)
      };
      var title = new TextBlock {
        Background = new SolidColorBrush(Color.Parse("#BC8F6F")),
        Foreground = new SolidColorBrush(Colors.White),
        [!TextBlock.TextProperty] = new Binding("Title")
      };
      var authorAndYear = new TextBlock {
        // 可选优化:同时显示作者和年份(需自定义StringFormatConverter)
        // [!TextBlock.TextProperty] = new MultiBinding {
        //   Bindings = { new Binding("Author"), new Binding("YearPublished") },
        //   Converter = new StringFormatConverter("{0} ({1})")
        // }
        [!TextBlock.TextProperty] = new Binding("YearPublished")
      };

      borderedStack.Children.Add(title);
      borderedStack.Children.Add(authorAndYear);
      bookBorder.Child = borderedStack;

      var dataTmplt = new StackPanel();
      dataTmplt.Children.Add(bookBorder);
      return dataTmplt;
    });

    var shelfAfterBooks = new TextBlock {
      Background = new SolidColorBrush(Colors.Black),
      Foreground = new SolidColorBrush(Colors.White),
      Text = "Shelf on which the stack of book sits"
    };
    var bracket = new TextBlock {
      Background = new SolidColorBrush(Colors.LightGray),
      Text = "Mounting brackets"
    };

    dynamicBookshelf.stpBooksPlusShelf.Children.Add(itemsCtlToAdd);
    dynamicBookshelf.stpBooksPlusShelf.Children.Add(shelfAfterBooks);
    dynamicBookshelf.DataContext = new BookshelfVM();
    stpDynamicControls.Children.Add(dynamicBookshelf);
    stpDynamicControls.Children.Add(bracket);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 02:35:55