WPF从列表视图项激发视图模型命令

azpvetkf  于 2023-01-14  发布在  其他
关注(0)|答案(3)|浏览(154)

有一个WPF MVVM应用程序。在主视图上,我有一个元素列表,这些元素是用ListView.ItemTemplate定义的,因为我想有一个带有删除操作的上下文菜单。
Command与视图分离,并保存在ViewModel DreamListingViewModel 中。
问题是,在单击Delete时,我无法让它在ViewModelk上执行命令,因为上下文是项目的上下文,而不是项目容器的上下文。
我可以通过将上下文菜单定义移到列表视图元素之外来使其工作,但是当我打开上下文菜单时,它会 Flink ,好像它被调用了“20”次(我认为确实发生了,因为我在集合中有元素),无论如何,我需要一个干净的解决方案,我对XAML非常不好。

以下是我的视图外观:

<ListView.ItemTemplate>
                <DataTemplate>
                    <Grid Margin="0 5 0 5" Background="Transparent" Width="auto">

                        <Grid.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="Delete"
                                          Command="{Binding DeleteSelectedDream}" 
                                          CommandParameter="{Binding DeleteSelectedDream, 
                                                                     RelativeSource={RelativeSource 
                                                                     Mode=FindAncestor,
                                                                     AncestorType={x:Type viewmodels:DreamListingViewModel}}}"
                                    />
                            </ContextMenu>
                        </Grid.ContextMenu>
...

它是主窗口,在App.cs的通用主机中初始化:

public partial class App : Application
    {
        private readonly IHost _host;

        public App()
        {
            ...

            _host = Host.CreateDefaultBuilder().ConfigureServices(services =>
            {
                ...
                services.AddTransient<DreamListingViewModel>();
                services.AddSingleton((s) => new DreamListingView()
                {
                    DataContext = s.GetRequiredService<DreamListingViewModel>()
                });
                ...
            }).Build();

Command和CommandParameter值是我一直在尝试的,但它不起作用

下面是我的ViewModel的外观:

internal class DreamListingViewModel : ViewModelBase
    {
        public ICommand DeleteSelectedDream{ get; }
 ...

最后,当命令被触发时,我需要传递显示菜单的当前元素。
所以,这是我想要的:
1.用户用鼠标右键点击列表项- OK
1.看到带有删除条目- OK的菜单
1.点击“删除”后,将当前梦想(列表中的项目)作为参数- ERR,激发命令DeleteSelectedDream

thigvfpy

thigvfpy1#

您的示例缺少必要的信息,但我会尽力提供帮助。
首先,你需要验证你是否真的绑定到了你的视图模型。你使用的是Prism还是标准的WPF?在你的视图代码隐藏的构造函数中,为你的VM示例设置 DataContext

InitializeComponent(); 
this.DataContext = new DreamListingViewModel();

现在,您通过Mode 'FindAncestor'绑定到一个相对源,AncestorType被设置为视图模型的类型。这通常不起作用,因为视图模型并不是WPF视图的“可视化树”的一部分。也许您的ItemTemplate以某种方式将其连接起来。在我的一个大型WPF应用程序中,我使用Telerik UI for WPF和类似的方法,然而,我将上下文菜单的 DataContext 设置为 RelativeSource(设置为 Self)和 Path(设置为 PlacementTarget.DataContext)。
在我的例子中,你不必使用所有的XAML,只要观察我是怎么做的。用“ContextMenu”替换“RadContextMenu”,忽略挪威语单词-在这里,只使用你需要的:

<telerik:RadContextMenu x:Key="CanceledOperationsViewContextMenu" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, UpdateSourceTrigger=PropertyChanged}">
                <MenuItem Header="{Binding PatientName}" IsEnabled="False" Style="{StaticResource ContextMenuHeading}" />
                <MenuItem Header="Gå til aktuell SomeAcme-liste" IsEnabled="{Binding IsValid}" Command="{Binding NavigateToListCommand}" />
                <MenuItem Header="Åpne protokoll..." Command="{Binding CommonFirstProtocolCommand, Mode=OneWay}" CommandParameter="{Binding}" />
                <MenuItem Header="Åpne Opr.spl.rapport...." Command="{Binding CommonFirstNurseReportCommand, Mode=OneWay}" CommandParameter="{Binding}" />
            </telerik:RadContextMenu>

在您的示例中,它将是:

<ContextMenu x:Key="SomeContextMenu" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, UpdateSourceTrigger=PropertyChanged}">
                <MenuItem Header="Delete" />
                Command="{Binding DeleteSelectedDream}" 
                                      CommandParameter="{Binding DeleteSelectedDream, 
                                                                 RelativeSource={RelativeSource 
                                                                 Mode=FindAncestor,
                                                                 AncestorType={x:Type ListViewItem}}}"
                                />
            </telerik:RadContextMenu>

现在,我认为您正在使用类 ListViewItemhttps://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.listviewitem?view=netframework-4.8。您可能需要在此处指定DataContext.DeleteSelectedDream,以确保绑定到ICommand实现所在的DataContext。

pbpqsu0x

pbpqsu0x2#

意外地发现this的答案,这基本上是我所需要的,只是添加到它的CommandParameter发送项目,它的工作原理就像魔术!

<ListView Name="lvDreams" ItemsSource="{Binding Dreams}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid Margin="0 5 0 5" Background="Transparent" Width="auto">

                <Grid.ContextMenu>
                    <ContextMenu>
                        <MenuItem 
                            Header="Delete"
                            Command="{Binding DataContext.DeleteSelectedDream, Source={x:Reference lvDreams}}"
                            CommandParameter="{Binding}"
                        />
                    </ContextMenu>
                </Grid.ContextMenu>

                ...

        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
moiiocjp

moiiocjp3#

我认为以下是最简单的;也许是因为我不理解WPF,但它“简单”好记,而且它可以与我的MVVM模式一起工作。

<ListBox ItemsSource="{Binding MyViewModelItemsCollection, Mode=OneWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Background="Transparent" >
                <TextBlock Text="{Binding Path=Name, Converter={StaticResource FullPathToFileName}, Mode=OneWay}" Grid.Column="0">
                    <TextBlock.ContextMenu>
                        <ContextMenu>
                            <MenuItem
                               
                                Command="{Binding Path=DataContext.MyViewModelAction, RelativeSource={RelativeSource AncestorType=ListBox}}" 
                                CommandParameter="{Binding}"
                                Header="{Binding Name, Converter={StaticResource resourceFormat}, ConverterParameter={x:Static res:Resources.CONTEXT_MENU_BLOCK_APPLICATION}}">

                            </MenuItem>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </grid>
        </DataTemplate>
    </ListBox.ItemTemplate      
</ListBox>

MyViewModelXXXXXXX命名项位于Map到控件的数据上下文的视图模型中。

相关问题