question

GordonS-9701 avatar image
0 Votes"
GordonS-9701 asked KyleWang-MSFT edited

Xamarin Forms Disable a button if no Internet

I want to disable a button if there is no Internet connection - without waiting for any user interaction. So, for example, on the Login page the user has entered id and password which would enable the button. However, if the Internet connection is lost (before they click the button) then the button should be disabled.

I have a BaseViewModel and am using Xamarin Essentials:

     public class BaseViewModel : INotifyPropertyChanged
     {
         public bool IsNotConnected
         {
             get => _isNotConnected;
             set => SetProperty(ref _isNotConnected, value);
         }
    
         public BaseViewModel()
         {
             Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
             IsNotConnected = Connectivity.NetworkAccess != NetworkAccess.Internet;
         }
    
         ~BaseViewModel()
         {
             Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
         }
    
         public void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
         {
             IsNotConnected = e.NetworkAccess != NetworkAccess.Internet;
         }
    
         protected bool SetProperty<T>(ref T backingStore, T value,
             [CallerMemberName] string propertyName = "",
             Action onChanged = null)
         {
             if (EqualityComparer<T>.Default.Equals(backingStore, value))
                 return false;
    
             backingStore = value;
             onChanged?.Invoke();
             OnPropertyChanged(propertyName);
             return true;
         }
    
     
         public event PropertyChangedEventHandler PropertyChanged;
         protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
         {
             var changed = PropertyChanged;
             if (changed == null)
                 return;
    
             changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
    
     }

In LoginPageViewModel I have:

         public string Password
         {
             get => _password;
             set
             {
                 if (value == _password) return;
                 User = new User { Username = Username, Password = value };
                 SetProperty(ref _password, value);
             }
         }
    
         public string Username
         {
             get { return _username; }
             set
             {
                 if (value == _username) return;
                 User = new User { Username = value, Password = Password };
                 SetProperty(ref _username, value);
             }
         }
    
            
         public LoginPageViewModel()
         {
               
             LoginCommand = new Command(async () => await LoginClicked(), LoginAllowed);
         }
    
         private bool LoginAllowed()
         {
             if (IsNotConnected)
                 return false;
    
             if (User == null)
                 return false;
    
             if (string.IsNullOrEmpty(User.Username) || string.IsNullOrEmpty(User.Password) || IsNotConnected)
                 return false;
    
             return true;
         }


So - this works if there is no Internet when the App starts / login page loads, it also works if Internet is lost while on the Login page and the user does something in the the username or password box, but how do I make the button disabled as soon as the loss of Internet is detected (and IsNotConnected is set)?

Also, more generally, I would like to be able to "tell" anything in the app that might be interested that the Internet connection status has changed so that "it" can react straight away, without requiring or waiting for user interaction.

dotnet-xamarin
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

You can make the IsNotConnected be static and bind the button IsEnabled to the property. then if the Connectivity is changed The button will changed.

0 Votes 0 ·
KyleWang-MSFT avatar image
0 Votes"
KyleWang-MSFT answered KyleWang-MSFT edited

Hi GordonS-9701,

Welcome to our Microsoft Q&A platform!

To change the IsEnabled property of Button in real time, you can use Data Binding to achieve it.

First, you need to define a boolean property "IsConnected" to store the current network status and implement interface "INotifyPropertyChanged".

 class BaseViewModel : INotifyPropertyChanged
 {

     private string username;
     public string Username
     {
         get => username;
         set
         {
             username = value;
             OnPropertyChanged("Username");
         }
     }

     private string password;
     public string Password
     {
         get => password;
         set
         {
             password = value;
             OnPropertyChanged("Password");
         }
     }

     private bool isConnected;
     public bool IsConnected
     {
         get => isConnected;
         set
         {
             isConnected = value;
             OnPropertyChanged("IsConnected");
         }
     }

     private bool isEnbale;
     public bool IsEnbale
     {
         get => isEnbale;
         set
         {
             isEnbale = value;
             OnPropertyChanged("IsEnbale");
         }
     }

     public BaseViewModel()
     {
         Username = string.Empty;
         Password = string.Empty;
         IsConnected = false;
         CheckNetworkOnStart();
         CheckNetwork();
     }
    
     public void CheckNetworkOnStart()
     {
         if (Connectivity.NetworkAccess == NetworkAccess.Internet)
         {
             IsConnected = true;
         }
         else
         {
             IsConnected = false;
         }
     }
    
     public void CheckNetwork()
     {
         Connectivity.ConnectivityChanged += (sender, args) =>
         {
             if (Connectivity.NetworkAccess == NetworkAccess.Internet)
             {
                 IsConnected = true;
             }
             else
             {
                 IsConnected = false;
             }
         };
     }
    
     public event PropertyChangedEventHandler PropertyChanged;
    
     protected void OnPropertyChanged(string propertyName)
     {
         if (propertyName != "IsEnbale")
         {
             if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password) || !IsConnected)
             {
                 IsEnbale = false;
             }
             else
             {
                 IsEnbale = true;
             }
         }
         var handler = PropertyChanged;
         if (handler != null)
             handler(this, new PropertyChangedEventArgs(propertyName));
     }
 }

Then you need to modify the XAML as follows.

 <Button Text="test" 
            IsEnabled="{Binding IsConnected}" 
            Clicked="Button_Clicked"/>

Regards,
Kyle


If the response is helpful, please click "Accept Answer" and upvote it.

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

But won't that only check the Network Status once, when the page is created? I need it to disable the button as soon as Network access is lost and then re-enable it when access returns.

I think I have solved it by creating an event handler in my base class and raising it whenever "ConnectivityChanged" happens.

0 Votes 0 ·

@GordonS-9701 I also posted the CheckNetwork method which can continuously check the network status. Have you tried it too?

0 Votes 0 ·

I haven't yet - it's not quite the solution I need because the button needs to be active / disabled based on the state of the username and password fields too (see my LoginAllowed code above).

I will need to undo / back out the solution I've already applied in order to test your option.

0 Votes 0 ·
Show more comments
GordonS-9701 avatar image
0 Votes"
GordonS-9701 answered

OK, I think I've solved this by using an EventHandler. I added this to my base class:

         public event EventHandler OnConnectivityChanged = null;
            
         public BaseViewModel()
         {
             Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
             IsNotConnected = Connectivity.NetworkAccess != NetworkAccess.Internet;
         }
    
         ~BaseViewModel()
         {
             Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
         }
    
         public void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
         {
             IsNotConnected = e.NetworkAccess != NetworkAccess.Internet;
    
             // Raise an event that Derived classes can handle
             OnConnectivityChanged?.Invoke(this, e);
         }

Then, added this to my Login page VM:
public LoginPageViewModel()
{
OnConnectivityChanged += ConnectivityChanged;

             LoginCommand = new Command(async () => await LoginClicked(), LoginAllowed);
             // other code removed for brevity
         }
    
         private void ConnectivityChanged(object sender, EventArgs e)
         {
             LoginCommand.ChangeCanExecute();
         }
    
         private bool LoginAllowed()
         {
             if (IsNotConnected)
                 return false;
    
             if (User == null)
                 return false;
    
             if (string.IsNullOrEmpty(User.Username) || string.IsNullOrEmpty(User.Password) || IsNotConnected)
                 return false;
    
             return true;
         }

Seems to work, but I am still testing it. Is this a reasonable / safe approach?

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.