MvvmLight unable to create a controller for key - ios

I am designing a cross platform application architecture using Xamarin iOS and Xamarin Android I decided to go with MvvmLight, it looks descent and is not hiding everything from the MVVM pattern, good and flexible.
While everything started to make sense trying to set it up and learn how to use it, I find myself difficult to understand why I get the following error.
Unable to create a controller for key ChartsPage
The setup.
In a PCL I have my ViewModels. I have a ViewModelLocator setup. I use the mvvmlightlibs Nuget Package.
public class ViewModelLocator
{
public static readonly string SchedulerPageKey = #"SchedulerPage";
public static readonly string ChartsPageKey = #"ChartsPage";
[SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SchedulerViewModel Scheduler
{
get
{
return ServiceLocator.Current.GetInstance<SchedulerViewModel>();
}
}
public BizchartsViewModel Bizcharts
{
get
{
return ServiceLocator.Current.GetInstance<BizchartsViewModel>();
}
}
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Haven't declared something yet
}
else
{
// Haven't declared something yet
}
SimpleIoc.Default.Register<SchedulerViewModel>();
SimpleIoc.Default.Register<BizchartsViewModel>();
}
}
The I have a unified iOS application using universal storyboard with size classes which has an initial UINavigationViewController SchedulerViewController and in the ViewDidLoad method I test the navigation to BizchartsViewController with 3 seconds delay. After 3 seconds I get the exceptions.
In the AppDelegate.
private static ViewModelLocator _locator;
public static ViewModelLocator Locator
{
get
{
if (_locator == null)
{
SimpleIoc.Default.Register<IDialogService, DialogService>();
_locator = new ViewModelLocator();
}
return _locator;
}
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
var nav = new NavigationService();
nav.Initialize((UINavigationController)Window.RootViewController);
nav.Configure(ViewModelLocator.ChartsPageKey, typeof(BizchartsViewController));
SimpleIoc.Default.Register<INavigationService>(() => nav);
return true;
}
The SchedulerViewController.
partial class SchedulerViewController : UIViewController
{
public SchedulerViewModel Vm {
get;
private set;
}
public SchedulerViewController (IntPtr handle) : base (handle)
{
Vm = AppDelegate.Locator.Scheduler;
}
public async override void ViewDidLoad ()
{
base.ViewDidLoad ();
await Task.Delay (3000);
Vm.NavigateToCharts ();
}
}
The SchedulerViewModel.
public class SchedulerViewModel : ViewModelBase
{
public void NavigateToCharts()
{
var nav = ServiceLocator.Current.GetInstance<INavigationService>();
nav.NavigateTo(ViewModelLocator.ChartsPageKey);
}
}
I definitely miss a detail somewhere!!!

If you follow carefully the blog post here, it says that with Storyboard you should use the string overload and not the typeof() in nav.Configure(Key, ViewController) and always set the storyboardId and restorationId in the Storyboard ViewController.
Note that because we are using a Storyboard, you must make sure to use
the Configure(string, string) overload, and NOT the Configure(string,
Type) one.

Related

NullReference when Show a MvxTabBarViewController mvvmcross ios

I have a storyboard made on xCode, and know I'm working on visual studio with Xamarin and mvvmcross, to do the IOS and the Android app with the same Core.
My StoryBoard have some views (including a SideBar on some of the initial views) that will open 4 views that will call ShowViewModel for a MvxTabBarViewController, and each one of that view will open a different tab.
All the Navigation to that 4 view, its done, now I have problems Showing the Tab Bar...
When I try to do a simple test of call ShowViewModel to MvxTabBarViewController and open a specific tab, on the ViewDidLoad of that tab, I have the error
System.NullReferenceException: Object reference not set to an instance
of an object
The structure of my Tab bar was all created on the storyboard.
Here is the code of my TabBarController:
[MvxFromStoryboard("Main")]
[MvxSidebarPresentation(MvxPanelEnum.Center, MvxPanelHintType.PushPanel, true)]
public partial class TabDetailView : MvxTabBarViewController<TabDetailViewModel>
{
private bool _constructed;
public TabDetailView(IntPtr handle) : base(handle)
{
_constructed = true;
// need this additional call to ViewDidLoad because UIkit creates the view before the C# hierarchy has been constructed
ViewDidLoad();
}
public TabDetailView()
{
_constructed = true;
// need this additional call to ViewDidLoad because UIkit creates the view before the C# hierarchy has been constructed
ViewDidLoad();
}
public override void ViewDidLoad()
{
if (!_constructed)
return;
base.ViewDidLoad();
var vm = (TabDetailViewModel)this.ViewModel;
if (vm == null)
return;
}
}
And Here is the code of my ViewController corresponding to the Tab that I want to open:
[MvxFromStoryboard("Main")]
[MvxSidebarPresentation(MvxPanelEnum.Center, MvxPanelHintType.PushPanel, true)]
public partial class TabDiaryView : MvxViewController<TabDiaryViewModel>
{
public TabDiaryView (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad(); //HERE IS THE ERROR (NULLREFERENCE)
}
}
public class Setup : MvxIosSetup
{
public Setup(MvxApplicationDelegate applicationDelegate, UIWindow window)
: base(applicationDelegate, window)
{
}
public Setup(MvxApplicationDelegate applicationDelegate, IMvxIosViewPresenter presenter)
: base(applicationDelegate, presenter)
{
}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
protected override IMvxIosViewPresenter CreatePresenter()
{
return new MvxSidebarPresenter((MvxApplicationDelegate)ApplicationDelegate, Window);
}
}
Please help me!!

