在ToolBar中WPF CommandParameter绑定失败?

时间:2015-03-23 01:07:18

标签: c# wpf mvvm icommand commandparameter

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

重新创建的步骤:

  1. 在ListView中选择一个项目
  2. 单击ContextMenu中的“选择时显示”
  3. 在ListView中选择另一个项目
  4. 此时,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>
    

    CommandBase.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;
        }
    }
    

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

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

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

1 个答案:

答案 0 :(得分:0)

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

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