2

I have a setup something like below:

MainWindow ------> MainWindowViewModel
  |--Menu
  |--Frame

Page1 --------> Page1ViewModel
Page2 --------> Page2ViewModel

Suppose my menu has 2 items in it namely Item1 and Item2. Menu's SelectedMenuItem is bound to a property called SelectedItem in MainWindowViewModel. When I click on Item1 I am navigated to Page1 and similarly when I click on Item2 I am navigated to Page2.

Now, In Page1ViewModel and in Page2ViewModel I want to have the current value of SelectedItem. How can I get that value ?

Before asking this question I have looked at Mediator Pattern as well as EventAggregator but for a newbie(in WPF) like me it is very hard to understand those patterns. Is there any other solutions that I can adopt?

Also I have looked at the accepted answer of this question. I have tried the code given there but I don't get value in Page1ViewModel or Page2ViewModel.

Update ---> @Poke

Sorry, I didn't mentioned that in question.

I have another item in my menu called Item3. When I Click on it Page1 should be visible.

So, now my setup is:

MenuItem   |      Page     |    ViewModel
-----------+---------------+-------------
Item1      |     Page1     |  Page1ViewModel
Item2      |     Page2     |  Page2ViewModel
Item3      |     Page1     |  Page1ViewModel

The only difference between Item1 Click and Item3 Click is that :

  1. When Clicking on Item1 :

    A Combobox called cbEffects should be visible.

  2. When Clicking on Item1 :

    A Combobox called cbEffects should be hidden.

Community
  • 1
  • 1
Vishal
  • 6,238
  • 10
  • 82
  • 158
  • 1
    This does not really make much sense. The pages should be independent from the above view (model). Also when the current page is bound to the selected item anyway, then why do you need that information in the page’s view model? If it’s active, it’s always the item that would result in the page being displayed. – poke Mar 22 '14 at 19:14
  • @poke I have updated my question. Can you please have a look at it? – Vishal Mar 22 '14 at 19:28
  • How are you creating the page view models? Can’t you just pass in a `showEffects` flag into the constructor of the Page1VM? – poke Mar 22 '14 at 19:45
  • Yes, I can pass in a flag into the constructor of Page1VM. Where should I get value of ShowEffects? I mean if I follow your approach then also I will be dependent on SelectedMenuItem. – Vishal Mar 22 '14 at 19:50
  • Well, I would assume that you create the page VMs inside of the `MainWindowViewModel`; and that ones does have access to (its own) `SelectedItem`. – poke Mar 22 '14 at 19:51
  • Sorry, I don't understand your above comment. Can you explain it with an example? – Vishal Mar 22 '14 at 19:55
  • Well, someone in `MainWindowViewModel` you create the `Page1ViewModel`, or not? Because where you create it, you can easily pass in the value of `MainWindowViewMode.SelectedItem`. – poke Mar 22 '14 at 19:56
  • My MainWindowViewModel is a Class. Also Page1ViewModel is a Class. Then how can I create a Class inside another class? – Vishal Mar 22 '14 at 19:58
  • Have you considered using a Messenger? In MVVM communication between ViewModels is realized via MessengerServices. The MVVM Light Toolkit provides a nice implementation. – Joel Mar 27 '14 at 13:01

2 Answers2

1

Here's one example of maintaining ViewModel instances in a static property available anywhere in your application via SessionViewModel.GetModuleInstance('VMName'):

namespace MyProject.ViewModels
{
    public sealed class SessionViewModel : ViewModelBase
    {
        private static readonly SessionViewModel instance = new SessionViewModel();
        public static SessionViewModel Instance
        {
            get { return instance; }
        }

        private List<IModule> modulesOpen;
        public List<IModule> ModulesOpen
        {
            get { return modulesOpen; }
            set
            {
                modulesOpen = value;
                NotifyPropertyChanged(() => ModulesOpen);
            }
        }
    }

    public static IModule GetModuleInstance(string moduleName)
    {
        string finalName = "MyProject.ViewModels." + moduleName + "ViewModel";
        IModule moduleToOpen = null;
        if (Instance.ModulesOpen != null)
        {
            moduleToOpen = Instance.ModulesOpen.SingleOrDefault(mod => mod.ModuleName == moduleName);
        }
        else
        {
            Instance.ModulesOpen = new List<IModule>();
        }
        if (moduleToOpen != null) return moduleToOpen;
        Type module = Type.GetType(finalName);
        moduleToOpen = (IModule) Activator.CreateInstance(module);
        Instance.ModulesOpen.Add(moduleToOpen);
        return moduleToOpen;
    }

