How to Update View from ViewModel using MVVMCross and Xamarin.Android - c#

Am new to MVVMCross with xamarin.android so little struck up with a scenario. I have a fab and mvx.recyclerview inside a fragment. so when i click on this fab it will make the recyclerview scroll by a row.
i.e
void onclick(sender object ,eventargs e)
{
mrecyclerview.SmoothScrollToPosition(somevariable++); // do something.
}
this is breaking mvvm pattern, so is there any way or method in the MVVM Cross which i can use to listen back to the View from ViewModel.
fab.click binding with ICommand => viewmodel => view=> updatescroll().
thanks in advance.

Well, since the ViewModel should not know about the View, you should not call any method of it.
I would propose an event in your ViewModel where your View can subscribe. So you call your event something like FabClickDone and your view does what ever it wants, when this event occured. In your case scrolling.
Here is an code example for your ViewModel:
public delegate void FabClickDoneEvent(object sender, EventArgs args);
public event FabClickDoneEvent FabClickDone;
protected virtual void OnFabClickDone()
{
FabClickDone?.Invoke(this, EventArgs.Empty);
}
You then just call it by
void onclick(sender object , eventargs e)
{
// Do something inside your viewmodel
// ...
OnFabClickDone();
}
In your View constructor subscribe to this event:
ViewModel.FabClickDone += ViewModel_FabClickDone;
And create a method where you want to do your scrolling
void ViewModel_FabClickDone(Object sender, EventArgs e)
{
mrecyclerview.SmoothScrollToPosition(somevariable++); // do something.
}
Since you're using MVVMcross I would suggest you using a command, where you call OnFabClickDone();

Related

Calling a method present in Viewmodel from OnNavigate of Page using MVVM

I have to do some operations in OnNavigation of page. But I want to do all those operations in ViewModel.cs. For that I just want to call that method in ViewModel.cs from OnNavigation of Page.
I am not getting how to do that. I am creating Viewmodel.cs object in particular View,
<Page.DataContext>
<obj:ViewModel/>
</Page.DataContext>
How to do this using Events and Delegates? and if there is some other way, then what is it.
In the code behind you can get the reference of the viewmodel:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var vm = this.DataContext as ViewModel;
vm.NameOfYourMethod();
base.OnNavigatedTo(e);
}

Removing event handlers in c#

