MvvmCross - View not loaded - ios

I upgraded an old project to Xamarin.iOS Unified and MvvmCross 4.1.
When I run the app, I get the following exception:
MvvmCross.Platform.Exceptions.MvxException: View not loaded for MyView
My classes look like:
MyView : MvxViewController<MyViewModel> {}
MvxViewController<T> : UIViewController, IMvxBindingContextOwner, IUIWrappable
where T : ViewModelBase
ViewModelBase : MvxViewModel {}
I suspect I'm missing an interface or something on MyView to enable MvvmCross to operate correctly but I'm not sure what interface this might be.
I suspect this because I already had to hard code in mappings between the View and it's ViewModel, to enable MvvmCross to find the ViewModel through reflection/auto-discovery associated with MyView. That is, before this error, I was getting an unable to find associated ViewModel error.
If I make MyView implement IMvxIosView, this error goes away, and I then get a null reference on the ViewModel later on in my code, so somewhere that is not getting instantiated where it needs to. My impression was this should all be handled automatically by MvvmCross, but I might be wrong.
I'm looking at samples such as https://github.com/MvvmCross/MvvmCross/wiki/Tip-Calc-A-Xamarin.iOS-UI-project
where TipView contains
public new TipViewModel ViewModel {
get { return (TipViewModel) base.ViewModel; }
set { base.ViewModel = value; }
}
MyView's base has no such ViewModel property. What am I missing to have access to this?
TipCalc's sample code is still referencing the Cirrious namespace, so it's possible things have changed.
Any suggestions are appreciated. I'm new to Xamarin and MvvmCross (although experienced in Windows/iOS development), and I'm having trouble finding up to date documentation.

When creating MVVMCross ViewModels and Views,there are some requirements
1.ViewModel and View needs to have same name until "View"
eg: MyView , MyViewModel
2.You need to load stroryboard name to the Viewcontroller.
eg:namespace [MvxFromStoryboard("MyView")]
public partial class MyView:MvxViewController where TViewModel :MyViewModel
3. When loading View, you need to have default constructor.
public MyView(IntPtr handle) : base(handle){}
4.You need to bind viewmodel to the view.Most of the time in ViewDidLoad
method in ViewClass
var set = this.CreateBindingSet<MyView, MyViewModel >();
set.Apply();

Try doing this:
var vc = this.CreateViewControllerFor(MvxViewModelRequest.GetDefaultRequest(viewModelType);) as MvxViewController;
vc.OnViewCreate();
The vc.OnViewCreate(); should ensure your View is loaded.
And the line:
this.CreateViewControllerFor(MvxViewModelRequest.GetDefaultRequest(viewModelType);) as MvxViewController;
Should make sure that the ViewController is created with the ViewModel already set. That way you don't have to set it manually.
That method is an extension method defined in the MvxCanCreateIosViewExtensionMethods class https://github.com/MvvmCross/MvvmCross/blob/4.0/MvvmCross/iOS/iOS/Views/MvxCanCreateIosViewExtensionMethods.cs
-- Edit --
Another thing I noticed in your sample code is this line:
MyView : MvxViewController<MyView> {}
The type parameter passed into MvxViewController<T> should be the associated ViewModel for MyView, not MyView itself. So for example, you might have something like:
MyViewModel : ViewModelBase {}
and then:
MyView : MvxViewController<MyViewModel> {}
-- UPDATE --
You should be inheriting from the existing MvxViewController provided with MvvmCross. https://github.com/MvvmCross/MvvmCross/blob/3c735adc534a5df2d4730e9d58a08f7863c30cee/MvvmCross/iOS/iOS/Views/MvxViewController.cs

Implementing IMvxIosView solved this particular error.
It looks like MvvmCross can't find the view unless it implements this interface.
Now I'm on to other errors.

Related

How do I chain SourceList observation using ReactiveUI and DynamicData?

