What's the Best Way to Pass Event to Viewmodel

What's the best way to pass event to ViewModel?

Using MVVM, the general way to handle events is to simply wrap them in Attached Properties, or use Attached Events. Here is an example using the PreviewKeyDown event in an Attached Property:

public static DependencyProperty PreviewKeyDownProperty = DependencyProperty.RegisterAttached("PreviewKeyDown", typeof(KeyEventHandler), typeof(TextBoxProperties), new UIPropertyMetadata(null, OnPreviewKeyDownChanged));

public static KeyEventHandler GetPreviewKeyDown(DependencyObject dependencyObject)
{
return (KeyEventHandler)dependencyObject.GetValue(PreviewKeyDownProperty);
}

public static void SetPreviewKeyDown(DependencyObject dependencyObject, KeyEventHandler value)
{
dependencyObject.SetValue(PreviewKeyDownProperty, value);
}

public static void OnPreviewKeyDownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = dependencyObject as TextBox;
if (e.OldValue == null && e.NewValue != null) textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
else if (e.OldValue != null && e.NewValue == null) textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
}

private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
KeyEventHandler eventHandler = GetPreviewKeyDown(textBox);
if (eventHandler != null) eventHandler(sender, e);
}

Note that it is just as easy (and better too) to use an ICommand instead of the actual KeyEventArgs object which shouldn't really be in the view model. Just create an Attached Property of type ICommand and call that from this TextBox_PreviewKeyDown handler instead:

private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
ICommand command = PreviewKeyDownCommand(textBox);
if (command != null && command.CanExecute(textBox)) command.Execute(textBox);
}

Either way, it would be used something like this:

<TextBox TextBoxProperties.PreviewKeyDown="SomeKeyEventHandler" />

Or if you used the preferred ICommand method:

<TextBox TextBoxProperties.PreviewKeyDownCommand="{Binding SomeCommand}" />

How to pass Events from Model to ViewModel in WPF/C#?

Solution 1:
Pass in the client to the viewmodel's constructor and let the viewmodel subscribe to OnNotification() itself (pass in an interface if available)

Solution 2:
Make also the model implement INotifyPropertyChanged if you're using MVVM; pass in the interface into the viewmodel's constructor and subscribe to PropertyChanged.

If you're not using MVVM, you can use the same methodology by adding a custom ClientNotification event to the model, pass in the entire model into the viewmodels constructor, and subscribe to the event.

Solution 3:
Use a messaging system (aka message bus) such as Prism's Event Aggregator class or MVVM Light's Messenger class, or write your own.

EDIT: Here's an example using MVVM Light: (note: coding from memory, not tested)

Add a using reference to GalaSoft.MvvmLight.Messaging;

Create a small message class containing the properties you need. You can inherit from MVVM Light's MessageBase class if you want but its not necessary.

public class ClientNotificationMessage : MessageBase
{
public string SomeProperty { get; set;}
public int AnotherProperty { get; set;}
}

In you model's event handler, you send a message by:

client.OnNotification += (s, e) =>
{
var msg = new ClientNotificationMessage() { ... };
Messenger.Default.Send<ClientNotificationMessage>(msg);
}

In the viewmodel constructor, register to receive messages by:

Messenger.Default.Register<ClientNotificationMessage>(this, msg => 
{
// handle incoming ClientNotificationMessage
// if (msg.SomeProperty != ) ...
});

I'm sure there are other additional solutions that other ppl can add.

how to pass events through properties of the same viewmodel

I use public getters and private/protected setters for computed properties to do this.

Instead of updating the backing field for computed properties, I update the private setter, which raises PropertyChanged for that property.

This requires that the computed property is stored, instead of computed on the fly.

Here is a snippet from my current project:

    private TimeSpan _duration;
public TimeSpan Duration
{
get { return _duration; }
set
{
if (SetValue(ref _duration, value))
{
StopTime = StartTime + _duration;
FromTo = CalculateFromTo();
}
}
}

private string CalculateFromTo()
{
return $"{StartTime:t} - {StopTime:t}";
}

private string _fromTo;
public string FromTo
{
get => _fromTo;
private set => SetValue(ref _fromTo, value);
}

