Caliburn.Micro: 如何将函数绑定到DataGrid的RowDetailsTemplate中的上下文菜单项?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Caliburn.Micro: 如何将函数绑定到DataGrid的RowDetailsTemplate中的上下文菜单项?相关的知识,希望对你有一定的参考价值。

我有一个Items的数据表格,其中包括一个行细节的SubItems数据表格。在主 "Items "数据表格中,有一个上下文菜单,它带有添加或删除项目的功能,这工作得很好。还有一个上下文菜单,用于行细节数据表格 "SubItems",有类似的功能,但我无法正确绑定。如果执行,我得到以下异常 "System.Exception: 'No target found for method AddSubItem.'"。

我已经拼命尝试了许多其他帖子上建议的解决方案,但到目前为止没有任何工作。我很感激任何见解。以下是我的测试代码。

ShellViewModel:

using System;
using Caliburn.Micro;
using ContextMenuTest.Models;

namespace ContextMenuTest.ViewModels
{
    public class ShellViewModel : Screen
    {
        public static Random rnd = new Random();
        private BindableCollection<ItemModel> items = new BindableCollection<ItemModel>();
        public BindableCollection<ItemModel> Items
        {
            get { return items; }
            set
            {
                items = value;
                NotifyOfPropertyChange(() => Items);
            }
        }

        private ItemModel selectedItem = new ItemModel(rnd);
        public ItemModel SelectedItem
        {
            get { return selectedItem; }
            set
            {
                selectedItem = value;
                NotifyOfPropertyChange(() => SelectedItem);
            }
        }

        private SubItemModel selectedSubItem = new SubItemModel(rnd);
        public SubItemModel SelectedSubItem
        {
            get { return selectedSubItem; }
            set
            {
                selectedSubItem = value;
                NotifyOfPropertyChange(() => SelectedSubItem);
            }
        }

        public ShellViewModel()
        {
            for(int i = 0; i < 10; i++)
            {
                Items.Add(new ItemModel(rnd));
            }
        }

        public void AddItem()
        {
            Items.Add(new ItemModel(rnd));
        }

        public void RemoveItem()
        {
            Items.Remove(SelectedItem);
        }

        public void AddSubItem(object e)
        {
            var _item = e as ItemModel;
            _item.SubItems.Add(new SubItemModel(rnd));
        }

        public void RemoveSubItem(object e)
        {
            var _item = e as ItemModel;
            _item.SubItems.Remove(SelectedSubItem);
        }
    }
}

ShellView:

<Window x:Class="ContextMenuTest.Views.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ContextMenuTest.Views"
        xmlns:cal="http://www.caliburnproject.org"
        mc:Ignorable="d"
        Title="ShellView" Height="450" Width="800"
        >

    <Grid Grid.Row="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <DataGrid x:Name="Items" Grid.Row="0" AutoGenerateColumns="False" CanUserResizeColumns="True" CanUserAddRows="false"
                        VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" MinHeight="100" SelectedItem="{Binding SelectedItem}">
            <DataGrid.ContextMenu>
                <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                    <MenuItem Header="Add Item" cal:Message.Attach="AddItem()"/>
                    <MenuItem Header="Remove Item" cal:Message.Attach="RemoveItem()"/>
                </ContextMenu>
            </DataGrid.ContextMenu>

            <DataGrid.Columns >
                <DataGridTextColumn Header="ID" Binding="{Binding Path=ID, UpdateSourceTrigger=PropertyChanged}" Width="*" IsReadOnly="True"/>
                <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
            </DataGrid.Columns>

            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <DataGrid ItemsSource="{Binding Path=SubItems}"  AutoGenerateColumns="False" CanUserResizeColumns="True" CanUserAddRows="false"
                                VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" MinHeight="100" 
                                  SelectedItem="{Binding Path=SelectedSubItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}">
                        <DataGrid.ContextMenu>
                            <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                                <MenuItem Header="Add Sub Item" cal:Message.Attach="AddSubItem($dataContext)"/>
                                <MenuItem Header="Remove Sub Item" cal:Message.Attach="RemoveSubItem($dataContext)"/>
                            </ContextMenu>
                        </DataGrid.ContextMenu>
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="ID" Binding="{Binding Path=ID}" Width="auto" IsReadOnly="True"/>
                            <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" Width="auto"/>
                            <DataGridTextColumn Header="Value" Binding="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}" Width="auto"/>
                        </DataGrid.Columns>
                    </DataGrid>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
    </Grid>
</Window>

ItemModel.ShellViewModel:ShellView:ItemModel。

using Caliburn.Micro;
using System;

namespace ContextMenuTest.Models
{
    public class ItemModel
    {
        public Guid ID { get; set; }
        public string Name { get; set; }
        public BindableCollection<SubItemModel> SubItems { get; set; } = new BindableCollection<SubItemModel>();

        public ItemModel(Random rnd)
        {
            ID = Guid.NewGuid();
            Name = "Item " + ID.ToString().Substring(3, 5);

            for (int i = 0; i < 5; i++)
            {
                SubItems.Add(new SubItemModel(rnd));
            }
        }
    }
}

SubItemModel:

using System;

namespace ContextMenuTest.Models
{
    public class SubItemModel
    {
        public Guid ID { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }

        public SubItemModel(Random rnd)
        {
            ID = Guid.NewGuid();
            Name = "SubItem " + ID.ToString().Substring(3,5);
            Value = rnd.Next();
        }
    }
}
答案

尝试使用TagHelper。它可以满足你的要求,示例代码如下。

<Grid Background="White" HorizontalAlignment="Stretch" Height="200" Name="GridLayout">
    <ListBox x:Name="ListBoxItems">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid Tag="{Binding DataContext, ElementName=GridLayout}">
                    <Grid.ContextMenu>
                        <ContextMenu Name="cm" cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Open"
                                      cal:Message.Attach="Open($dataContext)">
                            </MenuItem>
                        </ContextMenu>
                    </Grid.ContextMenu>

                    <TextBlock VerticalAlignment="Center">
                        .. text..
                    </TextBlock>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

以上是关于Caliburn.Micro: 如何将函数绑定到DataGrid的RowDetailsTemplate中的上下文菜单项?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 ValueConverter 应用于基于约定的 Caliburn.Micro 绑定示例?

使用 Caliburn.Micro 将命令绑定到 ListView 内的按钮

Caliburn Micro:如何设置绑定 UpdateSourceTrigger?

使用 Caliburn Micro 将 WebView2 绑定到 ViewModel

将UserControl绑定到ViewModel(Caliburn Micro WPF)

如何为caliburn.micro添加自定义约定?