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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 12:17:35