Apologies if the terminology is off; I'm an iOS developer having to use Xamarin.iOS to develop an app. I'm using ReactiveUI with DynamicData and an MVVM architecture. I'm fairly happy with RxSwift, and FRP concepts in general. I have a Model that publishes a SourceList<MyThing>, according to the docs, like so:
// Property declarations
private readonly SourceList<MyThing> Things;
public IObservableCollection<MyThing> ThingsBindable { get; }
// Later, in the constructor...
Things = new SourceList<MyThing>();
// Is this of the right type?
ThingsBindable = new ObservableCollectionExtended<MyThing>();
Things
.Connect()
.Bind(ThingsBindable)
.Subscribe();
I can successfully use .BindTo() in my View (i.e. ViewController in iOS-land) to get a UITableView to update when the Model changes:
Model
.WhenAnyValue(model => model.ThingsBindable)
.BindTo<MyThing, MyThingTableViewCell>(
tableView,
new NSString("ThingCellIdentifier"),
46, // Cell height
cell => cell.Initialize());
I'd like, instead of binding directly to the Model, to have the ViewModel subscribe-and-publish (or otherwise proxy) the SourceList<MyThing>, or the bindable version of this, so that the View is only using the ViewModel properties. The SourceList is declared private in the docs; I'm unsure of best practice here: do I make it public and do my Connect() in the ViewModel? Or is there a way of passing on the publicly exposed IObservableCollection<MyThing> ThingsBindable from the ViewModel? I'm also not convinced that ObservableCollectionExtended<MyThing> is the right type for the Bindable property, but it seems to work.
I've tried various combinations of .ToProperty(), .Bind(), .Publish() etc. and making a version of the View-binding Observable in the ViewModel to no avail and am now just throwing autocomplete at the wall to see what sticks. Any direction appreciated. TIA.
I think it was beginners misunderstanding. Here's what I've got working the way I want; maybe it will help other Xamarin.iOS/ReactiveUI/DynamicData newbies.
In my model I declare both a private SourceList and a publicly exposed IObservableList<MyThing>:
private readonly SourceList<MyThing> _ModelThings;
public IObservableList<MyThing> ModelThings;
Then instantiate them in my constructor:
_ModelThings = new SourceList<MyThing>();
ModelThings = _Things.AsObservableList();
In my ViewModel I declare a local ObservableCollectionExtended<MyThing> and bind that to the Model's public property:
public ObservableCollectionExtended<MyThing> ViewModelThings;
// Then, in the constructor:
ViewModelThings = new ObservableCollectionExtended<MyThing>();
model.ModelThings
.Connect()
.Bind(ViewModelThings)
.Subscribe();
In my ViewController I bind the table to the ViewModel.ViewModelThings, as in the question. If I wanted to have another level of Model I could simply pass through the Model.ModelThings and .Connect().Bind() lower down, as Glenn hinted in his comment.
FWIW, I found Roland's Blog (specifically the sections on Observable Lists/Caches) to be more straightforward to understand than the GitHub docs.

Aurelia Singleton View model

Within an Aurelia project I have a view model that I want to maintain state between router navigation. I thought that adding #singleton() to my view model class would accomplish this.
In fact, I have created a simple Aurelia project where this works. I am able to navigate away from and back to the same page and state is maintained. My constructor is only called the first time I navigate to that page.
import { singleton } from 'aurelia-framework';
#singleton()
export class Welcome {
heading = 'Welcome to the Aurelia Navigation App!';
constructor() {
console.log('constructor');
}
activate() {
console.log('activate');
}
attached() {
console.log('attached');
}
}
However, in my larger application this is not working. I add the decorator and my view model's constructor is still called the second time I navigate to that page. (I have even copied this view model into my larger application and it is not treated as a singleton.)
Obviously something must be different between these two projects. However, I don't see any difference. Is there a setting I may have set that would override the behavior of #singleton()?
It turns out the solution was to jspm update. I had thought that deleting my jspm_packages and running jspm install would be equivalent. But it is not.
Now that my Aurelia modules are up to date the singleton() decorator works fine.
You likely have multiple instances of the singleton custom element - singleton by definition cannot have multiple instances of itself. What you want is a view model that is created once, and reused.
Here's a solution that I used successfully. The idea is to have a global view model instance that is constructed once, and there is a controller view that uses that to compose the view/vm.
HTML
<template>
<require from="yourView.html"></require>
<p> parentClass.html </p>
<compose view="yourView.html" view-model.bind="singletonViewModel"></compose>
</template>
JS
import { YourView } from 'yourView';
export class parentClass {
// constructor for parentClass will run multiple times.
constructor() {
/** this is the key, you must instantiate a custom view model once
on some globally singleton object like window or global in Node */
if ( typeof window.yourView === 'undefined' ) {
window.yourView = new YourView();
}
}
bind() {
this.singletonViewModel = window.yourView;
}
}
Everything you do within this view model will persist now between navigations and constructor will only be called once. attached() and detached() for the singleton view model will still activate as you would expect.
Also note that you can use this to cache multiple instances of a view models that you can switch between. My application has a global service to keep track of multiple VMs that constructs/returns the VM as requested. Advantage this brings is I can do heavy processing once at the constructor stage then never worry about it again.