Dagger 2: how to change provided dependencies at runtime

In order to learn Dagger 2 i decided to rewrite my application but I'm stuck with finding the proper solution for the following problem.
For the purpose of this example let's assume we have an interface called Mode:
public interface Mode {
Object1 obj1();
//some other methods providing objects for app
}
and two implementations:
NormalMode and DemoMode.
Mode is stored in singleton so it could be accessed from anywhere within application.
public enum ModeManager {
INSTANCE,;
private Mode mode;
public Mode mode() {
if (mode == null)
mode = new NormalMode();
return mode;
}
public void mode(Mode mode) { //to switch modules at runtime
this.mode = mode;
}
}
The NormalMode is switched to DemoMode at runtime (let's say, when user clickcs on background couple of times)
public void backgroundClicked5Times(){
ModeManager.INSTANCE.mode(new DemoMode());
//from now on every object that uses Mode will get Demo implementations, great!
}
So first I got rid of the singleton and defined Modes as Dagger 2 modules:
#Module
public class NormalModeModule {
#Provides
public Object1 provideObject1() {
return new NormalObject1();
}
}
#Module
public class DemoModeModule {
#Provides
public Object1 provideObject1() {
return new DemoObject1();
}
}
Now in the method backgroundClicked5Times instead of dealing with singleton I would like to replace NormalModeModule with DemoModeModule in DAG so the other classes that need Object1 would get a DemoObject1 implementation from now on.
How can I do that in Dagger?
Thanks in advance.
Maybe you can consider using multibindings?
#Module
public class NormalModeModule {
#Provides
#IntoMap
#StringKey("normal")
public Object1 provideObject1() {
return new NormalObject1();
}
}
#Module
public class DemoModeModule {
#Provides
#IntoMap
#StringKey("demo")
public Object1 provideObject1() {
return new DemoObject1();
}
}
and when using Mode:
#Inject
Map<String, Mode> modes;
//or you perfer lazy initialization:
Map<String, Provider<Mode>> modes;
public void backgroundClicked5Times(){
ModeManager.INSTANCE.mode(modes.get("demo"));
//if you are using Provider:
ModeManager.INSTANCE.mode(modes.get("demo").get());
//from now on every object that uses Mode will get Demo implementations, great!
}
Having experimented with dagger for a while I came up with solution that seems to be working well in my use case.
Define class that will hold state information about mode
public class Conf {
public Mode mode;
public Conf(Mode mode) {
this.mode = mode;
}
public enum Mode {
NORMAL, DEMO
}
}
Provide singleton instance of Conf in Module
#Module
public class ConfModule {
#Provides
#Singleton
Conf provideConf() {
return new Conf(Conf.Mode.NORMAL);
}
}
Add module to AppComponent
#Singleton
#Component(modules = {AppModule.class, ConfModule.class})
public interface AppComponent {
//...
}
Define modules that provide different objects based on Mode
#Module
public class Object1Module {
#Provides
Object1 provideObject1(Conf conf) {
if (conf.mode == Conf.Mode.NORMAL)
return new NormalObject1();
else
return new DemoObject1();
}
}
To switch mode at runtime simply inject Conf object and modify it:
public class MyActivity extends Activity {
#Inject Conf conf;
//...
public void backgroundClicked5Times(){
conf.mode = Conf.Mode.DEMO;
//if you have dagger objects in this class that depend on Mode
//execute inject() once more to refresh them
}
}

How to configure ninject to inject different dependency types into the same class?

I've run into what I believe must be a common dependency injection-related problem. I'm having trouble finding relevant examples, and I do not like the best solution I've been able to come up with.
public class WasherDryerFolderSystem : ILaundrySystem
{
private IWasher _washer;
private IDryer _dryer;
private IFolder _folder;
public WasherDryerFolderSystem(IWasher washer, IDryer dryer, IFolder folder)
{...}
public void DoLaundry()
{
_washer.Wash();
_dryer.Dry();
_folder.Fold();
}
}
public class HandWasher : IWasher {...}
public class MachineWasher : IWasher {...}
public class HandDryer : IDryer {...}
public class MachineDryer : IDryer {...}
public class HandFolder : IFolder {...}
public class MachineFolder : IFolder {...}
Now in the main app I have something like
var laundrySystem = _kernel.Get<ILaundrySystem>(someUserInput);
What is a good way to configure the bindings required for something like this? Here's what I've been able to come up with thus far (that I don't like):
Bind<ILaundrySystem>().To<WasherDryerFolderSystem>()
.Named(MACHINEWASH_HANDDRY_HANDFOLD)
.WithConstructorArgument("washer", new MachineWasher())
.WithConstructorArgument("dryer", new HandDryer())
.WithConstructorArgument("folder", new HandFolder());
At first I didn't think this looked too bad, but when Washers and Dryers and Folders all have their own dependencies, this quickly gets ugly.
This feels to me like it should be a common problem, but I'm not finding anything that's much help. Do I have something designed incorrectly?
You could use a factory pattern:
public interface ILaundrySystemFactory
{
ILaundrySystem Create(string someUserInput);
}
public class LaundrySystemFactory : ILaundrySystemFactory
{
private readonly IKernel _kernel;
public LaundrySystemFactory(IKernel kernel){
_kernel = kernel;
}
public ILaundrySystem Create(string someUserInput)
{
if(someUserInput){
var washer = _kernel.Get<MachineWasher>();
var dryer = _kernel.Get<HandDryer>();
var folder = _kernel.Get<HandFolder>();
} else {
var washer = _kernel.Get<DifferentWasher>();
var dryer = _kernel.Get<DifferentDryer>();
var folder = _kernel.Get<DifferentFolder>();
}
return new WasherDryerFolderSystem(washer, dryer, folder);
}
}
and then simply
private readonly ILaundrySystemFactory _laundrySystemFactory;
ctor(ILaundrySystemFactory laundrySystemFactory){
_laundrySystemFactory = laundrySystemFactory;
}
public UserInputMethod(string someUserInput)
{
var loundrySystem = laundrySystemFactory.Create(someUserInput);
var loundry = loundrySystem.DoLaundry();
}
bindings:
Bind<ILaundrySystemFactory>().To<LaundrySystemFactory>();
(some DI containers might also need something like:)
Bind<MachineWasher>().To<MachineWasher>();
Make a concrete classes with concrete parameters you need, put them as dependencies of strategy which will use them based on user input. Next instantiate them all with SINGLE call for a resolution root class. OFC Strategy can be the resolution root itself but it also can be a dependency of different resoultion root. Example:
//DoLaundry based on user input
public class WasherDryerFolderSystemStrategy
{
ctor(MachineWashingHandDringHandFoldingSystem first,
MachineWashingHandDringHandFoldingSystem second,
HandWashingHandDringHandFoldingWithBreakfastSystem third) { ... }
public void DoLaundry(int userInput)
{
if(userInput == 1)
first.DoLaundry();
if(userInput == 2)
second.DoLaundry();
if(userInput == 3)
third.DoLaundry();
}
}
// MACHINEWASH_HANDDRY_HANDFOLD
public class MachineWashingHandDringHandFoldingSystem : WasherDryerFolderSystem
{
public MachineWashingHandDringHandFoldingSystem
(MachineWasher machineWasher, HandDryer handDryer, HandFolder handFolder) :
base(machineWasher, handDryer, handFolder)
{
}
}
// HANDWASH_HANDDRY_HANDNOFOLD
public class HandWashingHandDringHandFoldingSystem : WasherDryerFolderSystem
{
public MachineWashingHandDringHandFoldingSystem
(HandWasher machineWasher, HandDryer handDryer, HandFolder handFolder) :
base(machineWasher, handDryer, handFolder)
{
}
}
// HANDWASH_HANDDRY_HANDNOFOLD_WITHBREAKFAST
public class HandWashingHandDringHandFoldingWithBreakfastSystem : WasherDryerFolderSystem
{
private readonly BreakfastMaker breakfastMaker
public MachineWashingHandDringHandFoldingSystem
(HandWasher machineWasher, HandDryer handDryer, HandFolder handFolder, BreakfastMaker brekfastMaker) :
base(machineWasher, handDryer, handFolder)
{
this.breakfastMaker = breakfastMaker
}
public overide void DoLaundry()
{
base.DoLaundry();
brekfastMaker.AndMakeChipBreakAsWell();
}
}
Please note that the implementation above does not require any Ninject configuration. Ninject will autobind everything ToSelf() with the first use (as long as it is not an interface).
In general as long as you do not need some sort of composite/bulk operations with with multiple implementations, than you should avoid interface bindings (and interfaces at all). Composite like operation example:
// original WasherDryerFolderSystem refactored
public class WasherDryerFolderSystem
{
private IEnumerable<IWasher> washers;
private IEnumerable<IDryer> dryers;
private IEnumerable<IFolder> folders;
public WasherDryerFolderSystem(
IWasher[] washers, IDryer[] dryers, IFolder[] folders)
{
this.washers = washers;
this.dryers = dryers;
this.folders = folders;
}
// all inclusive
public virtual void DoLaundry()
{
foreach (var washer in washers)
washer.Wash();
foreach (var dryer in dryers)
dryer.Dry();
foreach (var folder in folders)
folder.Fold();
}
}
I hope that helps.

MvvmCross ViewModel transition from the left

I am developing an app for iOS using MvvmCross. On one of my Views I have some basic report data that is displayed in a tableview.
When the table row is touched a new view containing a detail report is displayed by making the call to ShowViewModel passing some parameters in a Dictionary. This works fine.
When the user swipes left or right the app needs to show the detail report for the next or previous item in the original list. I am doing this by updating some parameters and calling ShowViewModel again. The logic behind this is all working fine.
My problem; ShowViewModel animates the new view coming in from the right. This is perfect when the user has swiped left. However when swiping right it seems counter intuitive. How can I make ShowViewModel animate or transition in from the left side?
if you look to the MvvmCross source code here you see how the default behavior is showing the ViewControllers
You need to change that by doing something like the following:
How to change the Push and Pop animations in a navigation based app
for that, one idea is to have a custom view presenter and catch navigation to that particular view-model (override Show(IMvxTouchView view) )
or, maybe derive from UINavigationController, set it to MvvmCross to use it (look to the MvxSetup), and on some events change transition to that particular view
similar to this question
How to specify view transitions on iPhone
This is the solution I was able to come up with following the helpful pointers in the answer from Andrei N. In the end I opted for a TransitionFlipFromRight and TransitionFlipFromLeft when scrolling between detail reports. Hopefully it is useful to somebody else.
I already had a presenter class that was inherited from MvxModalSupportTouchViewPresenter
public class BedfordViewPresenter : MvxModalSupportTouchViewPresenter
Within this class I added a property of MvxPresentationHint.
private MvxPresentationHint _presentationHint;
In the override of method ChangePresentation the above property is used to store the passed in parameter
public override void ChangePresentation (MvxPresentationHint hint)
{
_presentationHint = hint;
base.ChangePresentation (hint);
}
Two new MvxPresentationHint class were declared (see later)
In the presenter class the Show method was overridden
public override void Show(IMvxTouchView view)
{
if (_presentationHint is FlipFromRightPresentationHint) {
var viewController = view as UIViewController;
MasterNavigationController.PushControllerWithTransition (viewController, UIViewAnimationOptions.TransitionFlipFromRight);
}
else
if (_presentationHint is FlipFromLeftPresentationHint) {
var viewController = view as UIViewController;
MasterNavigationController.PushControllerWithTransition (viewController, UIViewAnimationOptions.TransitionFlipFromLeft);
}
else {
base.Show (view);
}
_presentationHint = null;
}
A new class that provides extensions to a UINavigationController was created with the method PushControllerWithTransition
public static class UINavigationControllerExtensions
{
public static void PushControllerWithTransition(this UINavigationController
target, UIViewController controllerToPush,
UIViewAnimationOptions transition)
{
UIView.Transition(target.View, 0.75d, transition, delegate() {
target.PushViewController(controllerToPush, false);
}, null);
}
}
All that needs to be defined now are the two new MvxPresentationHint class derivations. These belong in your Core class library project rather than the iOS application project.
public class FlipFromLeftPresentationHint : MvxPresentationHint
{
public FlipFromLeftPresentationHint ()
{
}
}
and
public class FlipFromRightPresentationHint: MvxPresentationHint
{
public FlipFromRightPresentationHint ()
{
}
}
I hope this is a help to someone else trying to do something similar
Share my solution for android:
On view:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
var layout = view.FindViewById<LinearLayout>(Resource.Id.swippeable);
var swipeListener = new SwipeListener(this.Activity);
swipeListener.OnSwipeLeft += (sender, e) => this.ViewModel.LeftCommand?.Execute(); //Here use command into view model
swipeListener.OnSwipeRight += (sender, e) => this.ViewModel.RightCommand?.Execute();
layout.SetOnTouchListener(swipeListener);
return view;
}
Gesture listener:
public class SwipeListener : SimpleOnGestureListener, View.IOnTouchListener
{
private const int SWIPE_THRESHOLD = 100;
private const int SWIPE_VELOCITY_THRESHOLD = 100;
private readonly GestureDetector gestureDetector;
public SwipeListener(Context ctx)
{
this.gestureDetector = new GestureDetector(ctx, this);
}
public Boolean OnTouch(View v, MotionEvent e)
{
return this.gestureDetector.OnTouchEvent(e);
}
public event EventHandler OnSwipeRight;
public event EventHandler OnSwipeLeft;
public event EventHandler OnSwipeTop;
public event EventHandler OnSwipeBottom;
public override Boolean OnDown(MotionEvent e)
{
return true;
}
public override Boolean OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
Boolean result = false;
float diffY = e2.GetY() - e1.GetY();
float diffX = e2.GetX() - e1.GetX();
if (Math.Abs(diffX) > Math.Abs(diffY))
{
if (Math.Abs(diffX) > SWIPE_THRESHOLD && Math.Abs(velocityX) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffX > 0)
{
SwipeRight();
}
else
{
SwipeLeft();
}
result = true;
}
}
else if (Math.Abs(diffY) > SWIPE_THRESHOLD && Math.Abs(velocityY) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffY > 0)
{
SwipeBottom();
}
else
{
SwipeTop();
}
result = true;
}
return result;
}
public void SwipeRight()
{
this.OnSwipeRight?.Invoke(this, EventArgs.Empty);
}
public void SwipeLeft()
{
this.OnSwipeLeft?.Invoke(this, EventArgs.Empty);
}
public void SwipeTop()
{
this.OnSwipeTop?.Invoke(this, EventArgs.Empty);
}
public void SwipeBottom()
{
this.OnSwipeBottom?.Invoke(this, EventArgs.Empty);
}
}

