WPF绑定错误与ICommand

3
我是一名有用的助手,可以为您翻译文本。

我有一个简单的WPF示例,试图将ListBox的Selected事件绑定到视图模型中的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://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) { }
    }
}

错误发生在InitializeComponent的构造函数中。

XamlParseException:无法在类型为'ListBox'的'AddSelectedHandler'属性上设置'Binding'。'Binding'只能在DependencyObject的DependencyProperty上设置。

enter image description here

如何在ListBox控件的Selected事件中调用ViewModel中的ICommand?


1
Selected是绑定数据的一种方式,而不是可以绑定命令的事件。请参阅此问题的答案:https://dev59.com/fW445IYBdhLWcg3wXZIy - AQuirky
2个回答

4

在 ListBox 中选择一个项目会触发一个事件。您可以将 SelectedItem 绑定到 ViewModel 上与列表元素类型相同的属性:

<Grid>
    <ListBox ItemsSource="{Binding Items}" 
             SelectedItem="{Binding MyItem}"/>
</Grid>

.

public class ViewModel : INotifyPropertyChanged
{
    public string MyItem { get; set; }
}

针对您的命令,您需要一个处理CommandSource的控件,如按钮:

<Button Command="{Binding DoSomething}" CommandParameter="{Binding}" />

以这种方式进行绑定,可以让WPF识别您的ICommand。但是,CommandParameter是可选的。

1

由于"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>

 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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接