Why does MvvmCross create a new ViewModel when back button is pressed - c#

I'm building a Windows 8.1 mobile app with MVVMCross and I noticed some strange behavior when navigating between viewmodels.
I have 2 Viewmodels like so...
public class FirstViewModel : MvxViewModel
{
public override void Start()
{
base.Start();
}
public void Init()
{
//init code here
}
...
private void GoForward()
{
ShowViewModel<SecondViewModel>();
}
}
public class SecondViewModel : MvxViewModel
{
private void GoBack()
{
Close(this);
}
}
When I call Close(this) on SecondViewModel, Mvvmcross calls Start() and Init() on FirstViewModel again, like it was tombstoned.
Why does this happen? Even if I only leave FirstViewModel for a couple seconds, it seems to be tombstoned immediately. How do I stop this from happening? It's frustrating because I now have to maintain the state of FirstViewModel constantly.

You need to write the following line of code in your page constructor. This enables the caching of visited pages.
NavigationCacheMode = NavigationCacheMode.Required;

Related

Pop up a view and transfer parameters to original one with caliburn.micro

I used caliburn.micro version 4.0.173 and .NET framework 4.8.
Requirement is as below description
I have a script ViewModel. There is a button to click it and pop up a setting ViewModel.
After setting ViewModel appear, it can be setup some parameters and click button to confirm these settings. When confirm button is clicked, it can trigger to script ViewModel from setting ViewModel.
I refer this link to do this. To create a class is for event in project folder. But, when I publish in setting View, that doesn't work to subscribe in Handle of script viewmodel. How can I pass these parameters to another view model?
Thank you.
Event
public class FooEvent
{
public bool Foo { get; set; }
public FooEvent(bool foo)
{
Foo = foo;
}
}
Setting ViewModel
public class SettingViewModel : Screen
{
private IEventAggregator _eventAggregator;
public ItemViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void PublishFooEvent()
{
_eventAggregator.PublishOnUIThreadAsync(new FooEvent(true));
}
public void BtnSaveItem()
{
PublishFooEvent();
}
}
Script ViewModel
public class ScriptViewModel : Screen, IHandle<FooEvent>
{
private readonly IEventAggregator _eventAggregator;
[ImportingConstructor]
public ScriptViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
_eventAggregator.SubscribeOnUIThread(this);
}
public void Handle(FooEvent fooEvent)
{
}
}
I find out that problem is in other handle.
When I added interface and fixed error automatically.
IDE creates a new method (HandleAsync) which I didn't notice it.
Therefore, I type another handle manually.
It always works on HandleAsync after trigger with another viewmodel.

How to fix crash problem when using WebView in SeparateProcess execution mode?