How can i call dynamic object like a function?

I am studying orchard architecture.i have faced with a strange concept in display management section.
in Partial view page there is a 'function call like' syntax like so Display(Model.Head). that is not a function thought, it is a dynamic object defined in orchard WebViewPage.
I am wondering how to define a dynamic object so that you can call it (and pass it an argument as well) like a function as i mentioned.
thanks in advance.
A lighter weight way to do it without clay would be to subclass the built-in DynamicObject class.
public static dynamic Display;
void Main()
{
Display = new MyCallableObject();
//this is what i was after
Console.Write(Display("bla bla bla"));
}
public class MyCallableObject:DynamicObject
{
public override bool TryInvoke(InvokeBinder binder, object[] args, out Object result)
{
result = string.Format("This is response for {0}",args.FirstOrDefault());
return true;
}
}
I finally found it my self!
all the operations have done with Clay Library behind the scene.i have wrote a sample console app for demonstrating the process.
class Program
{
static void Main(string[] args)
{
Display = ClayActivator.CreateInstance<MyResponser>(new List<IClayBehavior> {new MyFunctionCallBehavior()});
//this is what i was after
Console.Write(Display("bla bla bla"));
}
public static dynamic Display;
}
public class MyFunctionCallBehavior : IClayBehavior
{
public object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
{
return ((MyResponser)self).ResponseForRequest(args.FirstOrDefault().ToString());
}
}
public class MyResponser
{
public string ResponseForRequest(string param)
{
return string.Format("This is response for {0}",param);
}
}

Resources