C# 工具栏中的WPF CommandParameter绑定失败?

C# 工具栏中的WPF CommandParameter绑定失败?,c#,wpf,mvvm,icommand,commandparameter,C#,Wpf,Mvvm,Icommand,Commandparameter,情况是这样的:这有点难以描述,因此,如果需要,请跳到重新创建代码并将代码复制/粘贴到新项目中的步骤。ListViewModel包含ViewModel(项目)列表和ICommand(操作)列表。MainWindow有一个绑定到操作的工具栏、一个绑定到项目的ListView和一个绑定到ListView中的操作的ContextMenu。在我的ICommand(Command.cs)实现中,我添加了插入自定义代码(OnIsVisible属性)的功能,用于检查命令可见性是否应可见或折叠。这段代码非常适用于

情况是这样的:这有点难以描述,因此,如果需要,请跳到重新创建代码并将代码复制/粘贴到新项目中的步骤。ListViewModel包含ViewModel(项目)列表和ICommand(操作)列表。MainWindow有一个绑定到操作的工具栏、一个绑定到项目的ListView和一个绑定到ListView中的操作的ContextMenu。在我的ICommand(Command.cs)实现中,我添加了插入自定义代码(OnIsVisible属性)的功能,用于检查命令可见性是否应可见或折叠。这段代码非常适用于工具栏和ContextMenu中的操作,直到您打开ContextMenu。然后工具栏的CommandParameter将永远为空,除非ContextMenu处于打开状态

