Xamarin.iOS with MVVMCross call only constructors at ViewControllers - ios

Does anybody face this problem? I'm trying to implement MVVM using MVVMCross in Xamarin.iOS project, but in ViewController only constructor was called, neither ViewDidLoad nor ViewWillAppear. What's wrong? Or may be I miss something? Thank you.
public partial class VideosViewController : MvxViewController<VideoListViewModel>
{
public VideosViewController() : base(nameof(VideosViewController), null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
}
}
public class VideoListViewModel : MvxViewModel
{
private readonly IVideoService _videoService;
public CancellationTokenSource TokenSource => new CancellationTokenSource();
public VideoListViewModel(IVideoService videoService)
{
_videoService = videoService;
}
public override async Task Initialize()
{
await base.Initialize();
}
}

Related

Xamarin Android cannot override OnBackButtonPressed method

My problem is whenever i try to override OnBackButtonPressed visual studio just tells me that there are no method that can be override.
Here is the code
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity {
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults){
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override bool OnBackButtonPressed() {
return true;
}
}
}
whenever i remove "override" tag the error disapper but the evente is never triggered.
Any idea why cant override this method?
OnBackPressed is an Android method, you can override it in the file MainActivity.cs this way:
public override void OnBackPressed()
{
// your code over here
base.OnBackPressed();
}

Navigate to ViewModel From Current View Instead of RootView

Is there a way to navigate to a ViewModel from my current view instead the root view?
I have created a view model that is presented modally from the root view and wish to present a view model within that view.
For example, when calling _navigationService.Navigate<MyViewModel>(); from my modal view, the root view navigates to MyViewModel instead of the current modal view. This leaves my modal screen with a blank page until you dismiss it and see that the navigation has taken place below.
Do I create a custom navigation, or is there a better way of going about this?
EDIT
Here is some code:
My View Model
public class ViewModel1 : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public ViewModel1(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public IMvxCommand NavigateCommand => new MvxCommand(ChangeView);
void ChangeView()
{
_navigationService.Navigate<ViewModel2>();
}
}
My View
[MvxModalPresentation(WrapInNavigationController = true, ModalPresentationStyle = UIModalPresentationStyle.Custom, Animated = false)]
public partial class MyView : MvxViewController
{
ViewModel1 VM;
public MyView() : base("MyView", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
NavigationController.NavigationBar.Hidden = true;
var set = this.CreateBindingSet<MyView, ViewModel1 >();
set.Bind(NextButton).To(vm => vm.NavigateCommand);
set.Apply();
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
}
I would like to have the navigation command present ViewModel2 within the presented modal.
For this kind of customization, you may need a write a custom presenter and there you can fiddle around to achieve whatever you can achieve with native.
I am dropping some hint.
In AppDelegate, inside finish launching.
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var presenter = new DelegatingPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
---
---
}
DelegatingPresenter looks like:
public class DelegatingPresenter : MvxIosViewPresenter {
private UIWindow currentWindow;
public DelegatingPresenter(AppDelegate appDelegate, UIWindow window)
: base(appDelegate, window) {
currentWindow = window;
--
---
}
protected override void SetWindowRootViewController(UIViewController controller, MvvmCross.iOS.Views.Presenters.Attributes.MvxRootPresentationAttribute attribute = null)
{
base.SetWindowRootViewController(controller, attribute);
}
public override void Show(MvxViewModelRequest request)
{
var viewCreator = Mvx.Resolve<IMvxIosViewCreator>();
var controller = (UIViewController)viewCreator.CreateView(request);
Show((UIViewController)controller);
}
//public override void Show(IMvxIosView view) {
//}
public override void ChangePresentation(MvxPresentationHint hint) {
---
----
}
public override void Close(IMvxViewModel toClose) {
---
----
}
public void Show(UIViewController) {
//if (viewController is IFlyoutNavigationControllerCapable) {
//flyoutPresenter.Show(viewController, flyout);
//}
//else {
//currentWindow.RootViewController = viewController;
//navigationPresenter.Show(viewController, flyout);
//}
//Do your thing here.
}
public bool Close(FlyoutNavigationController flyout) {
---
----
}
}
Here, your point of interest are "show" method override thats gets called when load new ViewModel.
Here you have your rootviewController and new controller that you want to present, so I guess you can take it from here.

How to pass the data from modally presented ViewController to another ViewController in Xamarin.iOS?

