MvvmCross MvxAsyncCommand CanExecute does not disable/enable attached button - xamarin.forms

I am using MvvmCross in a Xamarin.Forms solution. I have the command attached to the button with the following code
<Button Text="Login" Command="{Binding LoginCommand}"/>
The command is setup in the ViewModel like so
private ICommand _loginCommand;
public ICommand LoginCommand
{
get
{
_loginCommand = _loginCommand ?? new MvxAsyncCommand(Login,
() =>
{
return IsOnline;
});
return _loginCommand;
}
}
I then query when the network connection changes which should in turn change the boolean returned in the CanExecute Func in the MvxAsyncCommand.
{
base.OnlineSignalChanged(isOnline);
(LoginCommand as MvxAsyncCommand)?.RaiseCanExecuteChanged(); //tried calling other methods here as well in case I was calling the wrong thing
}
In that last code block I am able to stop a breakpoint to see that the CanExecute is queried again. However, the button is not enabled/disabled properly.
Is this a know issue with MvvmCross implementation of MvxAsyncCommand? I ended up using the advice of the following thread use John Thiriet version of an AsyncCommand and all works well.
How to implement Async Command

Related

ImageButton not firing Command binding in Xamarin Forms

I have an ImageButton that is not firing Command binding command using VMMV architecture. First all other bindings are working properly in the view.
Here is button:
<ImageButton Command="{Binding SelectedItemCommand}" Source="{Binding Logo}" Grid.Row="0" Grid.Column="1" HeightRequest="150" WidthRequest="150" HorizontalOptions="CenterAndExpand" VerticalOptions="EndAndExpand"></ImageButton>
and in ViewModel:
public ICommand SelectedItemCommand => new Command(GetSelectedItem);
When I click the image nothing happens. I've even tried to bind to Pressed parameter but from everything I have read only the Command parameter should be used in a binding scenario. Putting a breakpoint on the function GetSelectedItem never gets reached.
What am I doing wrong here?
Sorry been away for a few days. So nothing was working on the suggestions even though they really should be clicking wouldn't fire command. Anyway I managed to get it to fire now using an eventhandler like so:
SelectedItemCommand = new Command<string>(param => OnItemSelected(param));
public void OnItemSelected(string img1_2)
{
PressedEventHandler?.Invoke(this, EventArgs.Empty);
}
The param captures the CommandParameter so I know which image to the question was clicked "img1" "img2" to do something specific. So my function now accepts a sender object and empty eventarg. I would like to instead pass img1_2 value but that doesn't appear to be possible as of now. What is cusrious is the sender object contains all the properties and values from the images (like an array of all my properties) but I cannot seem to get at them.
Attempted this:
string str = Item1Image.ToString(); // property in sender and viewmodel
But this returns a null value and not value listed in the sender object value?
Any additional thoughts?
TIA!
Rick...
public ICommand SelectedItemCommand {get; private set;}
...
public YourViewModel(){
...
SelectedItemCommand = new Command(GetSelectedItem);
...
}
...
Or
public ICommand SelectedItemCommand{
get
{
return new Command(() => {
//Do something
});
}
}

Why DisplayAlert does not work in constructor?

