I using MvvmCross to build my UWP application. I have views with their own view models.
When I start the app then first time from Setting view navigate to Passcode view using ShowViewModel<PasscodeViewModel>(). Then it call view model and view constructor to build and initialize view. When user come back to setting view and again navigate to passcode view using same method like ShowViewModel<PasscodeViewModel>() then this time view and view model constructor not get called. Since unable to reinitialize passcode view. So it display previous instance of passcode view.
Following solution I have tried
I tried removing the backstack in the navigations but then also not constructor get call.
Also implement loaded event but this solution also not working for me.
Also implemented Void Init() in view model but this method also not get call.
I just want when I navigate to PasscodeView then each time it call constructor of PasscodeView and PasscodeViewModel.
So my question is how to force to re-initialize page and viewmodel each time while navigation??
Please help to resolve this issue.
I needed to use navigation cache mode on required.
I resolved it for me with the not very clean solution to call init again.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (this.ViewModel!= null && e.NavigationMode != NavigationMode.Back)
{
var reqData = (string)e.Parameter;
var converter = Mvx.Resolve<IMvxNavigationSerializer>();
var req = converter.Serializer.DeserializeObject<MvxViewModelRequest>(reqData);
this.Vm.CallBundleMethods("Init", new MvxBundle(req.ParameterValues));
}
base.OnNavigatedTo(e);
}
This code avoids to call init again on back navigation.
I just set Universal Windows Phone apps page navigation cache mode "Disabled". I think its default value is "Required".
public PasscodeView()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Disabled;
}
The above code work for me.
Related
I currently have a MainWindow that acts as a frame to navigate to other pages in my solution. The problem is, i require one of my pages to be instantiated for the entire duration of my application instead of every time when i navigate to a page, that page gets re-instantiated. I have tried the KeepAlive='true' property for my page but it did not work.
I would like to know if theres a way to implement "this static instance of a page" method for my codes. Thanks. (p.s im not looking or planning to implement the MVVM approach)
public MainWindow()
{
InitializeComponent();
//Instanciate ApiStartup class and Initialize the HTTPClient
ApiStartup.InitializeClient();
Application.Current.MainWindow = this;
Loaded += OnMainWindowLoaded;
}
private void OnMainWindowLoaded(object sender, RoutedEventArgs e)
{
ChangeView(new DetectionPage());
}
public void ChangeView(Page view)
{
MainFrame.NavigationService.Navigate(view);
}
private void quiz_Click(object sender, MouseButtonEventArgs e)
{
var mainWindow = (MainWindow)Application.Current.MainWindow;
mainWindow?.ChangeView(new DetectionPage());
}
If you want to use static value for use in entire application from start to end then you can mention it in app.config file. And easily you can use like ((App)Application.current).KeepAlive in your any xaml file.
A page has the keepalive property.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.page.keepalive?view=netframework-4.8
You may set that to true on whichever page you like.
When you later use navigation methods to navigate to that url, the first instance of the page will be returned.
If that doesn't suit for whatever reason you could cache instances of pages using a dictionary with key type and value page (instance). Then implement some logic decides which pages you do or do not cache in there.
You said you don't want to use mvvm but I think it best to at least mention what almost everyone else does. For other people who intend working in a team then MVVM will be expected and hence for other people reading:
Since pages and frames come with a journal and memory overheads that are often undesirable, most teams (I have encountered) use usercontrols rather than pages. These are presented via a contentcontrol. You can set content to a usercontrol but most teams use viewmodel first. State that matters is bound and templated from an instance of a viewmodel. To retain state between "navigations" a reference to the viewmodel is retained - which is almost guaranteed to be way lighter on memory than any sort of view.
x:Bind defaults to OneTime, which updates the target UI with the data when the Page's Loading event triggers the generated code's Initialize function.
I have a Page with a ViewModel property. This ViewModel class implements INPC for its properties. The data for the viewModel is loaded asynchronously, only after the page is loaded. So on Page initialization, and subsequently the generated code initialization, the UI target using x:Bind will have null data.
Since it is OneTime, it shouldn't change unless I manually call Update(which I don't).
So why does my x:Bind UI work?
The following is some simplified code snippets.
<Page x:Name="MyPage" x:Class="MyProject.Pages.MyPage">
<Button Command="{x:Bind ViewModel.GoToAnotherPageCommand}">
public sealed partial class MyPage : Page
{
public MyPageViewModel ViewModel { get; set; }
public MyPage()
{
this.InitializeComponent();
}
// called by an event bound to a Frame's Navigated, which all pages use
public void OnNavigatedTo()
{
this.ViewModel = new MyPageViewModel();
}
}
public class MyPageViewModel : INotifyPropertyChanged, INotifyPropertyChanging
{
// GoToAnotherPageCommand is an INPC property and its set in the constructor
The reason that your command works fine is because OnNavigatedTo will be called before the command instantiation. This means by the time the code tries to set the command, the ViewModel has already been instantiated and is no longer null.
To prove my point, first go open the file under the following path(could be ARM or *x64 depending on which platform you are running on) -
obj/x86/Debug/MyPage.g.cs
This is basically the code-generated file that hooks up all the x:Bind stuff for your page.
Now put a breakpoint at where the command is set. In my case, it's a method called Set_Windows_UI_Xaml_Controls_Primitives_ButtonBase_Command. Then put another breakpoint at OnNavigatedTo.
Now run the app, you will see that the OnNavigatedTo method gets called first.
If your page's NavigationCacheMode is set to Disabled, this behavior makes OnNavigatedTo the ideal place to instantiate x:Bind bindings so the page only uses memory to create these new objects when the user actually navigates to it, instead of doing everything inside the page constructor.
Don't do this inside the Loaded event of the Page though. Because it will get called after the command instantiation. You can try the following code to instantiate the ViewModel, and the result is very different(your command will not work).
public MyPage()
{
InitializeComponent();
Loaded += (s, e) => ViewModel = new MyPageViewModel();
}
The compiled binding system (x:Bind) is smart enough to check for initial null values and not consider them the actual value you wish to bind. It will wait for the first non-null value and bind that value.
This is by design, as binding to an initial null value is (almost) never the intention of the binding.
I didn't find the source of this information, but I believe it was in the Build talk detailing the x:Bind system in 2015.
Updated:
As Justin mentions in the comments below and in his own answer, the binding will not work if the view model is set after the binding operation happens.
I believe this is because the binding terminates when it encounter a null reference in the property chain, but I haven't tested this, so I might be incorrect.
I am using WPF Prism 6 with autofac and having issues navigating between views. What I have is a view that I only want to keep alive till I leave it, and the next time I navigate to it, I want to create a new version of this view.
On load, I regist an IModule that has the following code
_regionManager.RegisterViewWithRegion(RegionNames.MainRegion,
typeof(DxfDisplay.Views.DxfDisplay));
This registers my view and the system works on initial load, I implement the INavigationAware and IRegionMemberLifetime interfaces on the view model and have public bool KeepAlive => false; implementing the IRegionMemberLifetime so that my view is disposed when I am done.
When I navigate away from this view everything is fine, but when I attempt to navigate to navigate to the view using
_regionManager.RequestNavigate(RegionNames.MainRegion,
new Uri("DxfDisplay", UriKind.Relative), parameters);
The view is not opened and a view model constructor is not called. To make the navigation work correctly, I need to register with view with the region again. Or if I change the KeepAlive to true I can navigate back to the original view, but I cannot generate a new view if INavigationAware.IsNavigationTarget returns false.
My question is how do I register the view with the region manager in such a way that when I call _regionManager.RequestNavigate, it will create a new instance of the view and display it. I feel like I am missing something simple and just overlooking it.
_builder.RegisterTypeForNavigation<DxfDisplay.Views.DxfDisplay>();
In Prism 7, this is now called RegisterForNavigation<T>() and exists on the IContainerRegistry interface.
For example, in your module:
public class MyModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MyView>();
}
}
I'm working on an application and everything works fine but when I go back to the previous page the View Model is called so it will not maintain the old data.
I use this line to call My View Model in XAML.
prismmvvm:ViewModelLocator.AutoWireViewModel="true"
So my question is:
How do I disable the call of the View Model when I go back?
You're referring caching here.
In the constructor of your ViewModel , set your NavigationCacheMode
this.NavigationCacheMode = NavigationCacheMode.Required;
and in OnNavigatedTo event handler , check your navigationMode and delete if you're doing something more than default initializing.
I'm currently building a universal app but I'm concentrating on the WP8.1 part of it right now. I'm using MVVMLight with this project.
For simplicity sake, we'll just assume that I only have 2 pages in the project.
Page1.xaml contains a list which has various items. The Page1.xaml is binded to its own ViewModel i.e. Page1ViewModel. Each item in the list represents a viewModel i.e. ItemViewModel.
When I tap on an item, I call the following code:
public RelayCommand<ItemViewModel> ItemTapCommand
{
get
{
return this._itemTapCommand ?? (this._itemTapCommand =
new RelayCommand<ItemViewModel>((msg) =>
ExecuteItempTapCommand(msg)));
}
}
When an item in the list is tapped, I call the following code:
private object ExecuteItempTapCommand(ItemViewModel selectedItemViewModel)
{
Page2ViewModel page2ViewModel =
SimpleIoc.Default.GetInstance<ItemViewModel>();
page2ViewModel.SelectedItem = selectedItemViewModel;
_navigationService.Navigate(typeof(Page2),
selectedItemViewModel);
return null;
}
As you can see I'm using my Ioc to create get an instance of my Page2ViewModel and I then set the SelectedItem to the selectedItemViewModel.
Once it is set, I navigate to Page2 which is binded to my Page2ViewModel.
What I want to know is, is the above is ok to do? I've seen plenty of examples when dealing with passing object from one page to another is done by passing an Id for example and then I request the information from Page2, but why request it again when most of the information I need is already in my SelectedItemViewModel since it represents the tapped item in my list in Page1.
If it's not correct, what is the best way to go about this using MVVMLight?
Can you provide a sample? I've seen something about Messaging but I'm not sure how this would work as if I navigate to my page2, the Page2ViewModel will only be initiated when the page is created, so how can it receive a message? The way I have it above seems to initiate the Page2ViewModel and my Pag2 loads, it's re-using it and everything bind correctly but I'm not sure this is the correct way to go about it.
Any help would be appreciated.
Thanks.
In your Page2ViewModel, why not use
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Page2SelectedItem = e.Parameter as ItemViewModel;
base.OnNavigatedTo(e);
}
It looks like you are packing that data in with your _navigationService.Navigate call already.
With that set up, what happens if you just change to:
private object ExecuteItempTapCommand(ItemViewModel selectedItemViewModel)
{
_navigationService.Navigate(typeof(Page2), selectedItemViewModel);
return null;
}
You can use the ViewModel to get it if you do some work before that.
Read this blog post by Marco Minerva called Calling ViewModel methods in response to Page navigation events using MVVM Light in WinRT
which explains how to react to OnNavigatedTo and OnNavigatedFrom in the ViewModel.
It's a very cool solution.