I have to controller A and B.
From A I'm navigating to B in the B on button click I have open another ViewController using PresentModalViewController, which ask the user to enter some data. On the click of the submit button on that modally presented view I want to dismiss the modal and show the data on the B view that captured in modally presented view.
Here is what I have done:
I have InviteCollaboratorViewController from this I have modally presented AddNewCollaboratorDialogPopUpView and from there again I have modally presented another view named AddCollaboratorPopUpView.
AddCollaboratorPopUpView user will fill the details of and click on addCollaboratorButton, on this button click event I want to dismiss both the modally presented views, and want to reflects the changes in InviteCollaboratorViewController tableview.
InviteCollaboratorViewController
public partial class InviteCollaboratorViewController : UIViewController
{
public override void ViewDidLoad()
{
btnNewDialog.TouchUpInside += BtnNewDialog_TouchUpInside;
MyCollaboratorsTableView.Source = new MyCollaboratorsTableViewSource(this, _listOfMyCollaborators);
MyCollaboratorsTableView.RowHeight = UITableView.AutomaticDimension;
MyCollaboratorsTableView.ReloadData();
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
MyCollaboratorsTableView.ReloadData();
}
public void BtnNewDialog_TouchUpInside(object sender, EventArgs e)
{
AddNewCollaboratorDialogPopUpView addNewCollaboratorDialogPop = this.Storyboard.InstantiateViewController("AddNewCollaboratorDialogPopUpView") as AddNewCollaboratorDialogPopUpView;
if (addNewCollaboratorDialogPop != null)
{
addNewCollaboratorDialogPop.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
addNewCollaboratorDialogPop.ModalTransitionStyle = UIModalTransitionStyle.CrossDissolve;
this.PresentViewController(addNewCollaboratorDialogPop, true, null);
}
}
}
AddNewCollaboratorDialogPopUpView
public partial class AddNewCollaboratorDialogPopUpView : UIViewController
{
public AddNewCollaboratorDialogPopUpView (IntPtr handle) : base (handle) { }
public override void ViewDidLoad()
{
base.ViewDidLoad();
SET_FONTS();
SET_UI_CONTROLS_STYLING();
createNewCollaboratorButton.TouchUpInside += CreateNewCollaboratorButton_TouchUpInside;
}
public void CreateNewCollaboratorButton_TouchUpInside(object sender, EventArgs e)
{
AddCollaboratorPopUpView addCollaboratorPopUp = this.Storyboard.InstantiateViewController("AddCollaboratorPopUpView") as AddCollaboratorPopUpView;
if (addCollaboratorPopUp != null)
{
addCollaboratorPopUp.ModalPresentationStyle = UIModalPresentationStyle.Popover;
addCollaboratorPopUp.ModalTransitionStyle = UIModalTransitionStyle.CrossDissolve;
this.PresentViewController(addCollaboratorPopUp, true, null);
}
}
}
AddCollaboratorPopUpView
public partial class AddCollaboratorPopUpView : UIViewController
{
public AddCollaboratorPopUpView (IntPtr handle) : base (handle) { }
public override void ViewDidLoad()
{
base.ViewDidLoad();
textFieldFirstName.ShouldEndEditing += TextFieldFirstName_ShouldEndEditing;
textFieldLastName.ShouldEndEditing += TextFieldLastName_ShouldEndEditing;
textFieldPhoneNumber.ShouldEndEditing += TextFieldPhoneNumber_ShouldEndEditing;
textFieldEmail.ShouldEndEditing += TextFieldEmail_ShouldEndEditing;
dismissPopupButton.TouchUpInside += DismissPopupButton_TouchUpInside;
addCollaboratorButton.TouchUpInside += AddCollaboratorButton_TouchUpInside;
}
public void DismissPopupButton_TouchUpInside(object sender, EventArgs e)
{
PresentingViewController?.PresentingViewController?.DismissModalViewController(false);
}
public void AddCollaboratorButton_TouchUpInside(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(textFieldFirstName.Text) && !string.IsNullOrEmpty(textFieldLastName.Text)
&& !string.IsNullOrEmpty(textFieldPhoneNumber.Text) && !string.IsNullOrEmpty(textFieldEmail.Text))
{
//Now from here what I want is to call the InviteCollaboratorViewController's ViewWillAppear() because that Refresh's the list of
//users which reflect the added user on list instantly as popup get dismissed.
collaborator.FirstName = textFieldFirstName.Text;
collaborator.LastName = textFieldLastName.Text;
collaborator.Email = textFieldEmail.Text;
collaborator.PhoneNumber = textFieldPhoneNumber.Text;
GlobalConstants.listOfMyCollaborators.Add(collaborator);
PresentingViewController.PresentingViewController.DismissModalViewController(false);
}
else
{
Toast.MakeToast("Please fill all required fields before submitting.").Show();
}
}
}
Try code below, I have made few modifications. It might help you
InviteCollaboratorViewController.cs
public override void ViewDidLoad()
{
btnNewDialog.TouchUpInside += BtnNewDialog_TouchUpInside;
_listOfMyCollaborators.Add(GlobalConstants.Collaborators);
GlobalConstants.Collaborators=null;
MyCollaboratorsTableView.Source = new MyCollaboratorsTableViewSource(this, _listOfMyCollaborators);
MyCollaboratorsTableView.RowHeight = UITableView.AutomaticDimension;
MyCollaboratorsTableView.ReloadData();
}
AddCollaboratorPopUpView.cs
public void AddCollaboratorButton_TouchUpInside(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(textFieldFirstName.Text) && !string.IsNullOrEmpty(textFieldLastName.Text)
&& !string.IsNullOrEmpty(textFieldPhoneNumber.Text) && !string.IsNullOrEmpty(textFieldEmail.Text))
{
var objCollab=new Collaborators();
objCollab.FirstName = textFieldFirstName.Text;
objCollab.LastName = textFieldLastName.Text;
objCollab.Email = textFieldEmail.Text;
objCollab.PhoneNumber = textFieldPhoneNumber.Text;
GlobalConstants.Collaborators=objCollab;
PresentingViewController.PresentingViewController.DismissModalViewController(false);
}
}
GlobalConstants.cs
Public class GlobalConstants
{
public static Collaborators myCollab {get;set;}
}

Sidemenu not closing - Xamarin.iOS

I'm trying to implement a side menu in Xamarin.iOS by using this component and this example.
Everything worked fine except the part where I want to close the Side Menu.
Now it works like this: I can open the side menu either by using the LeftButton from NavigationItem or the finger swipe. But I can't close it with either of those two methods.
Does anyone have any idea why this is happening? What am I missing?
See also code below.
HomeView
public partial class HomeView : MvxViewController
{
public HomeView() : base("HomeView", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
CGRect screenRect = UIScreen.MainScreen.Bounds;
NavigationController.View.Frame = new CGRect(0, 0, screenRect.Width, screenRect.Height);
var app = UIApplication.SharedApplication.Delegate as AppDelegate;
NavigationItem.SetLeftBarButtonItem(
new UIBarButtonItem(UIImage.FromBundle("menu"),
UIBarButtonItemStyle.Plain, (sender, e) =>
{
app.SidebarController.ToggleMenu();
}), true);
}
RootView
public partial class RootView : MvxViewController
{
public RootViewModel RootViewModel
{
get { return (RootViewModel)ViewModel; }
set { ViewModel = value; }
}
public RootView() : base("RootView", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
if (ViewModel == null)
return;
var app = UIApplication.SharedApplication.Delegate as AppDelegate;
app.SidebarController = new SidebarController(this,
CreateViewFor(RootViewModel.Home, false), CreateViewFor(RootViewModel.Menu, true));
app.SidebarController.MenuWidth = 220;
app.SidebarController.ReopenOnRotate = false;
app.SidebarController.MenuLocation = MenuLocations.Left;
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
private UIViewController CreateViewFor(IMvxViewModel viewModel, bool navBarHidden)
{
var controller = new UINavigationController();
var screen = this.CreateViewControllerFor(viewModel) as UIViewController;
controller.PushViewController(screen, false);
controller.NavigationBarHidden = navBarHidden;
return controller;
}
This is how I've implemented Sidebar Navigation in my app and its working very well.
RootViewController.cs
public class RootViewController : UIViewController
{
UIStoryboard _storyboard;
// the sidebar controller for the app
public SidebarController LeftSidebarController { get; private set; }
// the navigation controller
public NavigationController NavController { get; private set; }
// the storyboard
public override UIStoryboard Storyboard {
get {
if (_storyboard == null)
_storyboard = UIStoryboard.FromName ("Main", null);
return _storyboard;
}
}
public RootViewController () : base (null, null)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var programController = (CareProgramController)Storyboard.InstantiateViewController ("careProgramController"); // This is the menu..
var leftMenuController = (LeftMenuController)Storyboard.InstantiateViewController ("leftMenuController"); // This is the initial ViewController
NavController = new NavigationController ();
NavController.PushViewController (programController, false);
LeftSidebarController = new SidebarController (this, NavController, leftMenuController) {
HasShadowing = false,
MenuWidth = 280,
MenuLocation = MenuLocations.Left
};
}
}
BaseViewController.cs
public class BaseViewController : UIViewController
{
// provide access to the sidebar controller to all inheriting controllers
protected SidebarNavigation.SidebarController SidebarController {
get {
return (UIApplication.SharedApplication.Delegate as AppDelegate).RootViewController.LeftSidebarController;
}
}
// provide access to the navigation controller to all inheriting controllers
protected NavigationController NavController {
get {
return (UIApplication.SharedApplication.Delegate as AppDelegate).RootViewController.NavController;
}
}
// provide access to the storyboard to all inheriting controllers
public override UIStoryboard Storyboard {
get {
return (UIApplication.SharedApplication.Delegate as AppDelegate).RootViewController.Storyboard;
}
}
public BaseViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
SetupNavigationBar ();
SetDrawerToogle ();
}
void SetupNavigationBar ()
{
if (NavController != null) {
NavController.NavigationBar.BarTintColor = AZConstants.PrimaryColor;
NavController.NavigationBar.Translucent = false;
NavController.NavigationBar.TintColor = UIColor.White;
NavController.NavigationBar.BarStyle = UIBarStyle.Black;
}
}
void SetDrawerToogle ()
{
NavigationItem.SetLeftBarButtonItem (
new UIBarButtonItem (UIImage.FromFile ("ic_menu_white.png").ImageWithRenderingMode (UIImageRenderingMode.AlwaysOriginal)
, UIBarButtonItemStyle.Plain
, (sender, args) => {
SidebarController.ToggleMenu ();
}), true);
}
}
Now all the ViewControllers in my app extends from BaseViewController so it'll have a navigation drawer
AppDelegate.cs
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public RootViewController RootViewController { get { return Window.RootViewController as RootViewController; } }
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, true);
GotoRootViewController();
return true;
}
public void GotoRootViewController()
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
//Window.RootViewController.DismissViewController(true, null);
//If you have defined a root view controller, set it here:
Window.RootViewController = new RootViewController();
// make the window visible
Window.MakeKeyAndVisible();
}
I managed to fix the strange behavior by adding the row below in ViewDidLoad() from RootView.cs:
View.Frame = new CGRect(0, 0, 0, 0);
For some reason it looks like the view from RootViewController was above the other two and this is the only way I managed to make it work. If anyone have a better solution, please add your answer and I'll try it. What I've done looks more like a workaround.
#HeisenBerg There is one difference between our projects, I'm using MVVMCross, which changes a bit more the flow between screens. But thank you for your help!