I have some problems in my Xamarin.Forms app related to invoking async method in the page constructor so when I test something trying to figure out the reason I just realized DisplayAlert method does not even work in the page constructor so I am wondering why is that happening?
Here is my code:
public MainPage ()
{
InitializeComponent ();
DisplayAlert("An alert", "Why I don't show up?", "Ok");
}
and I also tried to call async method that has DisplayAlert method but didn't work too, here is the code:
public MainPage ()
{
InitializeComponent ();
Async_Function_Has_DisplayAlert();
}
async void Async_Function_Has_DisplayAlert()
{
// I tried both and neither of them worked
await DisplayAlert("An alert", "Why I don't show up?", "Ok");
await Task.Run(()=> DisplayAlert("An alert", "Why I don't show up?", "Ok"));
}
So can someone explain why that is happening please?
Normally, you should not call an awaitable method like DisplayAlert() from the constructor.
What you can do is have a method that returns void (still not best practice) and call that method from your constructor.
Tweaking my recommendation after trying it out.
I used Device.Timer to delay the alert.
I think some components have not finished loading (in this case, the DisplayAlert) before trying to call it.
public MainPage()
{
InitializeComponent();
Device.StartTimer(TimeSpan.FromSeconds(4), () =>
{
ShowMessage();
return false; // True = Repeat again, False = Stop the timer
});
}
public async void ShowMessage()
{
await DisplayAlert("Alert", "I show here", "OK");
}
There seems to be a misconception on what is happening in the constructor.
The constructor simply creates a new Page class.
CustomMainPage mainpage = new CustomMainPage();
(App.Current as App).MainPage = new NavigationPage(mainpage);
So before we add the mainpage class to the NavigationPage, all that has happened is that the CustomMainPage class was initialized and is ready to be inserted into an appropriate container.
However after creating the new page, there is no actual UI on the screen, yet. For instance, the mainpage object wouldn't have width or height set, no layout has been done, etc...
If you run a UI related task, such as presenting an Alert, there isn't simply any foundation for it there, which could do anything resonable.
Of course you could already set members of the mainpage, such as labels or buttons to certain values, colors, styles or whatever you want, from within the constructor, but these wouldn't do anything at that point of time.
All of those values will be taken into account when the page is being layouted and presented but none of that will happen in the constructor.
However, back to your problem: You seemingly want to inform the user that something has gone wrong during the initialization.
I see two ways of adressing that issue:
Check the preconditions on the page or within the code before initializing your view and present the Alert from the page or class, which is initializing your page.
create a private variable in your page class, which you will set from within your page constructor if something goes wrong. This could be a simple bool flag, a string containing an error message, an enum or whatever suits your needs. Then override the OnAppearing() method, check that flag you set earlier and call DisplayAlert depending on the flag's value.
If you want any interactivity on your page, then you should consider Jason's comment to your question and implement it within OnAppearing, because this method will be called once your page has been fully layouted and is being presented on your screen.
Sample code for Jason's recommendation
public async void ShowMessage()
{
await DisplayAlert("Alert", "I show here", "OK");
}
protected override void OnAppearing()
{
ShowMessage();
}

Binding the Enabled property of a Android button with the MvvmCross

I have a problem when I try to bind the "Enabled" property of my Android Button to a Boolean of my ViewModel using the MvvmCross framework and I really don't know the origin of it.
So I have a ViewModel which contains the two following properties :
private ProjectDetailDTO _projectDetail;
public ProjectDetailDTO ProjectDetail
{
get { return this._projectDetail; }
set
{
_projectDetail = value;
RaisePropertyChanged(() => ProjectDetail);
RaisePropertyChanged(() => HasPicture);
}
}
private bool _hasPicture;
public bool HasPicture
{
get { return ((this.ProjectDetail != null) && !String.IsNullOrEmpty(this.ProjectDetail.Pictures)); }
set { _hasPicture = value;
RaisePropertyChanged(() => HasPicture);
}
}
As you would understand, my button is bind to the HasPicture property. So I have the following code for my button in my .axml file :
<Button
local:MvxLang="Text LblSeePicturesValue"
local:MvxBind="Enabled HasPicture,Click ShowProjectPicturesCommand"
android:id="#+id/buttonPictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
I don't think it's a ViewModel problem because my WP application works well with this code. In fact, my ProjectDetailDTO is filled by calling a web service, so by an asynchronous method. I think it's why when the binding is realized the HasPicture property has the false value. But with my ViewModel code, the HasPicture property should be updated when the ProjectDetailDTO is filled. Is there anything I did wrong in my Android View?
Thanks for any help !
I think what you are seeing here is some interaction between ICommand.CanExecute and the Enabled property. There's a discussion about this on https://github.com/MvvmCross/MvvmCross/issues/729
To work around this, try switching the binding to:
local:MvxBind="Click ShowProjectPicturesCommand;Enabled HasPicture"
(Also note that the separator in bindings is a ; - not a ,)

Messenger Class - Can AppBar Button us it to update screen

