2015-03-23 5 views
0

Вот что: немного сложно описать, поэтому пропустите шаги, чтобы воссоздать и скопировать/вставить код в новый проект, если хотите. ListViewModel содержит список ViewModel (Items) и список ICommand (Actions). MainWindow имеет ToolBar, который привязывается к Actions, ListView, который привязывается к элементам, и ContextMenu, который связывается с действиями в ListView. В моей реализации ICommand (Command.cs) я добавил возможность вставлять собственный код (свойство OnIsVisible), который проверяет, должна ли Visibility видимость или свертывание команды. Этот код отлично подходит для действий в ToolBar и ContextMenu, пока вы не откроете ContextMenu. Тогда CommandParameter для ToolBar навсегда null, за исключением случаев, когда ContextMenu открыт.Ошибка привязки WPF CommandParameter в ToolBar?

Шаги для воссоздания:

  1. Выберите элемент в ListView
  2. Нажмите кнопку "Показать Когда Selected" в ContextMenu
  3. Выбрать другой элемент в ListView

На данный момент , привязка CommandParameter всегда будет NULL для объекта команды. Таким образом, кнопка «Показать при выборе» в панели инструментов больше не будет отображаться.

Код:

В новом проекте приложения WPF под названием "NullParameter", создавать/редактировать следующие файлы ...

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

ответ

0

Так после прочтения несколько сообщений о том, как Glitchy CommandParameter DependencyProperty есть, я отказался от его использования полностью. Вместо этого я просто создаю объекты Command, передавая в список выбранных элементов в моем ListViewModel. Затем в методе CanExecute и Execute я использую сохраненный список выбранных элементов вместо параметра .NET.

Хотя это обеспечивает приемлемое решение, оно не обязательно решает проблему, возникающую из-за начального вопроса. Поэтому я оставлю это здесь в качестве предложения для кого-то еще, к сожалению, для этих проблем.