38

I have a method within the code behind of my View (this method does something to my UI).

Anyway, I'd like to trigger this method from my ViewModel. How could this be done?

0

7 Answers 7

42

My (and maybe others?) difficulty with MVVM was to understand a simple thing: View knows about ViewModel. I was using bindings and commands, but they are simple strings in xaml. Because of safe resolving at run-time (safe means you can do a typo, but software will not crash) this makes view decoupled from view-model (at compile time at least). And I was always looking for solution to keep this decoupling, to example, behaviors.

Truth is, you can get access directly to view model, which is typically a DataContext of window/user control:

var vm = (MyViewModel)this.DataContext;

Knowing that, using events probably the best way to call view method from view model, because view model don't know if there is subscriber, it just firing that event and event can be used by view or another view model.

// define in the view model
public delegate void MyEventAction(string someParameter, ...);
public event MyEventAction MyEvent;

// rise event when you need to
MyEvent?.Invoke("123", ...);

// in the view
var vm = (MyViewModel)DataContext;
vm.MyEvent += (someParameter, ...) => ... // do something
7
  • It does not seem like the syntax is correct in your example. I am having to change it a lot in order to make it work.
    – Deantwo
    Commented Aug 24, 2017 at 12:42
  • 1
    @Deantwo, 3 years ago I didn't use EventHandler<T> much, nor inherit from EventArgs for parameters... Fixing obvious typos for the moment, thanks for reporting.
    – Sinatr
    Commented Aug 24, 2017 at 12:54
  • 4
    That was excellent answer. I had to use: var vm = (MyViewModel)BindingContext; for xamarin forms instead of DataContext. Many thanks.
    – Ali123
    Commented Jun 22, 2018 at 13:04
  • It works absolutely fine, but is it ok in terms of MVVM pattern ? Dont we have to manage all these stuff via bindings? Commented May 27, 2023 at 7:50
  • Use WeakReferenceMessenger in .net maui Commented Jan 12, 2024 at 13:19
10

You can do it like this in View (code behind).

It casts to an interface to be implemented by the ViewModel, so that you are not constrained to one specific ViewModel type.

    // CONSTRUCTOR
    public SomeView()
    {
        InitializeComponent();

        DataContextChanged += DataContextChangedHandler;
    }

    void DataContextChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
    {
        var viewModel = e.NewValue as IInterfaceToBeImplementedByViewModel;

        if (viewModel != null)
        {
            viewModel.SomeEvent += (sender, args) => { someMethod(); }
        }
    }
1
  • works perfectly. I like the use of DataContextChanged because it works when viewModel is set later and not during View creation.
    – Welcor
    Commented Jul 26, 2020 at 11:57
5

According to MVVM pattern ViewModel is not aware of View, so this is not acceptable. To interact with ViewModel View could trigger a command, also you can use bindings. Moreover, you should not move UI-specific things like BusyIndicator to ViewModel level.

Please provide more details regardign your concrete use case - when you want to call a View's method and what this method does.

2
  • My UI has a "Busy" indicator that can only be initiated via the codebehind because it's derived from a user control that has this functionality. So I need the viewmodel to set this busy indicator since it's the one dealing with getting the data from the server
    – Shai UI
    Commented Dec 28, 2011 at 16:31
  • Can you try declaring BusyInduicator in XAML and setup Bindings to ViewModel properties? How exactly ViewModel should setup this indicator? can you show code of BudyIndicator initialization by a ViewModel? Moreover, you shouldnot move UI-specific things like BusyIndicator to ViewModel level
    – sll
    Commented Dec 28, 2011 at 16:34
4

Let's say you have a method within the code behind of my Login View, that updates UI by bringing Focus to the PasswordEntry if login fails, then the easiest & most universal way to trigger this method from your ViewModel is using Action delegates.

As you can see in this sample, all you need to add, where your services determine that the login has failed and you want the Password Entry to get the focus, is two lines of code in your ViewModel and an action handler in your View.

ViewModel code:

  • Declaration of the event: public Action<bool> OnLoginFailed { get; set; } &
  • Then simply, when needed, executing this OnLoginFailed?.Invoke(true);

View code:

ViewModel.OnLoginFailed = ((obj) =>
{
    PasswordEntry.Focus();
});

Update: I wrote an article to explain this in a lot more detail

0

I saw youre reply to the answer above, you are saying that you want your ViewModel to retrieve data and then tell your view to stop the busy indicator.

I'm not sure if my solution would be the best solution, but you can give it a try, and maybe someone can correct if I'm wrong.

So from your view, you would call a method from ViewModel to start reading the dataset, am I right? In this method, you can pass a delegate (pointing to a method that exists in your view) and when your ViewModel finishes reading the dataset from the server, trigger the delegate (from your viewmodel) that is linked to your method in your view that can stop the busy indicator.

so in your view you have

void StopBusyIndicator()
{
    this.BusyIndicator.IsBusy = false;
}

and when you call your ViewModel to read dataset,

call it like this:

ViewModel.ReadDataSet( ()= >StopBusyIndicator)

which will pass the StopBusyIndicator method as a delegate, which you can call at the end of your ReadDataSet.

HTH

0

You could write an action class that accepts a Data Transfer object. Within the DTO, add a property called "View" and assign it the current view. Call the action via the controller from within your view's codebehind, unbox the DTO and now you have full control of the view within the action class.

If you truely want to do this in your model, just create the method with a "View" type parameter in your Model and execute it, passing in the current view.

0

As I was working with Animations in the code behind, I was looking for a way to trigger the event to happen from the view model.

Here is a Simplified working example that may help clarify as I was not initially sure, other's may not be either...

    ////////////////   (TestView.xaml.cs)  ////////////////
    public partial class TestView
    {
        public TestView()
        {
            DataContext = new TestViewModel();
            InitializeComponent();

            // Subscribe to an event to Fire from ViewModel...
            var vm = (TestViewModel)DataContext;
            if (vm == null) return;  // in case the cast fails
            vm.DoSlideEvent += (left, top) => DoSlideAnimation(left, top);
        }

        public void DoSlideAnimation(double leftVal, double topValue)
        { 
            // Code to perform animation...
        }
    }

    ////////////////   (TestViewModel.cs)  ////////////////
    public delegate void DelegateEventAction(double leftValue, double topValue);
    public event DelegateEventAction DoSlideEvent;
    
    // Raise event to run the slide...
    DoSlideEvent?.Invoke(422.8, 23);  

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.