I create a project with infragistic which generates the view and views models folders, what I want to do now is create a binding context to the view model as it is normally done, but this view model has INavigationService parameters and I don't know how to configure it. Those parameters, if someone helps me, I would really appreciate it, I attach images so that they understand me more.
enter image description here
enter image description here
In the mainPage background code you can use following code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext= new PersonsViewModel(Navigation);
}
}
This Navigation comes from NavigableElement you can use Navigation directly in ContentPage.
NavigableElement is under the Xamarin.Forms namespace like following code.
============Prism==============
If you use Prism. you should registe it the App.xaml.cs.
public partial class App
{
/*
* The Xamarin Forms XAML Previewer in Visual Studio uses System.Activator.CreateInstance.
* This imposes a limitation in which the App class must have a default constructor.
* App(IPlatformInitializer initializer = null) cannot be handled by the Activator.
*/
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
}
}
In your MainPage.xaml.cs you do not need other binding code.
Here is a demo about it.
https://github.com/manacespereira/xamarin-prism-navigation
Related
I am trying to understand/learn Prism with Unity
I created following classes:
==========================================
Seperate Assembly containing a "Module":
using GlobalContracts;
using Prism.Ioc;
using Prism.Modularity;
namespace ModuleA
{
[Module(ModuleName = MyModuleA.NAME, OnDemand = true)]
public class MyModuleA : IModule
{
public const string NAME = "MyModuleA";
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<MyControlA>();
containerRegistry.Register<IView, MyControlA>(NAME);
containerRegistry.Register<PluginViewModelBase, MyControlViewModel>(NAME);
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
}
}
==========================================
A ViewModel
using GlobalContracts;
namespace ModuleA
{
public class MyControlViewModel : PluginViewModelBase
{
public MyControlViewModel(IView view) : base(view)
{
}
}
}
==========================================
The Host Application (other assembly):
public partial class App : PrismApplication
{
private Shell mShell;
private ShellViewModel mShellViewModel;
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog(){ModulePath = #"..\..\..\..\ModulesOutput"};
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IView, Shell>();
containerRegistry.Register<IViewModel, ShellViewModel>();
}
protected override Window CreateShell()
{
mShellViewModel = Container.Resolve<ShellViewModel>();
mShell = (Shell)mShellViewModel.View;
return mShell;
}
(...)
Now my question is:
How do I tell Prism to resolve the IView-Parameter passed to the
constructor of the ViewModel properly?
It resolves it as "Shell" and not as "MyControlA".
Further tips regarding my code are welcome
I found some sources in the web but they used "RegisterType" method of a container. And for now I do not have dependencies to Unity in my ModuleA and I would not know how to get the container to call the "RegisterType". All sources are outdated in the web..
By default, it resolves the default registration, which in your case is Shell.
Registering a type with a name does not mean that that name is automatically used to resolve dependencies. You have to do that manually, with parameter override, injection factory or the like. But I'd try to avoid that as it makes things a bit fragile and tedious.
I really made a search for this topic and did not find anything, and because of that, I am asking the question here.
I have a WPF application with Prism installed.
I have wired the view-model with the view automatically by name convention
<UserControl x:Class="Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
and the model in the 'Model' like this
public class ViewAViewModel {
public ViewAViewModel () {
// time-resource consuming operations
}
}
the automatic binding work perfectly without a problem and the view and its corresponding view-model is matching, but the problem here.
I have a lot of those views say (50) and for every one of them, the view-model will be created with constructor exhausting the processes. This will make the startup of the application longer and also it will create a lot of view-models objects and put them in the RAM without being sure that they will be used at all.
What I need is to create the view-model class when the view is activated (I mean when the view is navigated to). Is this possible and if yes how?
Update
here is how I register the view with the Module, this is causing all the views to be created when the startup of the module.
public class Module1 : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("region1", typeof(View1));
regionManager.RegisterViewWithRegion("region1", typeof(View2));
is there any way to delay the creating of the views, until the navigation request come?
You could use navigation, for each view.
Or you must create an interfaces for your view and view model.
An example:
public interface IMyView
{
IMyViewModel ViewModel { get; set; }
}
public interface IMyViewModel
{
}
In the module or app.cs, in the method RegisterTypes you should register these.
containerRegistry.Register<IMyView, MyView>();
containerRegistry.Register<IMyViewModel, MyViewModel>();
You must implement IMyView interface in your MyView.cs class.
public partial class MyView : UserControl, IMyView
{
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public IMyViewModel ViewModel
{
get => DataContext as IMyViewModel;
set => DataContext = value;
}
}
After you could use it:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var firstView = containerProvider.Resolve<IMyView>();
regionManager.AddToRegion(RegionNames.MainRegion, firstView);
}
In such case you shouldn't use ViewModelLocator.AutoWireViewModel in your view.
I am developing an Android app in Xamarin.Android(C#). However, i do feel that this question can also be answered by any Java devs as well.
I am new in android development. Anyways, i created a fragment with just a LinearLayout and a TextView inside it. When i create the background class for it, i don't inherit(on in JAVA's word, extend) it from the Fragment class but rather from the LinearLayout class.
So, the MyFragment.cs file starts like this :
public class MyFragment : LinearLayout
The JAVA equivalent would be
public class MyFragment extends LinearLayout
(P.S. I have limited knowledge of JAVA and it's sytaxes).
Anyways, all works fine. I have an Initialize method(In JAVA, it should be the Init method) which inflates the view of the fragment. From the view, it tries to find the TextView with the given Id.
So, the codes looks like this :
public class MyFragment : LinearLayout
{
Context mContext;
private void Initialize(Context ctx)
{
//Inflating the layout
mContext = ctx;
var inflatorService = (LayoutInflater)ctx.GetSystemService(Context.LayoutInflaterService);
View v = inflatorService.Inflate(Resource.Layout.MyFragmentView, this, false);
this.AddView(v);
GoalHeader = v.FindViewById<TextView>(Resource.Id.GoalHeader);
}
All works pretty well this far. I then go on implementing the MVVM pattern, using MVVMLight library. I create a ViewModel as follows :
public class Vm_MyFragment : ViewModelBase
{
private string _goaltitle = "";
public string GoalTitle
{
get { return _goaltitle; }
set { Set(ref _goaltitle, value); }
}
public void SetTest()
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
GoalTitle = "Test";
});
}
}
Still, everything's good. The problem starts when i try to bind the TextView's text property to the ViewModel's GoalTitle property, as follows :
private readonly List<Binding> _bindings = new List<Binding>();
private void Initialize(Context ctx)
{
//Inflating the layout
mContext = ctx;
var inflatorService = (LayoutInflater)ctx.GetSystemService(Context.LayoutInflaterService);
View v = inflatorService.Inflate(Resource.Layout.MyFragmentView, this, false);
this.AddView(v);
Vm_MyFragmentView viewmodel = new Vm_MyFragmentView();
GoalHeader = v.FindViewById<TextView>(Resource.Id.GoalHeader);
_bindings.Add(
this.SetBinding(
() => mainViewModel.GoalTitle,
() => GoalHeader.Text));
}
Note : Binding is from the GalaSoft.MvvmLight.Helpers namespace.
I add the fragment in my main view(I mean, MainActivity's view) and debug the app. Upon execution, i get the following error :
Could not activate JNI Handle 0xfff02a68 (key_handle 0x339790a) of Java type 'md55bfae9a06327fa0fdf207b4f768604b1/MyFragment' as managed type 'TestApp.MyFragment'.
Searching google, i realized that i am trying to bind the property before the view is even created(correct me if i'm wrong). The suggestions i found on other SO answers were either to put the code in the OnCreateView method or somehow delay the execution of the binding part's code.
The first solution didn't work for me as LinearLayout aka a View doesn't have such a method OnCreateView which i can override.
So, how am i supposed to bind the TextView to the ViewModel then? And also, am i on the right track on treating the fragment as a LinearLayout as i am inheriting from it?
Im not familiar with MVVMLIght extension but if you are using a fragment as it is supposed to (ie. in a tablayout) you should inherit from a fragment like this (This is a v4 support fragment):
public class CategoryFragment : SupportFragment {
RecyclerView _recyclerView;
private View _view;
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
_view = inflater.Inflate (Resource.Layout._CategoryLayout, container, false);
// Get our RecyclerView layout:
_recyclerView = _view.FindViewById<RecyclerView> (Resource.Id.categoryRecyclerView);
// Instantiate the layout manager
var linearLayoutManager = new LinearLayoutManager (Context, LinearLayoutManager.Vertical, false);
_recyclerView.SetLayoutManager (linearLayoutManager);
// Instantiate the adapter and pass in its data source:
_adapter = new CategoryAdapter (_categories);
//Register the item click handler with the adapter:
_adapter.ItemClick += OnItemClick;
// Plug the adapter into the RecyclerView:
_recyclerView.SetAdapter (_adapter);
return _view;
}
}
So, I've been developing an App for 2 months with prism, and just now I've realized that the method OnNavigatedTo is been calling twice when I select an Item from a MasterDetailPage.
I have no clue why it is happening, I'm sure I'm missing something but I'm about two days trying to solve it.
I'll put some code here, and if u guys need more info I can post more detailed.
Observation: When I'm in the page "A" and I choose the page "A" in the master detail item list, the OnNavigatedTo is called only once, but when I'm in Page "B" and I choose the page "A", The OnNavigatedTo is called twice.
Since now, Thank you guys and sorry about the ignorance.
MasterDetailPage MVVM:
public class PrincipalMasterDetailPageViewModel : ViewModelBase {
public ObservableCollection<PrincipalMasterPageItem> MenuItems { get; set; }
public PrincipalMasterDetailPageViewModel(INavigationService navigationService) : base(navigationService)
{
MenuItems = new ObservableCollection<PrincipalMasterPageItem>();
}
public async override void OnNavigatedTo(NavigationParameters parameters) {
base.OnNavigatedTo(parameters);
.. Here I'm calling an API, thats why I have the async
}
}
Custom Navigation Page MVVM:
public class PrincipalNavigationPageViewModel : ViewModelBase {
public PrincipalNavigationPageViewModel(INavigationService navigationService) : base(navigationService) {
}
}
The Page that I actually show when I select an item in masterdetailpage item list:
public class NewPageTestViewModel : ViewModelBase
{
public NewPageTestViewModel(INavigationService navigationService) : base(navigationService)
{
}
public override void OnNavigatedTo(NavigationParameters parameters)
{
base.OnNavigatedTo(parameters);
Debug.WriteLine("Calling twice HERE!");
}
}
The RegisterTypes of these three examples:
containerRegistry.RegisterForNavigation<PrincipalMasterDetailPage>();
containerRegistry.RegisterForNavigation<PrincipalNavigationPage>();
containerRegistry.RegisterForNavigation<NewPageTest>();
How do I call other pages from PrincipalMasterDetailPageViewModel:
NavigationService.NavigateAsync(string.Format("PrincipalNavigationPage/{0}", item.TargetPageName));
In App.cs I start like the following because I need the login page first:
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("LoginPage");
}
When the user log in, It navigate like this:
await NavigationService.NavigateAsync("/PrincipalMasterDetailPage/PrincipalNavigationPage/WhateverPageIWantTo");
I don't know if anybody is still interested in this but I ran into the same issue and figured out what is going on.
All code samples I found register a NavigationPage like this:
containerRegistry.RegisterForNavigation<NavigationPage>("Navigation");
In order to be able to do something like this on app launch:
NavigationService.NavigateAsync($"Main/Navigation/Home");
However, the problem seems to be that when this NavigationPage is instantiated without a specific ViewModel assinged to it, the 'INavigationAware' events are somehow propagated to the MasterDetailPage's ViewModel resulting in the events on that one to be called twice.
I fixed it by registering the NavigationPage for Navigation with a ViewModel like this:
containerRegistry.RegisterForNavigation<NavigationPage, NavigationPageViewModel>("Navigation");
The ViewModel itself is nothing special:
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Attributes;
namespace SocialRecipe.ViewModels
{
public class NavigationPageViewModel : ViewModelBase
{
public NavigationPageViewModel()
{
}
public override void OnNavigatedFrom(INavigationParameters parameters)
{
}
public override void OnNavigatedTo(INavigationParameters parameters)
{
}
public override void OnNavigatingTo(INavigationParameters parameters)
{
}
}
}
This way the events of the NavigationPage are routed to the NavigationPageViewModel and are no longer propagated to the MasterDetailPageā¦
I've noticed some inconsistent results with Prism as well from a previous project. If you are only seeing it now and it worked previously I would try and backtrack to see if the version you used was different.
https://github.com/PrismLibrary/Prism/issues
I'm working on a legacy application that's written using Silverlight 5, The application contains lot's of anti-patterns and bad practices. I'm responsible for adding real-time interactions (such as notification) using SingalR.
By the way, They're using these WCF RIA Services for interacting with authentication.
They have a Main page, this page is the place where I'm getting user's notification and show them for logged in users:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
//...
}
}
So as you can see I didn't set DataContext property as long as user is logged in, I need to set MainPage's DataContext after a user logs in to application, So I have to do that in LoginOperation_Completed inside LoginForm page:
public partial class LoginForm : StackPanel
{
private LoginRegistrationWindow parentWindow;
private LoginInfo loginInfo = new LoginInfo();
public LoginForm()
{
InitializeComponent();
//...
}
private void LoginOperation_Completed(LoginOperation loginOperation)
{
if (loginOperation.LoginSuccess)
{
// Here I need to access MainPages's DataContext property and set it with my ViewModel
}
}
}
Now my question is that, how can I set MainPage's DataContext property inside another class (in this case LoginFrom)?
I have also tried to give an ID to my MainPage user control and access it like this:
mainPage.DataContext = new NotificationItemViewModel();
But the compiler gives me this error:
The name 'mainPage' does not exist in the current context
I finally figured out How to solve my question, There is a simple way to achieve this, I should have created the static instance of the MainPage class in the class itself:
public partial class MainPage : UserControl
{
public static MainPage Instance { get; private set; }
public MainPage()
{
InitializeComponent();
Instance = this;
}
}
Now I can access to the MainPage's DataContext this way:
MainPage.Instance.DataContext = new NotificationItemViewModel();
You need to name your MainPage UserControl where you pasted it in LoginForm XAML. Not in the definition of MainPage.