How to use a Custom iOS View(located in a referenced Class Library) in an Interface Builder file?

I have two projects. The first one is the iOS-App which is referencing a class library with the target framework Xamarin.iOS. In that class library I implemented a CustomView which inherits from UIView. I also registered it with the "Register" attribute.
So in the interface designer I get the prefilled information to use this class as custom class after I dragged the UIView to my ViewController. But when I run the app I get the following information:
"Unknown class MyCustomView in Interface Builder file."
If I move the my CustomView from the class library project into my first project then it will run as expected.
Does someone know how to use a CustomView, which is located in a referenced class library, in an Interface Builder file?
I don't think it's possible to Register accross project boundaries. In my tests, even when you use the exact same namespace, you still end up with an error.
What is possible, on the other hand, is to use a proxy class and make use of inheritance to bring the desired behaviour to the proxy.
Basically, it boils down to this:
1) In your library, you define your Custom UIView and implement any behaviour you want to share:
public class MyCustomUIView : UIView
{
public MyCustomUIView(IntPtr ptr) : base (ptr)
{
}
public override void AwakeFromNib()
{
// Do something beautiful here...
AddSubview(new UILabel(new CGRect(0,0,200,30)) { Text = "I live in the library"});
BackgroundColor = UIColor.Green;
}
}
Note that we don't Registerthis class, because it won't work anyway.
2) Now in your app project, open up the storyboard in interface designer or XCode and assign a completely new name to your placeholder UIView. In my case, I chose MyCustomUIViewProxy. Xamarin studio will create two files for you: MyCustomUIViewProxy.cs and MyCustomUIViewProxy2.designer.cs. Note that in the designer.cs file, Xamarin automatically Registered your proxy class.
3) Now all we have to do is use inheritance, to make the proxy act like the original: In MyCustomUIViewProxy.cs, we change the base class from UIView to MyCustomUIView, and we're good to go:
partial class MyCustomUIViewProxy : MyCustomUIView
{
public MyCustomUIViewProxy (IntPtr handle) : base (handle)
{
}
}
That's it:

Custom control without Adapter. Binding List

