Вот что: немного сложно описать, поэтому пропустите шаги, чтобы воссоздать и скопировать/вставить код в новый проект, если хотите. 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?
Шаги для воссоздания:
- Выберите элемент в ListView
- Нажмите кнопку "Показать Когда Selected" в ContextMenu
- Выбрать другой элемент в 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"));
}
}
}
}