I'm working on a Windows Forms app and I've come to a point where I can't understand what's happening.
I have something similar to an MVC architecture. Sometimes I want controls that belong to the view to stop listening to events. So inside the view code I've written a method that looks like this:
public void enableEventHandlers(bool enable)
{
if (enable)
{
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
The thing is: when I want to remove an event handler I just call this method with false as a parameter.
If I call this method from inside the view code it works just fine. But if I call this method from inside the controller code, it doesn't work (the event handlers are not removed).
Just to give a little more context:
This works:
public partial class View : Form
{
public void enableEventHandlers(bool enable)
{
// The code I already showed
}
public void doSomething()
{
enableEventHandlers(false);
// do something
enableEventHandlers(true);
}
}
This doens't work:
public class controller
{
private View myView;
public void doSomething()
{
myView.enableEventHandlers(false);
// Do something... but somehow everything inside my Form is still paying attention to events
myView.enableEventHandlers(true);
}
}
Finally I found the problem. It seems that somehow I was attaching an event handler twice to the same Control. I couldn't find the exact line number where I was doing that anyway. The solution I found is to remove an event handler before adding a new one.
So the method enableEventHandlers looks now like this:
public void enableEventHandlers(bool enable) {
if (enable)
{
control.someEvent -= someEventHandler;
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
Thanks for your answers.
I don't know if that's it but you didn't initialize your View. You just say "private View view", but that doesn't point to anywhere. You want to either make a new View by doing private View v = new View(), or let that view point to the view that you want to change the events.

WPF Windows Controller

I am having some problems with WPFs.
I have a project that has multiple windows, so to control this windows, I have created a controller class. This controller will have a instance of each windows:
this.mainWindow = new MainWindow();
this.loginWindow = new LoginWindow();
this.registerWindow = new RegisterWindow();
The problem comes when I callback from any of the windows to the controller class and from this controller I want to update the information of the window (for example update the value of a property), the information is not being updated
// In controller
public void login(String email, String pass)
{
....
this.loginWindow.showErrorInPassword();
}
// In LoginWindow
public void showErrorInPassword()
{
this.emailErrorImage.Visibility = System.Windows.Visibility.Visible;
}
... but if I send from the LoginWindow a reference of itself to the login function on the controller, the emailErrorImage will be shown
public void login(String email, String pass, LoginWindow lw)
{
....
lw.showErrorInPassword();
}
Seems that the instance that I have in the controller is not the same as the one that is being displayed when I do this.loginWindow.show()
Can someone tell me what am I doing wrong?
You are going to need to bind the UI objects to a MVVM class to update each window.
Use events to call back to the controller.
Here is a brief example. First create a class to contain event args. Doesn't really have to contain anything. It just differentiates between different delegates. Make it its own class in the namespace so everything has access to it.
public class SomeEventArgs: EventArgs
{
}
Inside the window class:
public event EventHandler<SomeEventArgs> CallBackToController;
protected virtual void OnCallBackEvent(object sender, SomeEventArgse)
{
EventHandler<SomeEventArgs> handle = CallBackToController;
if (handle != null)
{
handle(this, e);
}
}
In the controller class, after instantiating the window assign the event to a method.
this.loginWindow = new LoginWindow();
this.loginWindow.CallBackToController += new EventHandler<SomeEventArgs>(MethodToHandleEvent);
Then the Method must have the same form as expected:
private void MethodToHandleEvent(object sender, SomeEventArgs e)
{
// Do something in response.
}
Now anytime you call OnCallBackEvent(this, new SomeEventArgs()) from the window class, the controller class will catch the event and execute MethodToHandleEvent
For instance:
private void LoginWindowBtn_Click(object sender, RoutedEventArgs e)
{
// Logged in ok, let the controller know.
OnCallBackEvent(this, new SomeEventArgs ());
}
There are a ton of tutorials on this, I think this is a better approach to passing references of windows from window to window.

How to get the callback to viewmodel from library

I have a UIControl(LatestHeadline) in a view(Home.xaml)(there are around 10 more controls on the same page) which is a textblock.I want to set the text of this control when user clicks on a button(named "Retrieve") from the same view.I have to call the method in utilities library from my viewmodel .The method(GetLatestHeadline) in the utilitieslibrary connects to a web api through webclient class and it fires the (deleagate)method Retrieve_Completed event.
Once this event is completed the required data is retrieved from eventargs paramater e.Now from the utilitieslibrary I want to return the data to the viewmodel so that the I can bind this to the LatestHeadline textblock.
LatestHeadline textblock has binding to a property(named "PropHeadLine") defined in viewmodel.
Is there a way we can achieve this in C#.Net?
If you have access to the utility class from the view model, then surely you can just add a handler to your Retrieve_Completed delegate in the view model.
utilityClass.Retrieve_Completed += UtilityClassRetrieve_Completed;
utilityClass.GetLatestHeadline();
...
public void UtilityClassRetrieve_Completed(EventArgs e)
{
// Do something with your e.New value here in the view model
LatestHeadlineProperty = e.New;
}
Of course, I'm just guessing at what your Retrieve_Completed definition is here, but hopefully you get the idea.
I like the concept of a "callback" for this kind of thing -- specifically, pass an Action<string> from the View Model to the service. The service can then save a reference to the callback, or pass it along to the completed-handler in the "UserState". So, the utilities method would look something like this:
public void GetLatestHeadline(Action<string> callback)
{
_apiClient.Retrieve(userState: callback);
}
private void Retrieve_Completed(object sender, RetrieveCompletedEventArgs args)
{
var callback = args.UserState as Action<string>;
if (callback != null)
callback(args.Result.Headline);
}
Now the view model can pass a callback to the utilities method -- the callback should presumably just set the property "PropHeadLine" to the result value:
private void Button_Click()
{
Utilities.GetLatestHeadline( result => PropHeadLine = result );
}
Expose a Delegate or Event in your service, in your view model just subscribe or hook to
that delegate or event
when ever you want to notify the viewmodel Once a particular operation is completed, just invoke the delegate or event
The method which is hooked in your viewmodel will get called.
In your viewmodel now you can do the necessary actions you want

Dependency Injection with MVVM and Child Windows

I am using MVVM Light and I'm currently using SimpleIoC that comes with the package. I'm getting a bit stuck with the dependency injection. I have a bunch of services that I want to use in my view models, however most windows are a List-Edit paradigm, i.e. one screen lists all of type Person and then you can Add or Edit a Person via a new screen.
When I was doing all code in the code behind my code for adding and editing a record was as follows:
View
private void btnEdit_Click(object sender, RoutedEventArgs e)
{
_viewModel.Edit();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
_viewModel.Add();
}
View Model
public void Add()
{
var f = new TypeDetails();
f.Show();
}
public void Edit()
{
if (SelectedItem == null)
return;
var f = new TypeDetails(SelectedItem.Id);
f.Show();
}
The constructor of TypeDetails is as follows:
public TypeDetails(int id = 0)
{
InitializeComponent();
_viewModel = new TypeDetailsViewModel(id);
DataContext = _viewModel;
}
What would the best be to implement this type functionality with MVVM Light? I have been using the ViewModelLocator class for the List screens, however I cannot see a way to do this using the SimpleIoC. My way round so far has been to keep the constructor the same, which works fine until I need to inject dependencies into the TypeDetailsViewModel such as a service. With a service the constructor of TypeDetailsViewModel would be:
public TypeDetailsViewModel(ISomeService someService, int id = 0)
{
...
}
But that means in my view constructor I have to build these dependencies one at a time and manually inject them...
public TypeDetails(int id = 0)
{
InitializeComponent();
_viewModel = new TypeDetailsViewModel(SimpleIoC.Current.GetInstance<ISomeService>(),id);
DataContext = _viewModel;
}
Is there a better way to do this?
First off I would look into the "RelayCommand" class which is part of MVVM Light. It will remove the need for events in your code behind. Start with that.
You should always favor "Constructor Injection" instead of the ServiceLocator (ex: SimpleIoC.Current.GetInstance())
Your ViewModel constructor should only be injecting services and not primitive types like "int". In your example "int id" should be the parameter of a method and not injected.
Ex: Instead, your TypeDetailsViewModel should look more like:
public TypeDetailsViewModel(ISomeService someService)
{
TypeDetail GetDetailsCommand(int id)
{
...
}
}
Lastly, your Models should never have any reference to your ViewModels.
For your DataContext, you can use a ViewModelLocator (ViewModels in ViewModelLocator MVVM Light)
To hook up your View and ViewModel to use the GetDetailsCommand, you can use the EventToCommand behavior (http://msdn.microsoft.com/en-us/magazine/dn237302.aspx). Ex: The OnLoaded event on the View calls the GetDetailsCommand on your ViewModel.

Categories

Resources