    public class UsageExample()
    {
        var vm = SessionViewModel.GetModuleInstance("MyVMName");
        ((MyVMName)vm).MyVMPropertyName;
    }
}
Brock Hensley
  • 3,617
  • 2
  • 29
  • 47
  • Sorry for the late reply. I have tried to use the above code. But I get a small problem. My Visual Studio code editor does not know IModule. by looking at it's name I think its an interface and while looking at Microsoft they suggest me that I should include a reference to Microsoft.Practices.Prism.Modularity or Microsoft.Practices.Composite.Modularity. But I cannot find any one of them. Can you help me? – Vishal Mar 27 '14 at 19:03
  • Sorry for the above comment. I got it using Nuget. Now I have got an error : `Microsoft.Practices.Composite.Modularity.IModule does not contain a definition for ModuleName` – Vishal Mar 27 '14 at 19:11
  • The IModule is a custom class I made, not the Prism's. `public interface IModule { string ModuleFriendlyName { get; } string ModuleName { get; }` – Brock Hensley Mar 28 '14 at 14:23
  • I get following error : `{"Unable to cast object of type 'Client.ViewModels.MainWindowViewModel' to type 'Client.Interfaces.IModule'."}` while using below line of code `moduleToOpen = (IModule)Activator.CreateInstance(module);` – Vishal Mar 28 '14 at 18:29
  • @Vishal is `MainWindowViewModel` derived from `IModule`? Should be like `MainWindowViewModel : IModule` – Brock Hensley Mar 29 '14 at 22:16
  • After reading your above comment I have tried to provide implementation of IModule in MainWindowViewModel like below code : ` public string ModuleFriendlyName { get { return "MainWindowViewModel"; } } public string ModuleName { get { return "MainWindowViewModel"; } } ` Is my implementation correct? – Vishal Mar 29 '14 at 22:25
  • But now the problem is that I don't get any errors but the value of any property say of type int always has the value 0. – Vishal Mar 29 '14 at 22:27
  • I just don't get the expected values of each variable declared in MainWindowViewModel. I think every time I get a new instance instead of current Instance of MainWindowViewModel. – Vishal Mar 29 '14 at 22:47
  • ModuleName should not have "ViewModel" in the name, just "MainWindow" – Brock Hensley Mar 29 '14 at 23:14
  • OK I have changed it. as you suggested but still I am getting a new Instance of MainWindowViewModel instead of the Current Instance. – Vishal Mar 29 '14 at 23:27
  • Step through it, is `ModulesOpen` null every time? – Brock Hensley Mar 30 '14 at 00:49
  • I have kept a breakpoint on the line `if(Instance.ModulesOpen != null)`. When I call it first time it's null so program executes else part. After that second time to n times Instance.ModulesOpen is not null, ModulesOpen.Count = 1 from second time to n times. – Vishal Mar 30 '14 at 09:55
  • With the behaviour mentioned in my above comment, I conclude that, when I call MainWindowViewModel's Current instance for the first time, it creates a new instance instead of returning the current instance. After that when I call it for 2nd, 3rd, ....... n times, that newly created instance is returned. – Vishal Mar 30 '14 at 10:04
  • @Vishal if it exists in the `ModulesOpen` list, then it returns that item instead of instantiating a new instance: `if (moduleToOpen != null) return moduleToOpen;` Is `moduleToOpen` null and it continues past this? Is your `moduleName` param matching the value in the `ModulesOpen` list? – Brock Hensley Mar 31 '14 at 12:20
  • I have added the below code to MainWindowViewModel's Constructor and now it works fine. `SessionViewModel.Instance.ModulesOpen = new List(); SessionViewModel.Instance.ModulesOpen.Add((IModule)this);` – Vishal Mar 31 '14 at 12:39
0

Not sure if this is what you are looking for, but you can access the application viewmodel through the App.Current.MainWindow.DataContext property, and store common properties in there.. or, what I do, is store variables accessible to all views in a static class..

JGU
  • 879
  • 12
  • 14