Avalonia .NET MVVM中如何为TextBlock绑定ICommand实现书籍添加
问题描述
使用Avalonia MVVM开发C#应用,窗口左侧是按分类分组的书籍TreeView,右侧是已选书籍的DataGrid,需求是用户双击左侧书籍即可将其添加到右侧列表。尝试为TextBlock绑定ICommand未成功,添加按钮时出现DataContext不匹配问题,添加SelectedItem后也无法处理事件,附上相关代码寻求技术帮助。
原MainWindow.axaml代码
<DockPanel> <TreeView DockPanel.Dock="Left" ItemsSource="{Binding BookTree.Genres}"> <TreeView.DataTemplates> <TreeDataTemplate DataType="models:Genre" ItemsSource="{Binding Books}"> <Border HorizontalAlignment="Left" BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Padding="15 3"> <TextBlock Text="{Binding Name}"/> </Border> </TreeDataTemplate> <DataTemplate DataType="models:Book"> <StackPanel Orientation="Vertical"> <TextBlock Text="{Binding Title}"/> <TextBlock Foreground="Red" Text="{Binding Author}"/> </StackPanel> </DataTemplate> </TreeView.DataTemplates> </TreeView> <DataGrid DockPanel.Dock="Right" ItemsSource="{Binding SelectedBooks.SelectedBooks}"> <DataGrid.Columns> <DataGridTextColumn Header="Title" Binding="{Binding Title}" /> <DataGridTextColumn Header="Author" Binding="{Binding Author}" /> </DataGrid.Columns> </DataGrid> </DockPanel>
原MainWindowViewModel.cs代码
namespace SummerBooks.ViewModels { public class MainWindowViewModel : ViewModelBase { public ICommand AddBookCommand { get; } public BookTreeViewModel BookTree { get; } public SelectedBooksViewModel SelectedBooks { get; } public MainWindowViewModel() { BookTree = new BookTreeViewModel(); SelectedBooks = new SelectedBooksViewModel(); AddBookCommand = ReactiveCommand.Create<Book>(book => { SelectedBooks.AddBook(book); }); } } public class BookTreeViewModel : ViewModelBase { public SelectedBooksViewModel SelectedBooks { get; } public List<Genre> Genres { get; } public BookTreeViewModel() { Genres = new List<Genre> { new Genre { Name = "Fantastic", Books = new List<Book> { new Book { Title = "1984", Author = "George Orwell"}, new Book { Title = "Solaris", Author = "Stanislav Lem"} } }, new Genre { Name = "Horror", Books = new List<Book> { new Book { Title = "Ridges of Madness", Author = "Howard Lovecraft" }, new Book { Title = "Dreamcatcher", Author = "Stephen King" } } }, new Genre { Name = "Fantasy", Books = new List<Book> { new Book { Title = "Lord of the Rings", Author = "John Tolkien" }, new Book { Title = "Harry Potter and the Philosopher's Stone", Author = "Joanne Rowling" } } } }; } } public class SelectedBooksViewModel : ViewModelBase { public ObservableCollection<Book> SelectedBooks { get; } public SelectedBooksViewModel() { SelectedBooks = new ObservableCollection<Book>(); } public void AddBook(Book book) { SelectedBooks.Add(book); //var box = MessageBoxManager.GetMessageBoxStandard("Caption", $"{SelectedBooks.Count}", MsBox.Avalonia.Enums.ButtonEnum.Ok); //box.ShowAsync(); } } }
解决方案
核心问题是Book项的DataContext是Book对象,而AddBookCommand位于MainWindowViewModel中,直接绑定会出现DataContext不匹配。解决方法是通过RelativeSource向上查找窗口的DataContext,将双击事件绑定到MainWindowViewModel的AddBookCommand,并传递当前Book作为参数。
修改后的MainWindow.axaml(关键部分)
修改Book的DataTemplate,给外层StackPanel添加DoubleTapped命令绑定:
<DataTemplate DataType="models:Book"> <StackPanel Orientation="Vertical" DoubleTapped="{Binding DataContext.AddBookCommand, RelativeSource={RelativeSource AncestorType=Window}, CommandParameter={Binding}}"> <TextBlock Text="{Binding Title}"/> <TextBlock Foreground="Red" Text="{Binding Author}"/> </StackPanel> </DataTemplate>
解释
RelativeSource={RelativeSource AncestorType=Window}:向上找到窗口的DataContext(即MainWindowViewModel),以此访问AddBookCommand。CommandParameter={Binding}:将当前Book对象作为参数传递给AddBookCommand,与ViewModel中ReactiveCommand.Create<Book>的参数类型完全匹配。
验证逻辑
双击左侧TreeView中的任意书籍项,AddBookCommand会被触发,调用SelectedBooks.AddBook(book)将书籍添加到右侧DataGrid的数据源中。由于SelectedBooks是ObservableCollection<Book>,DataGrid会自动更新显示新添加的书籍。
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

