0

I have application that works with Model View ViewModel. In my model I have a List based on my Client class.

public class Client
{
    public string Name { get; set; }

    public string Ip { get; set; }

    public string Mac { get; set; }
}

In my ClientRepository I make a List from XML file with my Client class.

    public ClientRepository()
    {
        var xml = "Clients.xml";
        if (File.Exists(xml))
        {
            _clients = new List<Client>();
            XDocument document = XDocument.Load(xml);
            foreach (XElement client in document.Root.Nodes())
            {
                string Name = client.Attribute("Name").Value;
                string Ip = client.Element("IP").Value;
                string Mac = client.Element("MAC").Value;
                _clients.Add(new Client() { Mac = Mac, Name = Name, Ip = Ip });
            }
        }
    }

In my UI/UX I have 3 Textboxes 1 for MAC, 1 IP and 1 Name I also have a Button thats has a binding to AddClientCommand.

<Label Grid.Row="0" Grid.Column="0" Content="Host Name:"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="tbHostName" Height="20" Text="{Binding Path=newClient.Name, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="1" Grid.Column="0" Content="IP Address:"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="tbIP" Height="20" Text="{Binding Path=newClient.Ip, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="2" Grid.Column="0" Content="MAC Address"/>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="tbMAC" Height="20" Text="{Binding Path=newClient.Mac, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Row="3" Grid.Column="0" Content="Remove" x:Name="bRemove" Margin="3 0 3 0" Click="bRemove_Click"/>
<Button Grid.Row="3" Grid.Column="1" Content="Add" x:Name="bAdd" Margin="3 0 3 0" Click="bAdd_Click" Command="{Binding AddClientCommand}"/>

To come to my point: What I want to know is what is the best way to implement the AddClientCommand?

What I currently have and I know it doesn't work:

    public ClientViewModel()
    {
        _repository = new ClientRepository();
        _clients = _repository.GetClients();

        WireCommands();
    }

    private void WireCommands()
    {
        AddClientCommand = new RelayCommand(AddClient);
    }

    public Client newClient
    {
        get
        {
            return _newClient;
        }
        set
        {
            _newClient = value;
            OnPropertyChanged("newClient");
            AddClientCommand.isEnabled = true;
        }
    }

    public void AddClient()
    {
        _repository.AddClient(newClient);
    }

RelayCommand class:

public class RelayCommand : ICommand
{
    private readonly Action _handler;
    private bool _isEnabled;

    public RelayCommand(Action handler)
    {
        _handler = handler;
    }

    public bool isEnabled
    {
        get { return true; }
        set
        {
            if (value != isEnabled)
            {
                _isEnabled = value;
                if (CanExecuteChanged != null)
                {
                    CanExecuteChanged(this, EventArgs.Empty);
                }
            }
        }
    }

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

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _handler();
    }
}
Sheridan
  • 68,826
  • 24
  • 143
  • 183
Koen H
  • 111
  • 2
  • 8

2 Answers2

1

Have you just tried putting your command into a property something like this?:

public ICommand AddClientCommand
{
    get { return new RelayCommand(AddClient, CanAddClient); }
}

public bool CanAddClient()
{
    return newClient != null;
}

Put whatever logic you want to inside the CanAddClient to enable or disable the ICommand.

Ahhhh... I see... you have the wrong implementation of the RelayCommand. You need one that uses the CanExecuteChanged event handler... you can find the correct implementation in the RelayCommand.cs page on GitHub.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
1

I recommend you to use DelegateCommands, you will find this class in many MVVM frameworks:

public ICommand AddClientCommand 
{
    get
    {
        return new DelegateCommand(AddClient, CanExecuteAddClient);
    }
}

I also see that _clients is of type List<Client>. If you are binding this to the UI to see the clients list, changes will not be notified unless you use ObservableCollection<Client>

Edit: As someone pointed out in comments, you should create the _newClient. Be aware of creating a new one for each client added, or you will end up adding the same instance of Client over and over!

Natxo
  • 2,917
  • 1
  • 24
  • 36
  • Thanks for your help I got the adding of items working, I have one question about getting the selected items from a DataGrid with MVVM can you help me with this? – Koen H Jan 16 '14 at 11:38
  • 1
    @KoenHendriks, glad to help man! There will be for sure similar questions to that in S.O., so check it before posting. I or anyone else will help you in case you need it, no worries :) – Natxo Jan 16 '14 at 11:52
  • @KoenHendriks WPF + MVVM is a powerful combination, but the learning curve is steep. Check [this](http://stackoverflow.com/questions/1405739/mvvm-tutorial-from-start-to-finish). – Natxo Jan 16 '14 at 12:07