Search code examples
wpfmvvmmvvm-light

MVVM Light messenger message not received from Command


I am building a WPF application using the MVVM Light framework.

I am trying to have a Window open and pass some information, i.e. an object, to this newly created ViewModel.

I have a WPF window (MainListView/MainListViewModel) which has a ListBox bound to a list of objects:

<ListBox ItemsSource="{Binding Selected_Agent.Associated_Letter, Mode=OneWay}"
         SelectedItem="{Binding Selected_Letter, Mode=TwoWay}">
<!-- More code below -->

In MainListView I also have a button bound to a command:

<Button Content="Update" Command="{Binding UpdateCommand}"/>
private ICommand _updateCommand;
public ICommand UpdateCommand
{
    get
    {
        if (_updateCommand == null)
        {
            _updateCommand = new RelayCommand(_windowService.OpenWindow<AddNewLetterView>, UpdateCommand_CanExecute());
            Messenger.Default.Send(new UpdateLetterMessage() { Letter = Selected_Letter });
        }
        return _updateCommand;
    }
}

This opens a new window, AddNewLetterView using the AddViewLetterViewModel, which is linked by MVVM Light's ViewModelLocator.

In AddNewLetterViewModel constructor I do this:

public AddNewLetterViewModel()
{
    Messenger.Default.Register<UpdateLetterMessage>(this, UpdateLetterMessageReceived);
}

The issue is that this message doesn't get received. The breakpoint on UpdateLetterMessageReceived is never hit, even though the view opens.

If I put a breakpoint on my command it is never hit, and neither is UpdateLetterMessageReceived on the AddNewLetterViewMdoel, when the button is clicked, even though the AddNewLetterView is opened.

What seems to be happening is that UpdateCommand is called once during MainListViewModel instantiation meaning that the Messenge is sent before AddNewLetterViewModel is instantiated.

Is that correct? If so, how can I make sure that my message is sent when the Command is called?

If not, what is going on here and how can I achieve this?


Solution

  • Your update command needs to be like this:

        private ICommand _updateCommand;
    
        public ICommand UpdateCommand
        {
            get
            {
                if (_updateCommand == null)
                {
                    _updateCommand = new RelayCommand(() =>
                    {
                        _windowService.OpenWindow<AddNewLetterView> ();
                        Messenger.Default.Send(new UpdateLetterMessage() {Letter = Selected_Letter});
                    }, UpdateCommand_CanExecute);
                }
                return _updateCommand;
            }
        }
    

    The way you had it, it was only sending the message when the command was first created, where I believe you want to send it when the command is executed

    By the way I find this structure for commands more helpful. Makes it look like a normal method

    public ICommand UpdateCommand => _updateCommand ?? (_updateCommand = new RelayCommand(() =>
    {
        _windowService.OpenWindow<AddNewLetterView> 
        Messenger.Default.Send(new UpdateLetterMessage() {Letter = Selected_Letter});
    }, UpdateCommand_CanExecute));
    

    To make this clearer, your function was doing this:

        private ICommand _updateCommand;
    
        public ICommand UpdateCommand
        {
            get
            {
                if (_updateCommand == null)
                {
                    _updateCommand = new RelayCommand(DoSomething, CanDoSomething);
                    Messenger.Default.Send(etc); <=====THIS SHOULD BE....
                }
                return _updateCommand;
            }
        }
    
    public void DoSomething()
    {
        _windowService etc
        <=====.....HERE
    }
    
    public bool CanDoSomething() {}
    

    The () => {} notation I've used is a shorthand for defining the function DoSomething