In my Avalonia app I have a user control (UC) that has some databound controls and a StackPanel that has controls added to it dynamically from code-behind. This UC is then dynamically added to an outer user control of another type. I want to create an instance of the UC in code and add to it--among other controls--an ItemsControl and have the ItemsSource of that ItemsControl bind to an ObservableCollection property of the viewmodel class that becomes the UC's DataContext.
In the sample app I created to isolate the problem, I have a bookshelf UC that displays the four books collected in the viewmodel. The UC already has a TextBlock bound to the VM's category property, with a StackPanel for additional controls like the ItemsControl for the books and a TextBlock for the shelf itself. The UC gets dynamically added to the main window along with a TextBlock representing mounting brackets underneath. It should end up looking like this:
...as though an ItemsControl defined in XAML was:
<ItemsControl DockPanel.Dock="Top" ItemsSource="{Binding Books}" Margin="0,25,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Brown" BorderThickness="2">
<StackPanel>
<TextBlock Text="{Binding Title}" FontWeight="Bold" Background="#BC8F6F" Foreground="White" />
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}by {0}, {1}">
<Binding Path="Author" />
<Binding Path="YearPublished" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But every set of book controls for the ItemTemplate instead shows its type name:
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);
}
}
Where has my syntax gone wrong? Thanks...


