简体   繁体   中英

Can we raise an event from ViewModel to View?

For example, my ViewModel do something. After that, I wanted to notify my View in order to do something that should only be done in View .

Something like;

public class MyViewModel : ViewModelBase{
  ...
  private void DoSomething(){
     //raise the event here and notify the View
  }
}

then in View;

public MyView(){
  InitializeComponent();
  ...
}

private void ViewModelSaidDoSomething(){
 //The View Model raises an event, do something here about it...
}

is that possible without breaking the MVVM concept?

Yes, it's possible. Usually, what you can do is define the event from your ViewModel , then let your View subscribe from that event.

For example, in your ViewModel.

public class MyViewModel : ViewModelBase{
  ...
  //Define your event.
  public delegate void YourEventAction(string your_argument); 
  public event YourEventAction? YourEvent;
  
  private void DoSomething(){
     YourEvent?.Invoke(your_argument); //raise the event here, any subscriber will received this.
  }
}

Then you can subscribe for that event in your View.

public MyView(){
  InitializeComponent();
  ...
  DataContextChanged += ViewModelSaidDoSomething; //subscribe to DataContextChanged.
}

private void ViewModelSaidDoSomething(){ 
    var viewModel = (MyViewModel)DataContext; //Get your ViewModel from your DataContext.
    viewModel.YourEvent += (your_argument) =>{  //Subscribe to the event from your ViewModel.
        //The View Model raises an event, do something here about it...
    };
}

Hope that helps.

My primary candidate for any viewmodel <-> view communication would be binding. This is a weak event pattern, late binding and the view doesn't need to be aware of what type the viewmodel is. They went to a load of trouble to implement binding and it works well IMO. Anyhow, binding means view and viewmodel are as loosely coupled as you can get whilst retaining reasonable practicality.

Here's an example. The purpose is to allow a viewmodel to close a window.

The view functionality is encapsulated in a control. Only code.

public class CloseMe : Control
{
    public bool? Yes
    {
        get
        {
            return (bool?)GetValue(YesProperty);
        }
        set
        {
            SetValue(YesProperty, value);
        }
    }
    public static readonly DependencyProperty YesProperty =
        DependencyProperty.Register("Yes",
                    typeof(bool?),
                    typeof(CloseMe),
                    new PropertyMetadata(null, new PropertyChangedCallback(YesChanged)));
    private static void YesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool?)e.NewValue != true)
        {
            return;
        }
        CloseMe me = (CloseMe)d;
        Window parent = Window.GetWindow(me) as Window;
        parent.Close();
    }
}

Anywhere in a window or usercontrol goes in the window you want to close:

   <local:CloseMe Yes="{Binding CloseYes, Mode=TwoWay}"/>

That binds to a bool property CloseYes in the viewmodel. Set that true and the window closes.

We can imagine encapsulating some data in a more complicated viewmodel object instead of just a boolean. That would not be quite so elegant though. A view isn't supposed to process data, so I'd question what you're doing if you're sending complex data.

Setting those doubts aside though.

If you wanted to transfer a complex type from A to B with loose coupling then the pub sub pattern is a good candidate.
The mvvm toolkit is my framework of preference and there's an implementation in there which you can use. Messenger: https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/messenger

The message sent is an object you define and you can use WeakReferenceMessenger. Which does what it implies and relies on a weak reference. Thus reducing the risk of memory leaks. Other frameworks have similar mechanisms so you could take a look at what's in the one you prefer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM