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

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);
}

Related

Navigate to a Default View when Application Loaded using Prism 7 in WPF

I follows samples code provided in https://github.com/PrismLibrary/Prism-Samples-Wpf/blob/master/17-BasicRegionNavigation
I want to achieve the following result when I run the application (without explicitly clicking Navigate to View A). Does anyone know how to do it?
I have tried adding Navigate("ViewA"); after this line. However, I cannot get the desired outcome. Is it because the module hasn't been initialized?
Thanks.
did you add your module to the modulecatalog using override method ConfigureModuleCatalog? take a look at here
Eventually I solve by adding the following code in MainWindow.xaml.cs
public partial class MainWindow
{
IRegionManager _regionManager;
public MainWindow()
{
InitializeComponent();
_regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
RegionManager.SetRegionManager(ContentRegion, _regionManager);
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
Get idea from: https://github.com/MahApps/MahApps.Metro/issues/1020#issuecomment-44779574
I'm kinda late to the party here, but I also stumbled over the question of how to navigate to a default view during the applications startup.
I found two ways:
1. App decides the default view
This can be solved in the CreateShell()-override in the App-Class.
This is my CreateShell-Method:
/// <inheritdoc />
protected override Window CreateShell()
{
var window = this.Container.Resolve<MainWindow>();
window.Loaded += (sender, args) =>
{
var manager = this.Container.Resolve<IRegionManager>();
manager.RequestNavigate("ContentRegion", "ViewA");
};
return window;
}
2. ViewModel decides the default view
Add a constructor to MainWindowViewModel that looks like this:
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RegisterViewWithRegion("ContentRegion", "ViewA");
}

Prism VM binding with View within Page code behind

Using Xamarin Forms & PCL
I saw a lot of examples and snippets About binding VM with View in the Page.Xaml
using this block
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
And what if I want to bind the view model within the page code behind (Page.cs).
You can get access to ViewMode from code behind is simply by typecasting your binding context
var pageViewModel = (PageViewModel)this.BindingContext;
It works for me.
That case you have to pass the both parameter on class instantiated because you are have required two parameter in constructor.Try the below code.
public Page()
{
InitializeComponent();
this.BindingContext = new PageViewModel(Navigation,PageDialogService);
}
In my case
I removed from page.xaml
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
and inside the code Behind (page.cs)
i added
public Page()
{
InitializeComponent();
this.BindingContext = new pageViewModel(null,null);
}
and it worked for me
You just can new the viewmodel and set it to the BindingContext.
public Page()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
==== EDITED ====
If your viewmodel is with parameter that need to dependency inject and you want to resolve it correctly.
App.xaml.cs
protected override void OnInitialized()
{
...
Microsoft.Practices.Unity.UnityContainerExtensions.RegisterType<IMyViewModel, MyViewModel);
...
}
Page.xaml.cs
public Page()
{
InitializeComponent();
var viewModel = Microsoft.Practices.Unity.UnityContainerExtensions.Resolve<IMyViewModel>(((App)Application.Current).Container);
this.BindingContext = viewModel;
}

Select which view to display when Main Window open in Prism

This really should be easy, but I could not establish it.
I have small WPF application with Prism 6
I have Main Window and two views inside it.
MainWindow with MainWindowViewModel view model class
ConfigurationView with ConfigurationViewModel view model class
SignInView with SignInViewModel view model class
Now when show the main window for the first time, I want to select which view to show according to some boolean condition
here is the snippet of the bootstrap class.
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>();
ViewModelLocationProvider.Register<SignInView, SignInViewModel>();
ViewModelLocationProvider.Register<ConfigurationView, ConfigurationViewModel>();
Container.RegisterInstance(new SignInView());
Container.RegisterInstance(new ConfigurationView());
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
IRegionManager _regionManager = Container.Resolve<IRegionManager>();
IRegion _region = _regionManager.Regions[RegionNames.MainRegion];
_region.Add(Container.Resolve<SignInView>());
_region.Add(Container.Resolve<ConfigurationView>());
}
Right now always the SignInView is displayed when the main window of the application is opened?
How can I select which view to show according to some condition which need to be brought from the MainWindowViewModel class ?
Update
public class MainWindowViewModel
{
private IAccountService _accountService;
public MainWindowViewModel(IUnityContainer container)
{
IRegionManager regionManager = Container.Resolve<IRegionManager>();
_accountService = container.Resolve<IAccountService>();
if (_accountService.IsSignedIn)
regionManager.RequestNavigate(RegionNames.MainRegion, new Uri(nameof(ConfigurationView), UriKind.Relative));
else
regionManager.RequestNavigate(RegionNames.MainRegion, new Uri(nameof(SignInView), UriKind.Relative));
}
}
Main Window View
<Window x:Class="Shell.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
this code is not working.
The region needs to be created before you can add a view to it. So you'd better create the MainViewModel yourself after the MainWindow has been created:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<SignInView, SignInViewModel>();
ViewModelLocationProvider.Register<ConfigurationView, ConfigurationViewModel>();
Container.RegisterInstance(new SignInView());
Container.RegisterInstance(new ConfigurationView());
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
IRegionManager _regionManager = Container.Resolve<IRegionManager>();
IRegion _region = _regionManager.Regions[RegionNames.MainRegion];
_region.Add(Container.Resolve<SignInView>());
_region.Add(Container.Resolve<ConfigurationView>());
var mainWindowViewModel = Container.Resolve<MainWindowViewModel>();
Application.Current.MainWindow.DataContext = mainWindowViewModel;
Application.Current.MainWindow.Show();
}
Remove this from the MainWindow:
prism:ViewModelLocator.AutoWireViewModel="True">
First of all, register the types of your views for navigation, not view instances.
Secondly: conditions - or more general: data - rarely originates from a view model (unless it's user input), so you should have some IUserManagement service to pull the current user from.
But that being said, if you have your view registered, you should be able to navigate to it. Without a concrete exception, it's difficult to guess what's going wrong.
Container.RegisterTypeForNavigation<SignInView>();
...
_regionManager.RequestNavigate( RegionNames.MainRegion, typeof(SignInView).Name );

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

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();

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