MVVMCross BindingContext is Null when not using MvxContentPage - xamarin.forms

I have an issue where several of my pages are using the SfBackdropPage control from Syncfusion. This control requires the page to have a base of SfBackdropPage and not the usual ContentPage otherwise it just won't work.
To make MVVMCross work, the page needs have a base such as MvxContentPage
You see where this is going ?
I opened a ticket with Syncfusion to see if they could work around this issue but basically they just said its not possible. So since I needed to have that control as part of the page I had to leave the base as it was.
How do I setup MVVMCross to work with these pages without having the page inherit from MvxContentPage ?
I've used the following in the setup.cs in the Android project:
protected override IMvxViewsContainer InitializeViewLookup(IDictionary<Type, Type> viewModelViewLookup)
{
viewModelViewLookup.Add(typeof(SitesViewModel), typeof(SitesView));
return base.InitializeViewLookup(viewModelViewLookup);
}
I can navigate to the page but I get a null reference exception due to the binding context not been set. I've tried to set this in the XAML but it requires a parameterless constructor but that's not possible as the view model uses Dependency Injection as it relies on these object been passed in.
Can anyone give any pointers, I'm still learning MVVMCross so hopefully I've just missed something.
XF: 4.8.0.1687
MVVMCross: 7.1.2
UPDATE 1:
I've tried to find something in the MVVMCross source to see how it does the setup for the binding context, but I've not found anything. So for now, I'm manually creating the binding context in the page code behind and using Mvx.IocProvider.Resolve<> to pass the required interfaces.
I'm not sure if this is the best to workaround this issue but it works. Maybe there is a better way ?
UPDATE 2:
It seems that update 1 route is no good as the doing this creates a new instance of the VM which is to be expected but an instance already exists which is created by the MVVMCross framework. The question is how do I get the VM instance from the code behind ? The only way I have found so far is to use IMvxOverridePresentationAttribute and the cast the request to MvxViewModelInstanceRequest which then allows access to the VM instance which I assign to the binding context. I may be better off creating another question as to the best approach for this method.

Related

Storing a view in Xamarin.Forms MvvmCross

I have an app with four main pages, switched through a tab bar (no "back" button).
One page has a lot of content (ScrollView) and takes quite a few seconds until it's rendered. I handle that by showing a "loading" overlay while the work is done. But for that specific page I'd like to keep the view alive, so that when the user switches to another page and comes back later, the page is ready without loading everything again.
I'm not sure how to do that in MvvmCross, though.
I did read the documentation and from what I understood the View Presenter would be the right way to do it, since the docs say:
"Another kind of presentation changes your app can request through
hints includes clearing / modifying the BackStack, changing a root
while maintaining the existent views, … possibilities are really
endless. Once again your app is king here!"
I guess I would need to create a custom MvxPresentationHint for that, but I don't quite get it :(
How or rather where would I access and store/load the View?
I'm generally still quite unfamiliar with MvvmCross (how it works under the hood) and especially customization of Mvx classes, even though I've been using it for a while.
Any explanation and preferably code examples beyond what's written in the documentation would be extremely appreciated!
It isn't meaningful to attempt to "store" a view in MVVM. The XF view is a representation of what will be created with native (e.g. "Android" or "iOS") widgets. Creating and measuring/laying out those native widgets is what is slow. MVVM View Presenter won't speed up that logic.
Instead of "store", you need "keep alive":
For a ContentPage called MyPage, when you create it, store it in a static variable. Then re-use that variable. If you never need more than one of these, you can store it in the class itself.
Modify the "code behind", MyPage.xaml.cs:
public partial class MyPage : ContentPage
{
// Singleton Pattern.
private static MyPage _it;
public static MyPage It {
get {
if (_it == null)
_it = new MyPage();
return _it;
}
}
// "private", because calling this directly defeats the purpose. Instead, use `MyPage.It`.
private MyPage()
{
InitializeComponent();
}
}
To create it, whereever you would put:
new MyPage()
instead put this:
MyPage.It
For instance, you might do PushAsync(MyPage.It);
This will always return the SAME INSTANCE of MyPage. So once it has been created, it keeps its state.
IMPORTANT: Note that the constructor is only called ONCE. Any code that needs to be done each time the page appears, put in override .. OnAppearing() method.
LIMITATION: Views "expect" to be part of the visual hierarchy when they are manipulated. If you attempt to alter the page or its view model while it is not on the screen, you may encounter problems. Those are beyond the scope of this answer - create a new StackOverflow question with the details of any problem you encounter.

Unable to override OnApplyTemplate

I'm trying to override the NavigationView behavior:
public partial class CustomizableNavigationView : NavigationView
{
public CustomizableNavigationView()
{
// This gets called
}
protected override void OnApplyTemplate()
{
// This doesn't
}
}
It works on UWP, but not on Android. On Android, it doesn't call OnApplyTemplate and the screen remains blank, there's not content. Questions:
Why doesn't OnApplyTemplate get called on Android? I see that here: https://platform.uno/docs/articles/implemented/windows-ui-xaml-frameworkelement.html it says OnApplyTemplate() is on all platforms
There's no error or anything displayed in the Output panne in VS while running with debugger. Should there be any in this case? Do I need to enable something to log errors?
I noticed that if I don't use partial it gaves me error saying partial is required. This is required only on Android, why is that? A more in-depth explanation would help a lot to understand how things work.
Once I figure out why OnApplyTemplate is not called, I want to do this:
base.OnApplyTemplate();
var settingsItem = (NavigationViewItem)GetTemplateChild("SettingsNavPaneItem");
settingsItem.Content = "Custom text";
My hunch is this won't work on Android. Am I correct? :)
Jerome's answer explains why OnApplyTemplate() was not getting called, to address your other questions:
You can configure logging filters for Uno, this is normally defined in App.xaml.cs. Warnings should be logged by default.
The partial is required because Uno does some code-gen behind the scenes to create plumbing methods used by the Xamarin runtime. Specifically because the control is ultimately inheriting from ViewGroup on Android, it's a native object, and requires special constructors that are used only by Xamarin's interop layer. There's some documentation in progress on this.
Try it and see. :) GetTemplateChild() is supported, and setting ContentControl.Content in this way is supported, so I would expect it to work.
At current version (1.45 and below), the application of styles is behaving differently from UWP. We're keeping track of this in this issue.
The gist of the issue is that Uno resolves the style using the current type and not DefaultStyleKey, and cannot find an implicit style for CustomizableNavigationView.
A workaround for this is to either create a named style from the default NavigationView style, or create an implicit style that uses CustomizableNavigationView as the TargetType instead of NavigationView.

Issues with wrapping PrismForms NavigationService

In PrismForms we got the problem, that the NavigationStack is empty after navigating to a new page. That means after using the hardware back-button on the SecondPage, the app is closed. Although the back-arrow in the header on Android isnt shown. If looking closely you can see the back-arrow for a short moment after the page is switched. I guess thats before the NavigationStack gets cleared.
To the first page we navigate with the following command in OnInitialized() in our App.xaml.cs which derives from PrismApplication.
NavigationService.NavigateAsync("NavigationPage/StartPage");
(If only Navigating to „StartPage“ here, the Stack doesnt get cleared.)
That has do to with PageNavigationService.ProcessNavigationForNavigationPage(...) calling
bool clearNavStack = GetClearNavigationPageNavigationStack(currentPage); and PageNavigationService.ProcessNavigationForContentPage(...) not.
From the StartPage to the next we navigate with NavigateAsync("SecondPage")“. Here the described behaviour appears.
For navigation we use a class which wraps the Prism NavigationService. We hold him as a property and get him via Unity in our constructor:
this.PrismNavigation = prismNavigation ?? throw new ArgumentNullException(nameof(prismNavigation));
The methods „NavigateAsync“ and „GoBackAsync“, etc. we just pass through.
This way we want to seperate our ViewModel-Project from references to XamarinForms to later be able to use the same ViewModels for for example a WPF-GUI.
Why is the stack beeing cleared by our own NavigationService? If we register the original Prism NavigationService in App.xaml.cs instead, navigating back works as expected again. We found the point in the framework and could avoid the clearing with a drity hack, but that’s against the navigation-logic implemented in PrismForms, but we don’t understand how to do it the correct way.
Every help appreciated!
We edited a few things to get it working after finding some interesting information by Brian Lagunas in the forlast-post here:
https://github.com/PrismLibrary/Prism/issues/591
Although the topic was about something else, it led to improvements for overwriting the Navigation Service.
Remember that in your viewModels the Navigation Service must be named "navigationService" by convention. Also we switched from just holding the Prism Navigation Service as a parameter to deriving from it as suggested in the link above.
public class MyNavigationService : UnityPageNavigationService

setting Style property of a WebControl From Code Behind

recently I found in one of my older projects (asp.net 4.0)
that I've been using this code
to set display Style-property To none
DDL_ChosenEmpl.Attributes.Add("style", "display:none");
lately I was using
DDL_ChosenEmpl.Style.Add("display", "none");
i would like to know
in any case such as - property already exist whether it's with different value or not , will any of them should be avoided ?
what are the main differences between both methods ?
Both does the same functionality as you have done. The thing is, in "Attributes" collection, you have a control's other attributes like "click", "dblclick", etc. You can use either way. I believe Microsoft has given the later part for user's convinience to make coding easy.

Accessing properties of AdvancedDataGrid runtime vs. design time

I am new to Flex and trying to modify some existing code. I am trying to It uses and AdvancedDataGrid. In the click event, the ListEvent is passed in, and the code is able to access properties such as event.itemRenderer.data.feature.
I would like to check whether the user has clicked on a parent (group) record or the child record; at runtime, I can see that the property I want to access is event.itemRenderer.listData.hasChildren. However, the listData property of itemRenderer does not show up in intellisense, and when I try to access that at design time, the project will not build because of the error "Access of possibly undefined property listData". But the property .data.feature doesn't show up in intellisense at design time either, and that builds and runs just fine.
What do I need to do to access event.itemRenderer.listData without throwing errors when I build the project? Do I need to capture a different event, or cast the event object or one of its properties into another object that will have the properties I need at runtime (I've investigated these options but cannot find info on how to do that).
Many thanks for any insight.
You probably have to perform a cast using IDropInListItemRenderer and AdvancedDataGridListData. Something like this:
((event.itemRenderer as IDropInListItemRenderer).listData as AdvancedDataGridListData).hasChildren

Resources