In order to use WebGL in UWP WebView , I looked for this.
How do I enable WebGL in Xamarin.Forms WebView on UWP?
But a new problem comes. When navigating to another page and go back for a few times, the application crashed and gives you no explain. It seems like too many webview instance was created then I tried to use Singleton pattern to solve it.
public static class WebViewSingleton
{
private static WebView instance;
public static WebView GetInstance()
{
if (instance == null)
{
instance = new WebView(WebViewExecutionMode.SeparateProcess)
{
Source = new Uri("https://www.google.com/")
};
}
return instance;
}
}
public HomePage()
{
this.InitializeComponent();
MyWebView = WebViewSingleton.GetInstance();
MyWebView.Navigate(new Uri("https://sketchfab.com/models/a6af6d1ae2744a55820d00599aca71f2/embed?
autostart=1&internal=1&ui_infos=0&ui_snapshots=1&ui_stop=0&ui_watermark=0"));
this.RootGrid.Children.Add(MyWebView);
}
In this way, debugger tells me MyWebView is already a children for another element. I tried every way to disconnect MyWebView with it's parent but failed.
I find it works when adding NavigationCacheMode="Enabled" to my page, but that is not what I want.
add this to page.xmal
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (this.RootGrid.Children.Contains(MyWebView))
{
this.RootGrid.Children.Remove(MyWebView);
}
}

WPF MVVM compliant way to open views

I would love to find a simple and MVVM compliant way,
to open a new view from the MainWindow.
I have already worked through some instructions and tutorials.
But somehow none really worked or it was a mixture of code behind.
I would like to open a view after pressing a button and edit an ObservableCollection in it.
I have already created DataTemplates in App.xaml and mapped the ViewModels with the respective views.
But I don't know how to cleanly open a separate window from the MainWindow (MainViewModel) via an ICommand for another ViewModel.
You should't open a window directly from the MainWindow nor the MainWindowViewModel. But the view model may use a service to open a window:
public class MainWindowViewModel
{
private readonly IWindowService _service;
public MainWindowViewModel (IWindowService service)
{
_service = service;
}
//...
public void OpenWindowExecuted()
{
_service.ShowWindow();
}
}
Service:
public interface IWindowService
{
void ShowWindow();
}
public class WindowService : IWindowService
{
public void ShowWindow();
{
Window window = new Window()
window.Show();
}
}
You could easily mock out the service in your unit tests.

IoC.BuildUp doesn´t build object

I Am very new to Caliburn and have to expand an existing program, using caliburn.micro
I need my DataContext in a View to be set to a ViewModel which has some propertyinjected parameters. All this should work, so i only need to build it up.
however it does not! here is the code where i try to bulid:
public ProductionMainView()
{
InitializeComponent();
var gg = IoC.Get<ProductionMainViewModel>();
IoC.BuildUp(gg);
this.DataContext = gg;
}
As far as i can understand I need to get the object and then build it.
Now I should get a view "Containing" my ViewModel, but the View is blank,
I hope i have explained myself well enough.
Best regards
First off you are using a really old version, present version is 2.0.2, and you still need to have overrides for GetInstance, GetAllInstances, BuildUp methods in your BootStrapper or a variant for use with Ninject
http://putridparrot.com/blog/caliburn-micro-and-inversion-of-control-using-ninject/
Link above has some source for this.
Remember there is nearly no code in the Codebehind of the View... It will all go into the ViewModels (for the most part). ShellView => ShellViewModel with in ShellViewModel var gg = IoC.Get<ProductViewModel>() depending on you are intent is with that you can bind it to a <ContentControl x:Name="ProductViewModel" /> with a property in yoru viewmodel called exactly that. CM will go out and fine that ViewModel and associated view and mash them together automagically. Expected results on the screen should be your ProductViewModel in that contentcontrol location.
public class ShellViewModel : Screen
{
private ProductViewModel _productViewModel;
public ShellViewModel (){
}
protected override void OnActivate(){
ProductViewModel = IoC.Get<ProductViewModel>();
}
public ProductViewModel ProductViewModel {
get{ return _productViewModel;}
set{
_productViewModel = value;
NotifyOfPropertyChanged (); // 2.0.2 feature...
}
}
}
Now, if you decide to move to the latest version there are subtle changes one Bootstrapper<T> does not exist anymore it was refactored into BootstrapperBase. Next Initialize() will be placed into the ctor of your Bootstrapper(). From there everything will be based on your starting screen is after Initialize() is called in the CTOR you will call DisplayRootViewFor<ShellViewModel>() in the OnStartup override.
Some have said and I tend to agree over using IoC.Get<T>() can be anti-pattern, caution when using it. Sometimes things change unexpectedly. Those tend to DI the items via the CTOR and use from there accordingly.
/*version 1.52*/
public class Bootstrapper : BootStrapper<LoginViewModel>{
public Bootstrapper(){
Start();
}
protected override Configure(){
/* -- your Ninject configurations -- */
}
protected override object GetInstance(Type service, string Key){
if(service == null)
throw new ArgumentNullException("Service");
return _kernel.Get(service);
}
protected override IEnumerable<object> GetAllInstances(Type service){
return _kernel.GetAll(service);
}
protected override void BuildUp(object instance){
_kernel.Inject(instance);
}
protected override void OnExit(object sender, EventArgs e)
{
kernel.Dispose();
base.OnExit(sender, e);
}
}
To clarify do you have to login to get to the ProductionMainViewModel? What determines your logic for getting to ProductionMainViewModel to even display?

MVP Navigation in WinForms

Have been learning about MVP and have tried writing a test app using it in WinForms. I'm struggling to find a well explained example on how to navigate between my forms/views. As an example, the program starts and I want to show a login dialog then go into my main view if the login was successful. At the moment, my Main method looks something like this:
static void Main()
{
var loginView = Injector.Resolve<ILoginView>();
if (loginView.DoLogin() != LoginResult.OK) return;
var mainView = Injector.Resolve<IMainView>();
Application.Run(mainView); // won't work as mainView isn't a form
}
The Injector object is just a wrapper around an IoC tool (currently StructureMap). The thing is, I've read that I shouldn't really be manually creating instances via the Injector as they should really be done via constructor injection.
I've managed to do this up to a point but not when it comes to navigation. I can't think of an elegant way of moving through my views and was wondering if anyone here might shed some light on this? I've read a little on application controllers but have not found an example to show it clearly.
In regards to the navigation question:
I've managed to do this up to a point but not when it comes to
navigation. I can't think of an elegant way of moving through my views
and was wondering if anyone here might shed some light on this? I've
read a little on application controllers but have not found an example
to show it clearly.
Below is a simplified version of a construct I've used. Note that the setup and tear down hooks are called automatically when the NavigateTo method is called. Also, +1 to #AlexBurtsev, as his answer hints at this very same approach.
// Presenter can and should offer common services for the
// subclasses
abstract class Presenter
{
public void Display()
{
OnDisplay();
}
public void Dismiss()
{
OnDismiss();
}
protected virtual OnDisplay() // hook for subclass
{
}
protected virtual OnDismiss() // hook for subclass
{
}
private NavigationManager _navMgr;
internal NavigationMgr NavigationManager
{
get
{
return _navMgr;
}
set
{
_navMgr = value;
}
}
}
// NavigationManager is used to transition (or navigate)
// between views
class NavigationManager
{
Presenter _current;
// use this override if your Presenter are non-persistent (transient)
public void NavigateTo(Type nextPresenterType, object args)
{
Presenter nextPresenter = Activator.CreateInstance(nextPresenterType);
NavigateTo(nextPresenter);
}
// use this override if your Presenter are persistent (long-lived)
public void NavigateTo(Presenter nextPresenter, object args)
{
if (_current != null)
{
_current.Dismiss()
_current.NavigationMgr = null;
_current = null;
}
if (nextPresenter != null)
{
_current = nextPresenter;
_current.NavigationMgr = this;
_current.Display(args);
}
}
}
class MainMenuPresenter : Presenter
{
private IMainMenuView _mainMenuView = null;
// OnDisplay is your startup hook
protected override void OnDisplay()
{
// get your view from where ever (injection, etc)
_mainMenuView = GetView();
// configure your view
_mainMenuView.Title = GetMainTitleInCurrentLanguage();
// etc
// etc
// listen for relevent events from the view
_mainMenuView.NewWorkOrderSelected += new EventHandler(MainMenuView_NewWorkOrderSelected);
// display to the user
_mainMenuView.Show();
}
protected override void OnDismiss()
{
// cleanup
_mainMenuView.NewWorkOrderSelected -= new EventHandler(MainMenuView_NewWorkOrderSelected);
_mainMenuView.Close();
_mainMenuView = null;
}
// respond to the various view events
private void MainMenuView_NewWorkOrderSelected(object src, EventArgs e)
{
// example of transitioning to a new view here...
NavigationMgr.NavigateTo(NewWorkOrderPresenter, null);
}
}
class NewWorkOrderPresenter : Presenter
{
protected override void OnDisplay()
{
// get the view, configure it, listen for its events, and show it
}
protected override void OnDismiss()
{
// unlisten for events and release the view
}
}
I haven't used WinForms for a long time, but I'll try to answer this. I would use the same strategy as WPF Prism do.
About MainView and Application.Run:
Create a main Region (root Form), with empty container inside which can hold UserControl (I forgot exact class names), then when you need to switch root view, you do RootView.SetView(UserControl view) which will do something like Form.Clear(), Form.AddChild(view).
About the navigation and using container:
You could create a service for navigation: INavigationService which you inject in constructors with method like INavigationService.NavigateView(String(or Type) viewName, params object[] additionalData)
You can insert a method in mainView that returns the actual form.Then you can call
Mainview:IMainView
{
Form GetView()
{
//return new Form();
}
}
In Main you can call ,
Application.Run(mainView.GetView())

Categories

Resources