I have a button on my AppBar to "Sync All". This call a webservice for each provider and updates their data that is has been cached. Once updated I need to update the selected providers data on the screen, so how do I do this with mvvm-light.
1) When I try to access the data in the click even of the button I am not sure how to access the currently loaded view models so I can refresh the data behind them. Is there a way to access the view model from a click event and refresh the underlying data.
2) I am wondering that is what the messenger class is used for and if so are there any good examples I can look at to use this feature in when the user clicks the "Sync All" Button.
3) If the messenger class is not the way to do this and there is no way to access the current view-model, what other options do I have.
Any help will be appreciated.
You've got two ways of doing this:
Using messenger
Using standard events
Option 1 works pretty much regardless of your design and goes something like this:
User clicks your AppBar button, which calls a command
The command calls a service that, internally calls your webservice (the nice way) or the command just calls your webservice (the not so nice way). I'm expecting this method to be an async method.
Once you've got your new data you call Messenger.Default.Send() to broadcast to all listening viewmodels that they should refresh their data.
Code:
ViewModel:
public class ViewModel
{
public ViewModel()
{
Messenger.Default.Register<DataRefreshEvent>(this,ReceiveDataRefreshEvent);
}
private void ReceiveDataRefreshEvent(DataRefreshEvent obj)
{
//do what you need to do
}
}
Service:
public class Service
{
public async void RefreshData()
{
await _webService.RefreshDataAsync();
Messenger.Default.Send(new ReceiveDataRefreshEvent());
}
}
Option 2 works like option 1 except it expects you to have a central data service wrapping your webservice. This service would be injected into all of your viewmodels.
User clicks your AppBar button, which calls a command
The command calls a service that, internally calls your webservice (the nice way) or the command just calls your webservice (the not so nice way). I'm expecting this method to be an async method.
The service raises a standard event to let the subscribed viewmodels know that they need to refresh their data. So basically same as option 1 except that each viewmodel is actives subscribing to an event on the service rather than a Messaging event.
Code:
ViewModel:
public class ViewModel
{
public ViewModel(IService service)
{
service.DataChanged+=ReceiveDataRefreshedEvent;
}
private void ReceiveDataRefreshEvent(sender obj,EventArgs args)
{
//do what you need to do
}
}
Service:
public class Service:IService
{
public event EventHandler ReceiveDataRefreshedEvent;
public async void RefreshData()
{
await _webService.RefreshDataAsync();
if(ReceiveDataRefreshedEvent!=null)
ReceiveDataRefreshedEvent(this,EventArgs.Empty);
}
}
Option 1 is nice because you don't have to hand over a reference to the service to the ViewModel. It's nicely de-coupled.
If you are unsure about messaging use option 2.

What are the benefits of using the RelayCommand object

I want to know what the advantage are using the relay command to call functions that refresh the screen. In my application I have the following relay command setup.
private RelayCommand _refreshSitesCommand;
public RelayCommand RefreshSitesCommand
{
get { return _refreshSitesCommand ?? (_refreshSitesCommand = new RelayCommand(RefreshSites)); }
}
private RelayCommand _refreshProvidersCommand;
public RelayCommand RefreshProvidersCommand
{
get { return _refreshProvidersCommand ?? (_refreshProvidersCommand = new RelayCommand(RefreshProviders)); }
}
private async void RefreshSites()
{
var sitesStats = await _dataService.GetSiteStats();
if (sitesStats != null)
{
SiteStats.Clear();
foreach (var site in sitesStats)
{
SiteStats.Add(new SiteStatsViewModel(site));
}
SelectedSite = SiteStats[0];
}
}
private async void RefreshProviders()
{
var providers = await _dataService.GetProviders();
if (providers != null)
{
Providers.Clear();
foreach (var provider in providers)
{
Providers.Add(new ProviderViewModel(provider));
}
SelectedProvider = Providers[0];
}
}
Then in my code I have the following calls to execute it.
RefreshProvidersCommand.Execute(null);
RefreshSitesCommand.Execute(null);
So why is that better than just calling the RefreshSites and RefreshProviders functions. Then I would not need the code for the RelayCommand objects. Other than exposing the functionality of the 2 private functions, what benefit does using the RelayCommand object have over just making the functions public and calling them.
MVVM is in part about avoiding code-behind in the View class.
If, for example, you want an action to be taken in response to a button click, then you can either assign a Click event handler or assign the Command property to command methods. (Commands have certain advantages over Click event handlers, but that was not the question.)
There is no other good option for handling the Click event other than defining a method in the View class. You cannot directly assign the Click event to a handler method in a different class than the View and you can bind only to properties, not methods.
However, you can assign the Command property to a binding to an object that implements the ICommand interface, e.g. a RelayCommand, and that binding can be to a property of your ViewModel object. This avoids having to define Click event handlers in the view's code behind file and at the same time gives your ViewModel the ability to easily enable/disable commands without needing to know anything about the View's specific implementation.
One can argue about the merits of religiously avoiding code-behind, but that was not the question asked.
Because you can bind to a Command in your view. You can't bind to methods in your views (well you can but binding to Commands is much cleaner)
RelayCommand also implements a CanExecute method which, when binding your RelayCommand to a button, is used to automatically toggle the button's IsEnabled property based on the action you specified for the CanExecute method.

Resources