1

Not sure if I am just doing this wrong or am misunderstanding some of the examples already on stack overflow here and here.

I am trying to take a selected item from my first view model and pass it to another view model I am navigating to. The purpose of this is so I can display the item that has been passed and allow the user to work with it.

Passing from first view model

This is just a small snippet of the first view model. Here I am first navigating to the new page/view model. Then pass the SelectedRule object using a messenger. Navigation is done using a ViewModelLocator class / navigation service provided with MVVM Light.

private ApprovedBomRule _selectedRule = new ApprovedBomRule();

public ApprovedBomRule SelectedRule
{
    get { return _selectedRule;}
    set { Set(ref _selectedRule, value); }
}

private void NavigateToUpdateRule()
{
    //Navigate to Update Rule page
    _navigationService.NavigateTo("UpdateBomRuleView");
    //Pass selected rule as a parameter using messenger service
    ApprovedBomRule ruleToSend = SelectedRule; // Selected by user.
    Messenger.Default.Send(ruleToSend);
}

On receiving view model

Here is my second view model where I register the same type of SelectedRule from above and set it to the public variable.

public class UpdateBomRuleViewModel : ViewModelBase
{
    private ApprovedBomRule _passedRule;

    public ApprovedBomRule PassedRule
    {
        get => _passedRule;
        set => Set(ref _passedRule, value);
    }
    //Constructor
    public UpdateBomRuleViewModel()
    {
        //Register message type
        Messenger.Default.Register<ApprovedBomRule>(this, GetMessage);
    }
    //Set the property to passed object
    public void GetMessage(ApprovedBomRule rule)
    {
        PassedRule = rule;
    }
}

My constructor is reached and the register method is set, but the GetMessage() function is never called. What am I missing here?

EDIT

I narrowed down the problem to the fact that the register method is being called after the message is sent. Now the second problem I am running into is how do I have my second view model register before the send? I am using a viewmodel locator in my pages to determine the view models for each page. Even though I am doing the _navigation.NavigateTo() before sending the data, the view model is not initialized until after the send.

Example of viewmodel locator in page

<local:BasePage x:Class="YAI.BomConfigurator.Desktop.Views.Rules.UpdateBomRuleView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:local="clr-namespace:YAI.BomConfigurator.Desktop"
  mc:Ignorable="d" 
  d:DesignHeight="450" d:DesignWidth="800"
  Title="UpdateBomRuleView"
  DataContext="{Binding UpdateBomRuleViewModel, Source={StaticResource Locator}}">

<Grid>
    <TextBlock Text="{Binding PassedRule.Description}" 
               VerticalAlignment="Center"
               HorizontalAlignment="Center">

    </TextBlock>
</Grid>

Selthien
  • 1,178
  • 9
  • 33
  • I lack information to draw a connection from `SelectedRule` to `ApprovedBomRule`. What precisely is _SelectedRule_; what is the debugger telling you about what kind of object _SelectedRule_ is when `Messenger.Default.Send(SelectedRule);` is being called? –  Dec 10 '18 at 21:49
  • SelectedRule is of type ApprovedBomRule. I will clear up the example to make it more clear. One moment. @elgonzo – Selthien Dec 10 '18 at 21:50
  • Hmm, no idea right now. Perhaps, unless someone else can come up with a helpful answer, get the MVVMLight source code (https://github.com/lbugnion/mvvmlight) and use it instead of the precompiled MVVMLight library. This will allow you to debug _into_ the MVVMLight messenger code to see what is going on there... –  Dec 10 '18 at 22:00
  • @elgonzo would you mind up voting the question then see if it can get some more interest. Thank you. – Selthien Dec 10 '18 at 22:02
  • No problemo...! –  Dec 10 '18 at 22:02

2 Answers2

0

Okay so I sort of found a solution to the problem. I used my ServiceLocator to get the instance before navigating.

 var vm = ServiceLocator.Current.GetInstance<UpdateBomRuleViewModel>();
 //Navigate to Update Rule page
 _navigationService.NavigateTo("UpdateBomRuleView");
 //Pass selected rule as a parameter using messenger service
 ApprovedBomRule ruleToSend = SelectedRule; // Selected by user.
 Messenger.Default.Send(ruleToSend);

This caused my register to be called before the send. I don't necessarily like this solution because the var vm is not being used for anything, but it works for now.

thank you for looking at the question.

Selthien
  • 1,178
  • 9
  • 33
  • Just an FYI: The (an) UpdateBomRuleViewModel instance has of course to exist to receive/handle the message. How could the UpdateBomRuleViewModel.GetMessage (an instance method) be ever called in any way if no UpdateBomRuleViewModel instance exist? You say that you don't like your solution, which sort of makes me think that you perhaps have a flaw in your application design/logic with regard to UpdateBomRuleViewModel. You might perhaps want to think about what led you to attempt sending a message to a UpdateBomRuleViewModel instance that doesn't exist (yet/anymore?). –  Dec 11 '18 at 13:54
  • @elgonzo yes I know what you mean. I believe it is the fact I did view first navigation instead of view model first navigation. The problem is the instance isn't initialized until after the navigation has completed and the new page has shown up. It makes sense to me now but I am still learning of course! Thank you for your input though I appreciate all the suggestions I can get. – Selthien Dec 11 '18 at 14:27
  • I can't give that good of an advice, because my view on your project is too narrow, basically limited to what can be seen here in the question/answer/comments. But it looks to me that you have some state shared across multiple page view models (such as `SelectedRule`). (1/2) –  Dec 11 '18 at 15:00
  • (2/2) As i said, i don't know enough of your project to have an idea about an approach that is good for you, but you might try managing the state (like _SelectedRule_ and possibly others) in some top-level ("master") view model or "navigation model" and use the mediator pattern approach to interact between this state-managing model and your view models. (If your project is small and narrow in scope, you could perhaps simplify things by just injecting and using the state-managing model in your view models in some fashion) –  Dec 11 '18 at 15:01
0

You need to wait for the page show up before sending the message. It is weird that MVVMLight doesn't offer any NavigateAsync method like Prism, so you have to roll for your own.

await Application.Current.Dispatcher.Invoke( 
    () => _navigationService.NavigateTo("UpdateBomRuleView");

ApprovedBomRule ruleToSend = SelectedRule; // Selected by user.
Messenger.Default.Send(ruleToSend);

Slightly modified from my UWP code, but it should be fine for WPF.

khoaharp
  • 11
  • 1