0

情况如下:描述起来有点困难,所以如果需要,请跳至重新创建代码并将代码复制/粘贴到新项目中的步骤。ListViewModel 包含一个 ViewModel (Items) 列表和一个 ICommand (Actions) 列表。MainWindow 有一个绑定到 Actions 的 ToolBar、一个绑定到 Items 的 ListView,以及一个绑定到 ListView 中的 action 的 ContextMenu。在我的 ICommand (Command.cs) 实现中,我添加了插入自定义代码(OnIsVisible 属性)的功能,用于检查命令 Visibility 是否应该为 Visible 或 Collapsed。在您打开 ContextMenu 之前,此代码非常适用于 ToolBar 和 ContextMenu 中的操作。然后 ToolBar 的 CommandParameter 永远为 null,除非 ContextMenu 打开。

重建步骤:

  1. 在 ListView 中选择一个项目
  2. 在 ContextMenu 中单击“选择时显示”
  3. 在 ListView 中选择另一个项目

此时,CommandParameter 绑定对于命令对象将始终为 NULL。因此工具栏中的“选择时显示”按钮将不再出现。

代码:

在名为“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>

命令库.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace NullParameter
{
    public abstract class CommandBase : ICommand, INotifyPropertyChanged
    {
        private Visibility _visibility;
        private string _error;

        public Visibility Visibility
        {
            get { return _visibility; }
            protected set
            {
                if (_visibility == value)
                    return;

                _visibility = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("Visibility"));
            }
        }
        public string Error
        {
            get { return _error; }
            set
            {
                if (_error == value)
                    return;

                _error = value;

                if (PropertyChanged == null)
                    return;

                PropertyChanged(this, new PropertyChangedEventArgs("Error"));
            }
        }

        public bool CanExecute(object parameter)
        {
            Error = DoCanExecute(parameter);

            Visibility = DoIsVisible(parameter) ? Visibility.Visible : Visibility.Collapsed;

            return Error == null;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            DoExecute(parameter);
        }

        protected abstract string DoCanExecute(object parameter);
        protected abstract bool DoIsVisible(object parameter);
        protected abstract void DoExecute(object parameter);

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

命令.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);
        }
    }
}

列表视图模型.cs

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)));
        }
    }
}

视图模型.cs

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"));
            }
        }
    }
}
4

1 回答 1

0

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

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

于 2015-03-24T20:14:02.533 回答