在多重绑定期间,多个命令参数值未传递给视图模型

Multiple Command parameter values are not getting passed to view-model during multi-binding

我嵌套了绑定到名为 'CollectionOfAuthors' 的可观察集合的菜单项。

这是菜单项层次结构: 作者 -->AuthorName1-->BookName1,BookName2,BookName3

作者是在作者姓名列表中打开的 TopLevelMenuItem,这样每个作者姓名都会在书籍列表中打开。

当通过 NavigateToBook 命令点击每个 BookName 菜单项时,我想将 BookName、AuthorName 和 AuthorID 作为命令参数发送到 ViewModel, 但我发现空值作为 (DependencyProperty.UnsetValue) 传递给 ViewModel。

想知道需要什么更正吗?

View.xaml

<Menu>
                            <MenuItem Header="Authors" x:Name="TopLevelMenuItem"
                                      ItemsSource="{Binding CollectionOfAuthors, Mode=TwoWay}">
                                <in:Interaction.Triggers>
                                    <in:EventTrigger EventName="PreviewMouseLeftButtonDown">
                                        <in:InvokeCommandAction Command="{Binding DataContext.RefreshAuthorsList,RelativeSource={RelativeSource AncestorType=Menu}}"/>
                                    </in:EventTrigger>
                                </in:Interaction.Triggers>
                                <MenuItem.ItemTemplate>
                                    <HierarchicalDataTemplate ItemsSource="{Binding Path=Books}">
                                        <StackPanel>
                                            <TextBlock x:Name="tbAuthor" Text="{Binding AuthorName}"/>
                                            <TextBlock x:Name="tbAuthorID" Text="{Binding AuthorID}" Visibility="Collapsed"/>
                                        </StackPanel>
                                        <HierarchicalDataTemplate.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock x:Name="tbBookName" Text="{Binding}">
                                                    <TextBlock.InputBindings>
                                                        <MouseBinding Command="{Binding DataContext.NavigateToBook, RelativeSource={RelativeSource AncestorType=Menu}}"  MouseAction="LeftClick" >
                                                            <MouseBinding.CommandParameter>
                                                                <MultiBinding Converter="{StaticResource MultiCommandConverter}">
                                                                    <Binding Path="Text" ElementName="tbBookName"/>
                                                                    <Binding Path="DataContext.AuthorName" RelativeSource="{RelativeSource AncestorLevel=2, AncestorType=MenuItem}" />
                                                                    <Binding Path="DataContext.AuthorID" RelativeSource="{RelativeSource AncestorLevel=2, AncestorType=MenuItem}" />
                                                                </MultiBinding>
                                                            </MouseBinding.CommandParameter>
                                                        </MouseBinding>
                                                    </TextBlock.InputBindings>
                                                </TextBlock>
                                            </DataTemplate>
                                        </HierarchicalDataTemplate.ItemTemplate>
                                    </HierarchicalDataTemplate>
                                </MenuItem.ItemTemplate>
                            </MenuItem>
                        </Menu>

ViewModel.cs

public ICommand NavigateToBook
{
   get { return new DelegateCommand(NavigateToBookExecute); }
}

private void NavigateToBookExecute(object obj)
{
     string selectedBookName = ((object[])obj)[0].ToString();
     string selectedAuthorName = ((object[])obj)[1].ToString();
     string selectedAuhorID = ((object[])obj)[2].ToString();           
}

public ICommand RefreshAuthorsList
    {
        get { return new DelegateCommand(RefreshAuthorsListExecute); }
    }


    private void RefreshAuthorsListExecute(object m)
    {
         CollectionOfAuthors = new ObservableCollection<Author>();

           //Here AuthorDetails is another global collection which gets loaded during constructor call
           foreach (var objAuthorItem in AuthorDetails)
           {
               CollectionOfAuthors.Add(new Author
                {
                    AuthorName = objAuthorItem.DisplayName,
                    Books = objAuthorItem.ListOfBooks,
                    AuthorID = objAuthorItem.Id,
                });
           }
    }   

private ObservableCollection<Author> _collectionOfAuthors; 

public ObservableCollection<Author> CollectionOfAuthors 
{ 
 get { return _collectionOfAuthors; } 
 set { SetProperty(ref _collectionOfAuthors, value); } 
} 

Author.cs

public class Author 
{ 

public string AuthorName { get; set; } 

public string AuthorID { get; set; } 

List<string>Books = new List<string>();

} 

MultiCommandConverter.cs

public class MultiCommandConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.Clone();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

由于您在顶级菜单项中有命令,因此该命令甚至会在您的内部命令之前尝试调用,无论什么事件应该触发它。 作为解决方法,您可以将 TopMenuItem 的 IsSubmenuOpen 属性 作为 CommandParameter 传递,并检查菜单是否打开,然后在命令的执行操作中,您可以检查菜单是否打开然后继续或 return。这将阻止您的项目被刷新。

你的命令的CallStack是:

  • 点击图书菜单项
  • 刷新列表命令运行
  • 正在刷新项目,删除旧项目
  • 绑定正在尝试从刚刚删除的项目中获取属性

示例解决方案:

View.xaml

<Menu>
    <MenuItem Header="Authors"  Background="Red" x:Name="TopLevelMenuItem"
                              ItemsSource="{Binding CollectionOfAuthors, Mode=TwoWay}">
        <in:Interaction.Triggers>
            <in:EventTrigger EventName="PreviewMouseLeftButtonDown">
                <in:InvokeCommandAction Command="{Binding DataContext.RefreshAuthorsList,RelativeSource={RelativeSource AncestorType=Menu}}" CommandParameter="{Binding IsSubmenuOpen , ElementName=TopLevelMenuItem}"/>
            </in:EventTrigger>
        </in:Interaction.Triggers>
        <MenuItem.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Books}">
                <StackPanel DataContext="{Binding}">
                    <TextBlock x:Name="tbAuthor" Text="{Binding AuthorName}"/>
                    <TextBlock x:Name="tbAuthorID" Text="{Binding AuthorID}" Visibility="Collapsed"/>
                </StackPanel>
                <HierarchicalDataTemplate.ItemTemplate>
                    <DataTemplate>
                        <TextBlock x:Name="tbBookName" DataContext="{Binding}"  Text="{Binding}">
                                <in:Interaction.Triggers>
                                    <in:EventTrigger EventName="MouseDown">
                                        <in:InvokeCommandAction  Command="{Binding DataContext.NavigateToBook, RelativeSource={RelativeSource AncestorType=Menu}}"  >
                                            <in:InvokeCommandAction.CommandParameter>
                                                <MultiBinding Converter="{StaticResource MultiCommandConverter}">
                                                    <Binding Path="Text" ElementName="tbBookName"/>
                                                    <Binding Path="DataContext.AuthorName" RelativeSource="{RelativeSource AncestorLevel=1, AncestorType=StackPanel}" />
                                                    <Binding Path="DataContext.AuthorID" RelativeSource="{RelativeSource AncestorLevel=1, AncestorType=StackPanel}" />
                                                </MultiBinding>
                                            </in:InvokeCommandAction.CommandParameter>
                                        </in:InvokeCommandAction>
                                    </in:EventTrigger>
                                </in:Interaction.Triggers>
                        </TextBlock>
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
        </MenuItem.ItemTemplate>
    </MenuItem>
</Menu>

然后在你的 RefreshAuthorsListExecute

private void RefreshAuthorsListExecuteExecute(object m)
{
    if ((bool)m)
        return;