重新创建的步骤:

  • 在列表视图中选择一个项目
  • 单击上下文菜单中的“选中时显示”
  • 在列表视图中选择另一项
  • 此时,CommandParameter绑定对command对象始终为空。因此,工具栏中的“选择时显示”按钮将不再显示

    代码:

    在名为“NullParameter”的新WPF应用程序项目中,创建/编辑以下文件

    MainWindow.xaml:

    <Window x:Class="NullParameter.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.Resources>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="IsSelected"
                        Value="{Binding IsSelected}" />
            </Style>
            <Style x:Key="contextMenuItemStyle"
                   TargetType="{x:Type MenuItem}">
                <Setter Property="Header"
                        Value="{Binding Header}" />
                <Setter Property="Command"
                        Value="{Binding}" />
                <Setter Property="Visibility"
                        Value="{Binding Visibility}" />
                <Setter Property="CommandParameter"
                        Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.SelectedItems}" />
            </Style>
            <DataTemplate x:Key="toolBarActionItemTemplate">
                <Button Content="{Binding Header}"
                        Command="{Binding}"
                        CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToolBar}, Path=Tag.SelectedItems}"
                        Visibility="{Binding Visibility}" />
            </DataTemplate>
        </Window.Resources>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.Children>
                <ToolBar Grid.Row="0"
                         ItemsSource="{Binding Actions}"
                         ItemTemplate="{StaticResource toolBarActionItemTemplate}"
                         Tag="{Binding}" />
                <ListView Grid.Row="1"
                          ItemsSource="{Binding Items}"
                          SelectionMode="Extended"
                          Tag="{Binding}">
                    <ListView.ContextMenu>
                        <ContextMenu ItemsSource="{Binding Actions}"
                                     ItemContainerStyle="{StaticResource contextMenuItemStyle}" />
                    </ListView.ContextMenu>
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Id"
                                            DisplayMemberBinding="{Binding Id}"/>
                        </GridView>
                    </ListView.View>
                </ListView>
            </Grid.Children>
        </Grid>
    
    </Window>
    
    Command.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Input;
    
    namespace NullParameter
    {
        public class Command : CommandBase
        {
            public Action OnExecute { get; set; }
            public Func<bool> OnIsVisible { get; set; }
            public Func<string> OnCanExecute { get; set; }
            public string Header { get; set; }
    
            protected override string DoCanExecute(object parameter)
            {
                if (OnCanExecute == null)
                    return null;
    
                return OnCanExecute();
            }
    
            protected override bool DoIsVisible(object parameter)
            {
                if (OnIsVisible == null)
                    return true;
    
                return OnIsVisible();
            }
    
            protected override void DoExecute(object parameter)
            {
                if (OnExecute == null)
                    return;
    
                OnExecute();
            }
        }
    
        public class Command<T> : CommandBase
            where T : class
        {
            public Action<T> OnExecute { get; set; }
            public Func<T, bool> OnIsVisible { get; set; }
            public Func<T, string> OnCanExecute { get; set; }
            public string Header { get; set; }
    
            protected T Convert(object parameter)
            {
                if (parameter == null)
                    Console.WriteLine("NULL");
    
                return parameter as T;
            }
    
            protected override string DoCanExecute(object parameter)
            {
                if (OnCanExecute == null)
                    return null;
    
                var p = Convert(parameter);
                if (p == null)
                    return "Invalid Parameter";
    
                return OnCanExecute(p);
            }
    
            protected override bool DoIsVisible(object parameter)
            {
                if (OnIsVisible == null)
                    return true;
    
                var p = Convert(parameter);
                if (p == null)
                    return false;
    
                return OnIsVisible(p);
            }
            protected override void DoExecute(object parameter)
            {
                if (OnExecute == null)
                    return;
    
                var p = Convert(parameter);
                if (p == null)
                    return;
    
                OnExecute(p);
            }
        }
    }
    

    因此,在阅读了一些关于CommandParameter DependencyProperty有多麻烦的帖子之后,我已经完全放弃了使用它。相反,我只是通过在ListViewModel中传递所选项目的列表来构造命令对象。然后在CanExecute和Execute方法中,我使用存储的选定项列表,而不是.NET提供的参数

    虽然这提供了一个可行的解决方案,但并不一定能解决最初问题所带来的问题。因此,我将把这一点留在这里,作为对其他不幸遇到这些问题的人的建议

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Input;
    
    namespace NullParameter
    {
        public class Command : CommandBase
        {
            public Action OnExecute { get; set; }
            public Func<bool> OnIsVisible { get; set; }
            public Func<string> OnCanExecute { get; set; }
            public string Header { get; set; }
    
            protected override string DoCanExecute(object parameter)
            {
                if (OnCanExecute == null)
                    return null;
    
                return OnCanExecute();
            }
    
            protected override bool DoIsVisible(object parameter)
            {
                if (OnIsVisible == null)
                    return true;
    
                return OnIsVisible();
            }
    
            protected override void DoExecute(object parameter)
            {
                if (OnExecute == null)
                    return;
    
                OnExecute();
            }
        }
    
        public class Command<T> : CommandBase
            where T : class
        {
            public Action<T> OnExecute { get; set; }
            public Func<T, bool> OnIsVisible { get; set; }
            public Func<T, string> OnCanExecute { get; set; }
            public string Header { get; set; }
    
            protected T Convert(object parameter)
            {
                if (parameter == null)
                    Console.WriteLine("NULL");
    
                return parameter as T;
            }
    
            protected override string DoCanExecute(object parameter)
            {
                if (OnCanExecute == null)
                    return null;
    
                var p = Convert(parameter);
                if (p == null)
                    return "Invalid Parameter";
    
                return OnCanExecute(p);
            }
    
            protected override bool DoIsVisible(object parameter)
            {
                if (OnIsVisible == null)
                    return true;
    
                var p = Convert(parameter);
                if (p == null)
                    return false;
    
                return OnIsVisible(p);
            }
            protected override void DoExecute(object parameter)
            {
                if (OnExecute == null)
                    return;
    
                var p = Convert(parameter);
                if (p == null)
                    return;
    
                OnExecute(p);
            }
        }
    }
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    
    namespace NullParameter
    {
        public class ListViewModel
        {
            public IList<ViewModel> Items { get; private set; }
            public IList SelectedItems { get; private set; }
            public IList<ICommand> Actions { get; private set; }
    
            public ListViewModel()
            {
                var items = new ObservableCollection<ViewModel>()
                {
                    new ViewModel()
                    {
                        Id = 1
                    },
                    new ViewModel()
                    {
                        Id = 2
                    },
                    new ViewModel()
                    {
                        Id = 3
                    },
                };
                Items = items;
                SelectedItems = items;
                Actions = new List<ICommand>()
                {
                    new Command()
                    {
                        OnExecute = ShowAlways,
                        Header = "Show Always"
                    },
                    new Command<IList<ViewModel>>()
                    {
                        OnExecute = ShowWhenSelected,
                        OnIsVisible = (list) => { return list.Count(o => o.IsSelected) > 0; },
                        Header = "Show When Selected"
                    }
                };
            }
    
            public void ShowAlways()
            {
                Console.WriteLine("ShowAlways()");
            }
    
            public void ShowWhenSelected(IList<ViewModel> viewModels)
            {
                Console.WriteLine("ShowWhenSelected({0})", String.Join(",", viewModels.Where(o => o.IsSelected).Select(o => o.Id)));
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace NullParameter
    {
        public class ViewModel : INotifyPropertyChanged
        {
            private bool _isSelected;
            private int _id;
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public int Id
            {
                get { return _id; }
                set
                {
                    if (_id == value)
                        return;
    
                    _id = value;
    
                    if (PropertyChanged == null)
                        return;
    
                    PropertyChanged(this, new PropertyChangedEventArgs("Id"));
                }
            }
            public bool IsSelected
            {
                get { return _isSelected; }
                set
                {
                    if (_isSelected == value)
                        return;
    
                    _isSelected = value;
    
                    if (PropertyChanged == null)
                        return;
    
                    PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
                }
            }
        }
    }