C# 使用ICommand的WPF绑定错误
我有一个简单的WPF示例,试图将ListBox的选定事件绑定到视图模型中的ICommand XAMLC# 使用ICommand的WPF绑定错误,c#,wpf,xaml,data-binding,C#,Wpf,Xaml,Data Binding,我有一个简单的WPF示例,试图将ListBox的选定事件绑定到视图模型中的ICommand XAML <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<ListBox ItemsSource="{Binding Items}"
Selected="{Binding DoSomething}"/>
</Grid>
</Window>
查看模型
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Items = new List<string>();
Items.Add("A");
Items.Add("B");
Items.Add("C");
DoSomething = new MyCommand();
}
public List<string> Items { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public ICommand DoSomething { get; set; }
}
public class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { }
}
}
名称空间WpfApp1
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DataContext=新的ViewModel();
}
}
公共类视图模型:INotifyPropertyChanged
{
公共视图模型()
{
项目=新列表();
项目。添加(“A”);
项目。添加(“B”);
项目。添加(“C”);
DoSomething=新的MyCommand();
}
公共列表项{get;set;}
公共事件属性更改事件处理程序属性更改;
公共ICommand DoSomething{get;set;}
}
公共类MyCommand:ICommand
{
公共事件处理程序CanExecuteChanged;
public bool CanExecute(对象参数){return true;}
public void Execute(对象参数){}
}
}
该错误发生在InitializeComponent的构造函数中
XamlParseException:无法在上设置“绑定”
“ListBox”类型的“AddSelectedHandler”属性。“绑定”只能
可以根据DependencyObject的DependencyProperty设置
如何从ListBox控件的选定事件在视图模型中调用ICommand?在ListBox上选择是一个事件。您拥有SelectedItem,可以将其绑定到与viewmodel上列表元素类型相同的属性:
<Grid>
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding MyItem}"/>
</Grid>
对于您的命令,您需要一个处理CommandSource的控件,如按钮:
<Button Command="{Binding DoSomething}" CommandParameter="{Binding}" />
像这样绑定它,将使WPF能够识别您的ICommand。CommandParameter是可选的。这是因为“Selected”是事件,实际上您不能将命令直接绑定到任何事件。但是有几种可行的方法来解决这个问题。
在下面的示例中,我使用自定义命令实现绑定到WPF事件,并将EventArgs作为命令的参数。您需要System.Windows.Interactivity程序集
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Example"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView
ItemsSource="{Binding Country}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<local:InteractiveCommand Command="{Binding SelectedCountryCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
<Grid
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label
HorizontalAlignment="Center"
Content="SELECTED ITEMS:"/>
<ListView
Grid.Row="1"
ItemsSource="{Binding SelectedCountry}"/>
</Grid>
</Grid>
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DataContext=新的MainViewModel();
}
}
公共类MainViewModel:INotifyPropertyChanged
{
公共列表国家{get;set;}=新列表
{
“美国”,
“加拿大”,
“法国”,
“德语”,
“日本”,
“意大利”,
“乌克兰”,
“波兰”,
“大不列颠”,
“土耳其”
};
public ObservableCollection SelectedCountry{get;set;}=new ObservableCollection();
公共ICommand SelectedCountryCommand=>
_selectedCountryCommand???(\u selectedCountryCommand=new RelayCommand(
参数=>
{
SelectedCountry.Clear();
SelectedCountry.Add((参数为SelectionChangedEventArgs.AddedItems[0].ToString());
}));
专用ICommand_selectedCountryCommand;
//INotifyPropertyChanged实现
公共事件属性更改事件处理程序属性更改;
[NotifyPropertyChangedInvocator]
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共类中继命令:ICommand
{
私有只读操作\u执行;
私有只读谓词_canExecute;
公共中继命令(操作执行)
:此(执行,空)
{
}
公共RelayCommand(操作执行,谓词canExecute)
{
_execute=execute??抛出新的ArgumentNullException(nameof(execute));
_canExecute=canExecute;
}
[调试步骤至]
公共布尔CanExecute(对象参数)
{
返回_canExecute?.Invoke(参数)?true;
}
公共事件事件处理程序CanExecuteChanged
{
add=>CommandManager.RequerySuggested+=值;
remove=>CommandManager.RequerySuggested-=值;
}
public void Execute(对象参数)
{
_执行(参数);
}
}
公共类InteractiveCommand:TriggerAction
{
受保护的覆盖无效调用(对象参数)
{
if(AssociatedObject==null)
返回;
var command=ResolveCommand();
if(command!=null&&command.CanExecute(参数))
{
command.Execute(参数);
}
}
专用ICommand ResolveCommand()
{
ICommand命令=null;
if(命令!=null)
{
返回命令;
}
if(AssociatedObject!=null)
{
foreach(AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)中的变量信息)
{
if(typeof(ICommand).IsAssignableFrom(info.PropertyType)&&string.Equals(info.Name、CommandName、StringComparison.Ordinal))
{
command=(ICommand)info.GetValue(AssociatedObject,null);
}
}
}
返回命令;
}
私有字符串_commandName;
公共字符串命令名
{
得到
{
ReadPremission();
返回命令名;
}
设置
{
if(CommandName==值)
返回;
writeStreamble();
_commandName=值;
WritePostscript();
}
}
#地区司令部
公共ICommand命令
{
get=>(ICommand)GetValue(CommandProperty);
set=>SetValue(CommandProperty,value);
}
//使用依赖项
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Example"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView
ItemsSource="{Binding Country}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<local:InteractiveCommand Command="{Binding SelectedCountryCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
<Grid
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label
HorizontalAlignment="Center"
Content="SELECTED ITEMS:"/>
<ListView
Grid.Row="1"
ItemsSource="{Binding SelectedCountry}"/>
</Grid>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
public class MainViewModel:INotifyPropertyChanged
{
public List<string> Country { get; set; } = new List<string>
{
"USA",
"CANADA",
"FRANCE",
"GERMAN",
"JAPAN",
"ITALY",
"UKARAINE",
"POLAND",
"GREAT BRITAIN",
"TURKEY"
};
public ObservableCollection<string> SelectedCountry { get; set; } = new ObservableCollection<string>();
public ICommand SelectedCountryCommand =>
_selectedCountryCommand ?? (_selectedCountryCommand = new RelayCommand(
param =>
{
SelectedCountry.Clear();
SelectedCountry.Add((param as SelectionChangedEventArgs).AddedItems[0].ToString());
}));
private ICommand _selectedCountryCommand;
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke(parameter) ?? true;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
public class InteractiveCommand : TriggerAction<DependencyObject>
{
protected override void Invoke(object parameter)
{
if (AssociatedObject == null)
return;
var command = ResolveCommand();
if (command != null && command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
private ICommand ResolveCommand()
{
ICommand command = null;
if (Command != null)
{
return Command;
}
if (AssociatedObject != null)
{
foreach (var info in AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (typeof(ICommand).IsAssignableFrom(info.PropertyType) && string.Equals(info.Name, CommandName, StringComparison.Ordinal))
{
command = (ICommand)info.GetValue(AssociatedObject, null);
}
}
}
return command;
}
private string _commandName;
public string CommandName
{
get
{
ReadPreamble();
return _commandName;
}
set
{
if (CommandName == value)
return;
WritePreamble();
_commandName = value;
WritePostscript();
}
}
#region Command
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
// Using a DependencyProperty as the backing store for Command. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null));
#endregion
}