This is from a class that stores information about events. There are StartTime, StopTime, and Duration properties, with a computed string showing a friendly display value for them named FromTo.

SetValue is a method on a base class that sets the backing field and automatically raises PropertyChanged only if the value actually changed. It returns true only if the value changed.

Changing Duration will cascade to StopTime and FromTo.

How to raise an event in a ViewModel from within a subview's ViewModel

Textbook example for using the EventAggregator, though I should add that child view models are also a textbook example for going view model-first. The ViewModelLocator is most useful for independent top-level views...

That being said, in sub view model:

_eventAggregator.GetEvent<MySubViewEvent>().Publish();

Somewhere else:

public MainViewModel( IEventAggregator eventAggregator )
{
eventAggregator.GetEvent<MySubViewEvent>().Subscribe( OnSubViewEvent );
}

Correct way to pass events back from a view in Android

It gets into it further down in the section on unidirectional data flow - or there's a separate article on events (you're looking at the overview page)

This is the example they use:

class LatestNewsActivity : AppCompatActivity() {

private lateinit var binding: ActivityLatestNewsBinding
private val viewModel: LatestNewsViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
/* ... */

// The expand section event is processed by the UI that
// modifies a View's internal state.
binding.expandButton.setOnClickListener {
binding.expandedSection.visibility = View.VISIBLE
}

// The refresh event is processed by the ViewModel that is in charge
// of the business logic.
binding.refreshButton.setOnClickListener {
viewModel.refreshNews()
}
}
}

They have two kinds of events there - one that's purely about the UI (whether part of it is expanded or not), and one that actually relates to the underlying data in some way.

The ViewModel doesn't need to care about the UI state, so that's handled directly in the UI. But when it comes to refreshing the data, they call a handler function on the ViewModel itself. And because of the observer pattern, if that event causes some change in the data in the VM, the observer will see it and update the UI in response.

So you don't actually update the UI directly, in response to the event! You update the VM, and the UI will update itself because of how everything is wired up.

Passing Event Information from Custom View to ViewModel

Sorry for sharing this in JAVA... But I guess you can capture the ideia.

On CustomView, create a custom interface:

public class PaintView {

private OnCoordinateUpdate mCoordinatesListener;

override fun onTouchEvent(event: MotionEvent): Boolean {
val pointX = event.x
val pointY = event.y
....
if(listener != null) {
listener.onUpdate(pointX, pointY);
}
}
...

public void setCoordinatesListener(OnCoordinateUpdate listener) {
mCoordinatesListener = listener;
}

public interface OnCoordinateUpdate {
void onUpdate(int x, int y);
}
}

Then, on your activity:

public class MainActivity {
...
mPaintView.setCoordinatesListener(new PaintView.OnCoordinateUpdate {
@Override
void onUpdate(int x, int y) {
if(mTextView != null) {
mTextView.setText("X: " + x + " Y: " + y);
}
}
});
}

This way, your PaintView can invoke the onUpdate whenever you want. Then, in your activity, everytime the onUpdate is called, the TextView will have its content updated.

I guess this is a good a approach because you create a custom interface (OnCoordinateUpdate) and it only makes sense to your custom view only.

Bind event to ViewModel

InvokeCommandAction requires the ICommand to be bound not an event handler as you've bound (EmployeeGrid_MouseLeftButtonUp).

So you can introduce a command in ViewModel and bind to it:

View Model:

public ICommand SomeActionCommand { get; set; }

XAML:

<i:InvokeCommandAction Command="{Binding SomeActionCommand}" />

How raise an event in the viewmodel of the actual landing page, when it is triggered from a menu item in the view of the main page?

For your requirement, you could use NavigationView as the navigation to process the page navigate, bind the loaded event with Command then execute SaveEntry in View Model when the page was loaded.

<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:InvokeCommandAction Command="{x:Bind ViewModel.ViewLoadedCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>

ViewModel

public class MainViewModel : ViewModelBase
{
public RelayCommand ViewLoadedCommand { get; private set; }
public MainViewModel()
{
ViewLoadedCommand = new RelayCommand(SaveEntry);
}

private void SaveEntry()
{
// save entry.
}
}

I have uploaded the code sample please refer.



Related Topics



Leave a reply



Submit