Does the OnActionExecuted method of an attribute always execute?

I've searched high and low and I can't seem to find a straight answer.
If I have a custom attribute/filter, will the OnActionExecuted method always be called? Even if there is an exception thrown?
At least with MVC 5, #tvanfosson's answer is no longer correct. This may apply to earlier versions as well.
OnActionExecuted is always called and has access to the thrown exception via filterContext.Exception.
Test case with exception in an action:
public class HomeController
: Controller
{
public ActionResult Index()
{
throw new Exception("Index");
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new Foo());
filters.Add(new Bar());
}
}
public class Foo
: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine($"{nameof(Foo)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine($"{nameof(Foo)}.{nameof(OnActionExecuted)}");
Debug.WriteLine($"Has exception: {filterContext.Exception != null}");
base.OnActionExecuted(filterContext);
}
}
public class Bar
: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine($"{nameof(Bar)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine($"{nameof(Bar)}.{nameof(OnActionExecuted)}");
Debug.WriteLine($"Has exception: {filterContext.Exception != null}");
base.OnActionExecuted(filterContext);
}
}
Output:
Bar.OnActionExecuting
Foo.OnActionExecuting
Exception thrown: 'System.Exception' in WebApplication1.dll
Foo.OnActionExecuted
Has exception: True
Bar.OnActionExecuted
Has exception: True
Test case with exception in a filter
public class HomeController
: Controller
{
public ActionResult Index()
{
return new HttpStatusCodeResult(200);
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new Foo());
filters.Add(new Bar());
}
}
public class Foo
: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine($"{nameof(Foo)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine($"{nameof(Foo)}.{nameof(OnActionExecuted)}");
Debug.WriteLine($"Has exception: {filterContext.Exception != null}");
throw new Exception("Foo");
base.OnActionExecuted(filterContext);
}
}
public class Bar
: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine($"{nameof(Bar)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine($"{nameof(Bar)}.{nameof(OnActionExecuted)}");
Debug.WriteLine($"Has exception: {filterContext.Exception != null}");
base.OnActionExecuted(filterContext);
}
}
Output:
Bar.OnActionExecuting
Foo.OnActionExecuting
Foo.OnActionExecuted
Has exception: False
Exception thrown: 'System.Exception' in WebApplication1.dll
Bar.OnActionExecuted
Has exception: True

Resources