How to call functions in a main view model from ot

2019-01-01 06:24发布

问题:

My program is composed of a TreeView and two contentPresenters at ground level. The mainWindow, TreeView, and each contentPresenter all have their own viewModels.

I would like to call a function in the mainWindowViewModel from the TreeViewViewModel.

I need to do this because the mainWindowViewModel controls what is displayed in the contentPresenters, and I would like to manually update the display.

I\'m guessing I would do something like this...

TreeViewViewModel:

public class TreeViewViewModel
{
     //Do I need to declare the MainWindowVM?

     public TreeViewViewModel() { ... }

     private void function()
     {
          //Command that affects display

          //Manually call function in MainWindowVM to refresh View
     }
}

I have tried to get access to the MainWindowVM from the TreeViewViewModel by using:

public MainWindowViewModel ViewModel { get { return DataContext as MainWindowViewModel; } }

But it doesn\'t make much sense. because the MWVM is not the DataContext of the TreeViewViewModel.

回答1:

The delegate method used in this and the linked answer can be used in any parent-child relationship and in either direction. That includes from a child view model to a parent view model, a Window code behind to the code behind of a child Window, or even pure data relationships without any UI involved. You can find out more about using delegate objects from the Delegates (C# Programming Guide) page on MSDN.

I just answered a similar question to this earlier today. If you take a look at the Passing parameters between viewmodels post, you\'ll see that the answer involves using delegate objects. You can simply replace these delegates (from the answer) with your method(s) and it will work in the same way.

Please let me know if you have any questions.


UPDATE >>>

Yes, sorry I completely forgot you wanted to call methods instead... I\'ve been working on too many posts tonight. So still using the example from the other post, just call your method in the ParameterViewModel_OnParameterChange handler:

public void ParameterViewModel_OnParameterChange(string parameter)
{
    // Call your method here
}

Think of the delegate as being your path back to the parent view model... it\'s like raising an event called ReadyForYouToCallMethodNow. In fact, you don\'t even need to have an input parameter. You could define your delegate like this:

public delegate void ReadyForUpdate();

public ReadyForUpdate OnReadyForUpdate { get; set; }

Then in the parent view model (after attaching the handler like in the other example):

public void ChildViewModel_OnReadyForUpdate()
{
    // Call your method here
    UpdateDisplay();
}

As you have multiple child view models, you could define the delegate in another class that they both have access to. Let me know if you have any more questions.


UPDATE 2 >>>

After reading your last comment again, I\'ve just thought of a much simpler method that might achieve what you want... at least, if I understand you correctly. It is possible for you to Bind directly from your child views to your parent view model. For instance, this would allow you to Bind a Button.Command property in a child view to an ICommand property in your parent view model:

In TreeViewView:

<Button Content=\"Click Me\" Command=\"{Binding DataContext.ParentCommand, 
RelativeSource={RelativeSource AncestorType={x:Type MainWindow}}}\" />

This of course assumes that an instance of the parent view model in question is set as the DataContext of the MainWindow.



回答2:

The easiest way is to pass a method as Action to the constructor of the child view model.

public class ParentViewModel
{
    ChildViewModel childViewModel;

    public ParentViewModel()
    {
        childViewModel = new ChildViewModel(ActionMethod);
    }

    private void ActionMethod()
    {
        Console.WriteLine(\"Parent method executed\");
    }
}

public class ChildViewModel
{
    private readonly Action parentAction;

    public ChildViewModel(Action parentAction)
    {
         this.parentAction = parentAction;

         CallParentAction();
    }

    public void CallParentAction()
    {
        parentAction.Invoke();
    }
}