I am wondering why binding a button inside the Loaded event in WPF page does not work and will only work after navigating to another page, and going back.
I have an inventory app and on the main page, most of the ViewModel are called because of a Back button which goes back to a specific lists and what causes that is, it will start binding the even if that command is not for that page and it will also load the collections for other pages.
So I used Loaded page event to call the necessary methods to populate the lists and also start binding commands for this specific page. I also used Unloaded page event for clean up like unsubscribing to some CRUD events.
The problem now though is, buttons does not get binding in Loaded page event. I do not know why..
I have made a miniature app to demo the problem. It can be downloaded here
(full source code included)
https://www.dropbox.com/s/qzumzyicuvrktsi/ICommandTest.zip?dl=0
This is because your views are not getting notified about the change of Command_ShowAddWindow and Command_ClickMe. Let me explain:
When your Page constructor is first run the bindings to your commands are initialized and transferred to the view, but by that time your commands are null, so the view binds both buttons' commands to null.
Then when your Loaded event is fired the commands are initialized, but the view is not getting notified about it, so it keeps command bindings to null.
The solutions to the problem are:
You manually call RaisePropertyChanged to notify the view about commands change when you initialize them:
void InitCommands()
{
Command_ShowAddWindow = new RelayCommand(Command_ShowAddWindow_Click);
Command_ClickMe = new RelayCommand(Command_ClickMe_Click);
RaisePropertyChanged("Command_ShowAddWindow");
RaisePropertyChanged("Command_ClickMe");
}
Or you initialize your commands in your ViewModel constructor before DataBindings are initialized:
public ViewModel_Page1()
{
InitCommands();
...
}
Related
What is the best place to call a service and load data in xamarin forms
Till now I am calling the service in the view model constructor and loading data
I have a new situation -
In my app I check for network connectivity, if it's not connected to internet I will not load data in the main form and show a modal form saying network is not available check. If the recheck is success will pop the modal but as the data is not loaded the main form is empty.
So in this case I have to write service call in on appearing override function. Which gets called after the modal popped. Which is okay. But problem is every time when we navigate to that view it will make a service call.
Please guide me the best place to call these services
For your problem, you can use C# Events (Publisher-Subscriber) as the solution
Write a event called InternetDisconnectedEvent in the View Model. Subscribe to that event in Code behind of the View (.xaml.cs)
Make UI Changes in the View, when internet disconnected.
For more about Event & Delegates, check this tutorial
"...But problem is every time when we navigate to that view it will make a service call."
Create a flag (a boolean property), say IsLoaded in the viewmodel (or view, depending on your current implementation), that set to false initially. Then add a logic that check IsLoaded flag before calling the service. If IsLoaded is false, run your current logic of checking internet connection, calling service, so on.. and lastly update IsLoaded to true.
I am trying to use NFC along with MvvmCross. So I have created an IntentFilter which looks like this:
[IntentFilter(new[]{"android.nfc.action.NDEF_DISCOVERED"},
Categories = new[] {"android.intent.category.DEFAULT"},
DataScheme = "bksv-resource", DataHost = "nmt")]
And in OnResume I am looking for Intent Actions of the type NfcAdapter.ActionNdefDiscovered. What happens is when NDEF is discovered, the Activity gets created and an Intent is set with the data inside. All this works fine in an Activity, however problems occur when wanting to use an MvxActivity or MvxFragmentActivity.
Problem is, when the MvxActivity gets recreated the base.OnCreate() call gives no ViewModel back and no reason is given, only output like this is shown:
mvx:Diagnostic: 6.09 Loading new ViewModel from Intent with Extras
mvx:Warning: 6.13 ViewModel not loaded for view ScanView
I am not entirely sure how the ViewModel is found for a View, however, in this case it seems not to be found. Why could that be? Do I need a custom app start or something?
MvvmCross picks up its ViewModel navigation information from the passed in Intent
You can:
see how MvvmCross creates it's Intents in: MvxAndroidViewsContainer.cs#L120
see how MvvmCross gets the information back out in: MvxAndroidViewsContainer.cs#L87
If you are using a custom non-MvvmCross Intent then MvvmCross obviously won't be able to pick up its navigation information - so you'll have to work out a way to create the ViewModel yourself.
I am slowly migrating my app over to MvvmCross on Android. I have not converted all Activities over to ViewModels yet. Therefore, I need to navigate from an Mvvm-controlled Activity to a regular Activity. To do this, I made my own Presenter and intercepted the Show method and did my own StartActivity. That seemed to work. However, now I need to go the other direction and have my regular Activity go back to the original Mvvm-controlled Activity. I tried just doing a StartActivity on the ViewModel using CLEAR_TOP flag, but I got an error:
"Null Extras seen on Intent when creating ViewModel - this should not happen - have you tried to navigate to an MvvmCross View directly?"
How can I go back to the original Mvvm-controlled activity from a regular Activity?
Simple...
To go back from any standard Android Activity, you can simply ask the Activity to close using Finish()
But beyond that...
If instead you want to go forwards to an MvvmCross View, then you need to know a little about MvvmCross internals: in particular about how MvvmCross navigation conceptually happens between ViewModels rather than between Activities, Pages or UIViewControllers.
If you want to go forwards to a new ViewModel, then you can do this using the IMvxViewDispatcher singleton - how to do this is shown in Show view from non-view/viewmodel in mvvmcross
If you then later want to go back from the current ViewModel, then you can try calling Close(this) within the ViewModel - by default, on Android this will map to Finish(), on Touch to PopViewController, on WpDev to GoBack()
This seemed to work, but is a hack since I use a special string "MvxLaunchData".
Intent i = new Intent(this,typeof(LoginView));
i.AddFlags(ActivityFlags.ClearTop);
var converter = Mvx.Resolve<IMvxNavigationSerializer> ();
MvxViewModelRequest request = MvxViewModelRequest.GetDefaultRequest (typeof(LoginViewModel));
i.PutExtra ("MvxLaunchData", converter.Serializer.SerializeObject(request));
StartActivity(i);
I will try the method shown in the other question you referenced.
I am programming WP7 application adhering the MVVM pattern.
I have ViewModelLocator which ensures that each instance of ViewModel is only one. These ViewModels are created when application is launched. ViewModels communicating with each other using messages. I use messages for navigatig to next Page (For this I am using NavigationService.Navigate(), which is Raised from MainPage CodeBehind - it is the only functionality that is in CodeBehind ). View and ViewModels are connected together by setting the DataContext in the Page to ViewModelLocator.
Everythig works at first sight.
But during each navigation, there is created new Page instance, which is connected to the ViewModel from ViewModelLocator(which is designed for it). The result is that: when a user often switches between pages, there are multiple instances of a page connected to one ViewModel. Of course, there is visible only one page at one point.
Very simple solution could be setting NavigationCache, but it is readonly in WP7.
I am looking for solution of unwanted behavior.
Every time you Navigate somewhere a new instance is created for that page.
You could avoid this by using NavigationService.GoBack(); where ever you can.
You should also unregister from every event when navigating away from the page, so that way the garbage collector can clear out that page.
I hope this helps.
You may try to declare an instance of your ViewModel at App.xaml.cs, such as,
private static YourViewModel viewModel = null;
public static YourViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = ViewModelLocator.MainStatic;
return viewModel;
}
}
Then from the page you will navigate to, you can reference it as App.ViewModel.
If your page is in different assembly form your main application, you could declare following in your App.xaml,
<vm:ViewModelLocator x:Key="VMLocator" />
Where vm referencing to your main app, then you can use is as following,
((ViewModelLocator)Application.Current.Resources["VMLocator"]).YourViewModel;
Hope it would help.
I am working on an application which is using WPF, MVVM, Prism, and MEF.
I am using a combination of navigation with request navigate, controllers with view management using region manager, and eventing via event aggregator to run the application in a single window. I'm using a view first approach similar to how the Stock Trader RI works.
This works great when any view model code that would interact with the UI (busy indicator) is kicked off by the user, but when it is started behind the scenes there can be problems.
I know this may seem like a poor implementation, but I think I have a valid scenario. My particular example has to do with login.
Currently the application starts and loads the shell. The login view is loaded into the main content region of the shell. When the user clicks "login" a busy indicator is shown and the client application services login is executed. When the login is complete, the busy indicator goes away, and the screen is navigated to the user's home screen.
This works well because the navigation login and navigation are initiated by the user clicking the login button.
So now I have a new requirement that a user can select Auto Login on the login form, such that the next time the user starts the app, the login view will not show up and login will happen behind the scenes.
Now if I just want to call the auto login feature, there is no problem, this by itself has no UI interaction and will work fine. But login takes a few seconds and I want to display my busy indicator.
The problem is where do I initiate the auto login call? The shell view model constructor? The shell view model PartImportsSatisfied implementation? In any of these places, the shell view (which contains my busy indicator) isn't really loaded yet. As a result, none of the resources I need, like regions and region managers aren’t available.
So what might be a good way for me to implement this:
Check if previous user should auto login (got this part figured out)
If yes then
Show busy indicator
Attempt to auto login
If auto login was success
Hide busy indicator
Navigate to user home screen
Else
Hide busy indicator
Navigate to login screen
Else
Hide busy indicator
Navigate to the login screen
Any ideas are greatly appreciated.
Implement an interface within your ShellViewModel which will deal with the concept of being loadable. Do not perform this logic within the constructor of the ShellViewModel as this is typically bad practice and should be used to instantiate objects at the most.
public class ShellViewModel : ILoadable
{
public ShellViewModel()
{
}
bool ILoadable.Load()
{
//this is where you can take care of your auto login
}
}
You could then call this within your Bootstrapper class. If this were being done within another module you could call it within the IModule.Initialize method.
I would also argue that this logic should get implemented within a service which could be called by the ShellViewModel as mentioned above or could in theory be called directly from the Bootstrapper class; allowing the ShellViewModel to potentially then make use of stateful data from within the service.