简体   繁体   中英

mvvm confusion with canexecute and binding commands

I'm having a seriously hard time wrapping my head around the logic in the tutorials and posts about this subject. I'm trying to implement it in a wpf application I'm writing.

Basically, I'm using a listbox to display a ToString of objects in a list and allowing users to add and remove from that list and its corresponding listbox via an add and remove button. The problem I'm having is with the implementation of the Remove button. I want the button disabled if no listbox item is selected, which is one of the things that this pattern is good for. I'm lost as to how to implement that condition.

At the moment, the button is not enabling when I highlight a listbox item. I suppose the CanExecuteChanged event isn't firing.. How do I need to change this?

my CommandsHandler class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace TechopsTools
{

    public class CommandHandler : ICommand
    {
        private Action<object> _execute;
        private bool _canExecute;

        public CommandHandler(Action<object> execute)
            : this(execute, true)
        {
        }

        public CommandHandler(Action<object> execute, bool canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }


        public bool CanExecute(object parameter)
        {
            return _canExecute;
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }

}

my viewmodel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace TechopsTools
{
    class LogCheckClientViewModel : INotifyPropertyChanged
    {
        private string uri;
        private string response;
        private bool _canRemove;
        private LogConstraints selectedConstraints;

        private ObservableCollection<LogConstraints> constraints;

        public event PropertyChangedEventHandler PropertyChanged;

        public LogConstraints SelectedConstraints
        {
            get
            {
                return selectedConstraints;
            }
            set
            {
                selectedConstraints = value;
                OnPropertyChanged("SelectedConstraints");
            }
        }

        private CommandHandler removeItemCommand;
        public ICommand RemoveItemCommand
        {
            get
            {
                if (removeItemCommand == null)
                    removeItemCommand = new CommandHandler(param => RemoveConstraint(), SelectedConstraints != null);
                return removeItemCommand;
            }
        }

        public string Response 
        {
            get
            {
                return response;
            }
            set
            {
                response = value;
                OnPropertyChanged("Response");
            }
        }

        public string Uri
        {
            get
            {
                return uri;
            }
            set
            {
                uri = value;
                OnPropertyChanged("Uri");
            }
        }

        public ObservableCollection<LogConstraints> Constraints
        {
            get
            {
                return constraints;
            }
            set
            {
                constraints = value;
                OnPropertyChanged("Constraints");
            }
        }

        public LogCheckClientViewModel()
        {
            constraints = new ObservableCollection<LogConstraints>();
        }

        public void AddConstraint()
        {
            NewConstraint newConstraint = new NewConstraint();
            newConstraint.ShowDialog();
            if (newConstraint._vm.constraint != null)
            {
                constraints.Add(newConstraint._vm.constraint);
            }
        }

        private void RemoveConstraint()
        {
            Constraints.Remove(SelectedConstraints);
            OnPropertyChanged("Constraints");
        }

xaml:

<Window x:Class="TechopsTools.LogCheckClient"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TechopsTools"
        Title="LogCheck" Height="453.057" Width="495.986">
    <Grid>
        <TextBox Text="{Binding Response}" HorizontalAlignment="Left" Height="128" Margin="38,212,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="413" VerticalScrollBarVisibility="Auto" IsEnabled="False"/>
        <Label Content="Response" HorizontalAlignment="Left" Margin="38,188,0,0" VerticalAlignment="Top" Width="78" Height="24"/>
        <TextBox Text="{Binding Uri}" HorizontalAlignment="Left" Height="23" Margin="38,26,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="413"/>
        <Label Content="Uri" HorizontalAlignment="Left" Margin="38,0,0,0" VerticalAlignment="Top" Width="78" Height="24"/>
        <Button Content="Add Constraint" HorizontalAlignment="Left" Margin="38,54,0,0" VerticalAlignment="Top" Width="127" Height="56" Click="Add_Click"/>
        <Button x:Name="Submit" Content="Submit Request" HorizontalAlignment="Left" Margin="38,345,0,0" VerticalAlignment="Top" Width="413" Height="70" Click="Submit_Click"/>
        <ListBox SelectedItem="{Binding Path=SelectedConstraints,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Constraints}" HorizontalAlignment="Left" Height="124" Margin="182,54,0,0" VerticalAlignment="Top" Width="269">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=description}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Command="{Binding RemoveItemCommand}" Content="Remove Constraint" HorizontalAlignment="Left" Margin="38,122,0,0" VerticalAlignment="Top" Width="127" Height="56" />
    </Grid>
</Window>

You really need to be using a CanExecute delegate the same way you're doing an Execute handler.

Basically right now you are checking if it can execute when RemoveItemCommand is first accessed. But then it just keeps that value the entire time.

If you pass in a delegate with that same condition (perhaps adding in for an empty list, not just a null list), I'm betting it'll work.

In other words, in your CommandHandler, change

private bool _canExecute;

to

private Func<bool,object> _canExecute;

and change

public bool CanExecute(object parameter)
{
    return _canExecute;
}

to

public bool CanExecute(object parameter)
{
    return _canExecute(parameter);
}

and then in your ViewModel, change

removeItemCommand = new CommandHandler(param => RemoveConstraint(), 
                                       SelectedConstraints != null);

to

removeItemcommand = new CommandHandler(param => RemoveConstraint(), 
                                       param => SelectedConstraints != null);

(note that this might not be exactly the right code, as I am just writing it freehand, but hopefully you get the point)

I think that thing can be done inside your XAML file, just using DataTrigger.

If this solution would satisfied you, please let me know writing comment, and I will provide you some code.

Best regards

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM