I have been working with MVVM for a while (comming from XAML) and I'm using MVVMCross at this moment for a Xamarin project.
What is the best (a good) strategy to load data into the ViewModels\Models in a async way?
PS: I want to prevent long frozen screens when navigating, I'm looking to navigate first and the async load the data.
just to clearify;
The ViewModels in MVVMCross (MvxViewModel) do have some handy override methods.
Init for picking up navigation parameters
Start to do everything else after the ViewModel is innitualized.
To load ViewModel data in a more UX friendly way I was happy with the following in my ViewModels
public override async void Start()
{
base.Start();
myViewModel = await LoadViewModel();
}
Thanks Thomas and Cheesebaron for the comments
Related
Many tutorials and guides suggest that when you create a custom control in Xamarin.Forms or .NET MAUI with a ContentView (or extending another view), it should implement IDisposable interface when needed. See here and here.
That can be useful, as far as i can see, to unsubscribe from events.
My question is: when is Dispose() called?
I tried some scenarios in Xamarin.Forms involving moving from the page where the custom view lies and removing that page from the navigation stack, but none of these actions called the Dispose() method.
Do I have to call it manually?
Solution: Unsubscribe from the message in the message action/handler, or use a pattern of subscribing in OnAppearing and unsubscribing in OnDisappearing.
MessagingCenter.Subscribe<string, DetailClass>(this, "NavigateDetail", async (detail) =>
{
MessagingCenter.Unsubscribe<string>(this, "NavigateDetail");
await Naviation.PopAsync();
});
It seems you have to do it manually.
Javier Suarez answered this question with a Youtube comment under his video.
My page layout looks like this:
<StackLayout x:Name="detailsLayout" VerticalOptions="FillAndExpand">
</StackLayout>
When I create my ViewModel I pass in a pointer to the page. What I would like to do is this:
public class CardsViewModel : BaseViewModel
{
private readonly Cards cardsPage;
public CardsViewModel(Cards cardsPage)
{
this.cardsPage = cardsPage;
}
async public Task CardBtn()
{
cardsPage.detailsLayout.Children.Clear();
But I don't have access to the detailsLayout due to its protection level.
Can someone tell me how I can get access to do this?
The example above is an example of coupling and breaking the separation between the ViewModel and the View(Page). If you want to benefit from MVVM pattern - don't keep a reference to your UI layer and definitely don't try to manipulate the UI layer directly in the ViewModel.
What you should do instead is depends on your needs, however I can give you few ideas:
Encapsulate the logic within the UI control it self, exposing a delegate, command or property you could bind to from your ViewModel to trigger the specific event.
Keep the logic on UI layer, the Page itself and use a MessagingCenter to communicate to it from the ViewModel.
Good luck.
P.S.: You can find more information and examples about MessagingCenter in the official Xamarin documentation. Just don't forget to unsubscribe, otherwise you will find yourself in a bad place.
Is there any reason or problem I cant call InitializeComponent method in OnAppearing function of a page in xamarin forms project?
I understand that I must call InitializeComponent only once to create the actual page. But what if I check that Content is already created and do it as below. Is it a bad implementation or practice? because it is said that no xaml based application does it and always call it in a constructor of the page.
reason I want to do it as below because xamarin.forms start up time is slow running on Android and if you use Masterdetail page(I think same for tabbed page), you must initialize it at the start up, it causes every navigation page defined in masterdetail page to be initialized and it costs you 2-3 secs depending on your UI could be even higher cost. any thoughts or experiences on this?
protected override void OnAppearing()
{
if (Content == null)
{
InitializeComponent();
}
}
I do not recommend this approach. From the xamarin docs.
The constructor of that class calls InitializeComponent, which then calls the LoadFromXaml method that extracts the XAML file (or its compiled binary) from the PCL. LoadFromXaml initializes all the objects defined in the XAML file, connects them all together in parent-child relationships, attaches event handlers defined in code to events set in the XAML file, and sets the resultant tree of objects as the content of the page.
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
If your forms are too slow on Android I would enable Fast Renders instead
In the OnCreate of your main activity add this line of code just before Xamarin.Forms.Init
Forms.SetFlags("FastRenderers_Experimental");
https://xamarinhelp.com/xamarin-forms-fastrenderers-android/
It would not hurt to try to use compiled Xaml also
using Xamarin.Forms.Xaml;
...
[XamlCompilation (XamlCompilationOptions.Compile)]
public class HomePage : ContentPage
{
https://developer.xamarin.com/guides/xamarin-forms/xaml/xamlc/
I wanted to add a lazy loading list box(load content when swipe) in a panorama page in one of my windows phone 7 applications. I could however do it using a pivot page. I referred this link
But this is not working with panorama page. Can anyone please help me?
Okay, you're going to need to do one of two things: use the BCL Async package (basically adds async Tasks and such to WP7) or use a background worker. I highly suggest the BCL Async package, it's easy to get on Nuget.
Now, in your ViewModel (you are using MVVM, yes?) the property that it's bound to, let's call it Items should return an ObservableCollection of the item type you need. Now, here's where the magic happens. In the Getter of that property, return a new collection and use a task to fill it. Something like this:
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> retCollection = new ObservableCollection<object>();
FillCollection(retCollection);
return retCollection;
}
}
public async void FillCollection(ObservableCollection<object> collectionToFill)
{
Task.Factory.StartNew(() =>
{
foreach(object objectToAdd in collectionImGettingThisDataFrom)
{
// We do this using the Dispatcher to
// be sure to pop back into the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(
() => collectionToFill.Add(objectToAdd));
}
}
}
Because FillCollection is async, the Get method will continue and return the current collection. On another thread, the Task that's created will find the data to add, then push it to the UI thread to add it into the collection. This way, you'll be able to lazy load the data only when you ask for it, without completely blocking your UI thread. If it turns out that it's still making your UI slow, you can add the line:
await TaskEx.Delay(25); // Some time in milliseconds. Too much and it will
// take a long time to load the list,
// too little and it will still bog down your UI.
At the end of the foreach block, but not in the Dispatcher invokation.
Happy coding!
Have you looked at the Telerik Rad Controls yet? They have all types of pull to refresh controls. I used them in a recent app I released called "Rad Libs". You can see the controls here http://www.telerik.com/products/windows-phone.aspx and you can also download an app that demos all of their controls. (Disclaimer: I have no affiliation with telerik. I stand to gain nothing from promoting them here)
Ok, so what I am looking to do is to display some sort of login control (maybe a UserControl with a TextBox and PasswordBox) when the app is started.
In a non-mvvm situation, a way of doing this would be to use the PopUp primitive control, add the usercontrol as a child element and off you go.
In an MVVM situation, i'm a bit confused about how you would achieve a simmilar result.
I have looked into messaging with the DialogMessage and this is fine for displaying a typical MessageBox, but what about a custom usercontrol?
any help would be fantastic! I can't seem to find any demo code of this anywhere.
In a MVVM situation you can use a delegate to let your View open the dialog when the ViewModel requests it.
You define a delegate at the VM:
public Func<LoginResult> ShowLoginDialogDelegate;
In your View you define the function that will be called:
private LoginResult ShowLoginDialog()
{
LoginResult result;
// show a dialog and get the login data
return result;
}
Then you "connect" the delegate and method in the View:
_viewModel = new MyViewModel();
DataContext = _viewModel;
_viewModel.ShowLoginDialogDelegate += ShowLoginDialog;
And now you can use it in your ViewModel e.g. when a command is executed like that:
LoginResult result = ShowLoginDialogDelegate();
An easier answer is to control it's visibility through a View State which with a little manipulation can be made to work through databinding allowing the view model to display the "Logon Page" state when required.
I just recently wrote about this for the Silverlight/XNA series which you can view here.
It would be much simplier if the SL4 DataEventrigger was available but hay ho.