I created a Custom Control for monodroid. Following the tutorial N-20 CustomControl and this MvxListView because my control binding a IEnumerable.
My control inherits to FrameLayout, then I don't have access to Adapter property from Parent Class.
When I assign the List binding property and call the RaisePropertyChanged event. Doesn't raise up. How can I do that?
Thanks in advance.
Edit to show code
Talk is cheap, I Show the code.
This is the header of custom control and the list of binding.
public class DrawingBoardControl : View
{
private DrawingItems m_drawingItems;
[MvxSetToNullAfterBinding]
public DrawingItems CanvasItems
{
get
{
return m_drawingItems;
}
set
{
m_drawingItems = value;
this.Update();
}
}
...
I use a class called "DrawingItems", there are
public class DrawingItems : IEnumerable<IDrawingElement>
{
private List<IDrawingElement> myDrawingItems = new List<IDrawingElement>();
public IEnumerator<IDrawingElement> GetEnumerator()
{
return myDrawingItems.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public void Add(IDrawingElement element)
{
myDrawingItems.Add(element);
}
}
However, in other custom controls I use a IEnumerable and generic List and the problem persist.
When, I use RaisePropertyChanged in my ViewModel, the Items property don't raise.
Sorry for not include more information yesterday
There are always some answers for uncomplete questions:
If you cannot access the Adapter of the parent, the parent has to set the context of the child. There are no more options. Make your custom control fully bindable by exposing the complete interfaces. The list has to provide the context for the list items.
Somehow a customcontrol binding to a IEnumerable feels very wrong. A custom control for displaying a list item, I can understand, but not for a list. But of course it can be done. But you end up setting references to the container class en setting the datacontext or viewmodel of each item.
But please provide more info if you want others to help.
UPDATE:
Then I also update :) This provides some clues of the problem:
Don't assign a new list, but inherit the this from ObservableCollection, or manually call the raiseProperty with the correct parameters in the setter. I think it is by design assigning a new list will not call PropertyRaised.
As the 'App is King'-rule: in this case I notify the propertychanged myself. It might have something to do with the weak references problem (see the attribute on the ListItem property).

Registration in IoC container after initial configuration

I have a scenario where I would like to register a single instance of a component in the container, but unfortunately it cannot be created at application startup.
This component could only be instantiated passing some objects which are only available a bit later in application lifecycle (they are not other IoC registered services, however) [see note below].
Is registering a component in a IoC container after the initial configuration (run in app startup) a bad practice?
How to accomplish it without directly referencing the container? Should I abstract a registration service?
There is a better approach to support the scenario?
NOTE about the actual scenario
The component I would like to put in the container is initialized with a particular instance of an UI control (it is basically an adapter), hence I have to manually create the component instance and register it in the container.
I would have done this at application startup, but unfortunately I don't have the UI control instance available yet (nor can I create it by myself).
Even at later time, I cannot reach the UI control instance from the surface of other components without knowing their concrete class.
For this reason I thought I could put the responsibility for the adapter registration into the class which owns the UI control.
My initial scenario:
public interface IDockManager { ... }
public class AcmeDockManagerAdapter : IDockManager {
public AcmeDockManager(DockControl control) { ... }
...
}
public class ShellViewModel { ... }
public class ShellView : Window {
internal DockControl theDockControl;
}
public class AnotherViewModel {
AnotherViewModel(IDockManager dockManager) { ... }
}
The solution I'm unconfortable with:
public class ShellView : Window {
internal DockControl theDockControl;
public ShellView () {
InitializeComponents();
var dockManager = new AcmeDockManagerAdapter(theDockControl);
//registration in the container
}
}
You could register a "lazy wrapper" instead. Such a wrapper implements the same interface and can be instantiated immediately, but will internally postpone the creation of the actual component that does the work. Take a look at ploeh's example of a LazyOrderShipper or LazyOrderShipper2.
edit: If I understand correctly, you're just trying to connect your views to your viewmodels, MVVM-style. I prefer to let the container handle viewmodel construction, but to do the view construction and viewmodel wiring myself. My start-up code woul look like this:
var mainViewModel = container.Get<MainViewModel>();
var mainView = new MainView(mainViewModel);
Application.Run(mainView);
And inside the MainView constructor I'd take care of child controls which require their own viewmodel:
public MainView(MainViewModel viewModel)
{
// link "subviews" to "subviewmodels"
this.SomeChildControl.ViewModel = viewModel.SomeChildViewModel;
// normal MVVM property wiring
viewModel.TitleChanged += delegate { this.Text = viewModel.Title; };
...
}
If you strictly follow the MVVM approach, then you should not have to register any view with the container. Anything that "needs to talk to the view" really needs to talk to underlying viewmodel instead. (Things get more interesting when you want to allow for pluggable views in tabbed interface or docked window GUI, but that's another story.)
The solution the way I understand the question, is relatively simple - provide theDockControl from outside. I know that's messing with autogenerated WinForms/WPF/whatever-you're-using crap, but I'm afraid there's no pretty solutions here.

Resources