mvvm confusion with canexecute and binding command

2019-07-26 08:16发布

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>

标签: c# wpf mvvm
2条回答
乱世女痞
2楼-- · 2019-07-26 08:35

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

查看更多
Lonely孤独者°
3楼-- · 2019-07-26 08:37

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)

查看更多
登录 后发表回答