【问题标题】:WPF binding error with ICommandICommand 的 WPF 绑定错误
【发布时间】:2018-09-05 04:10:09
【问题描述】:

我有一个简单的 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”属性。 “绑定”只能 在 DependencyObject 的 DependencyProperty 上设置。

如何从 ListBox 控件的 Selected 事件在我的视图模型中调用 ICommand?

【问题讨论】:

标签: c# wpf xaml data-binding


【解决方案1】:

在 ListBox 上选择是一个事件。您拥有 SelectedItem,您可以将其绑定到与视图模型上列表元素相同类型的属性:

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

.

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

至于您的命令,您需要一个处理 CommandSource 的控件,例如 Button:

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

像这样绑定它,将使 WPF 能够识别您的 ICommand。不过,CommandParameter 是可选的。

【讨论】:

    【解决方案2】:

    这是因为“选定”是事件,实际上您不能将命令直接绑定到任何事件。但是有几种可能的方法可以解决这个问题。 在下面的示例中,我使用自定义命令实现来绑定到 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
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-02
      • 2015-11-13
      • 2013-08-05
      • 2012-06-11
      • 1970-01-01
      • 2021-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多