【问题标题】:ActivityIndicator not displaying in XAML bound to ViewModelActivityIndi​​cator 未在绑定到 ViewModel 的 XAML 中显示
【发布时间】:2017-10-06 06:19:44
【问题描述】:

您好,有一个看似相当简单的登录表单,我已绑定到视图模型。

除了活动指示器之外,一切都可以正常运行。我已经尝试了各种方法来让它出现。

据我了解,IsVisible 和 IsRunning 属性在设置为 true 时应该会显示指示器。我将这些绑定到一个 bool 属性,该属性在整个登录命令中被设置为 true/false。

我需要做什么才能显示它?

Login.xaml

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TechsportiseApp.Views.Login">
    <ContentPage.ToolbarItems>
        <ToolbarItem Name="Register" Order="Primary" Icon="addperson.png" Text="Register" Priority="0" Command="{Binding RegisterCommand}" />
        <ToolbarItem Name="Help" Order="Primary" Icon="help.png" Text="Help" Priority="1" Command="{Binding HelpCommand}" />
    </ContentPage.ToolbarItems>
    <ContentPage.Content>
        <RelativeLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
            <StackLayout HorizontalOptions="FillAndExpand">
                <StackLayout HorizontalOptions="FillAndExpand" BackgroundColor="Red" Padding="2" IsVisible="{Binding IsOffline}">
                    <Label Text="OFFLINE" BackgroundColor="Red" TextColor="White" FontAttributes="Bold" FontSize="Small" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" />
                </StackLayout>
                <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Orientation="Vertical">
                    <ScrollView Orientation="Vertical" VerticalOptions="StartAndExpand">
                        <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Orientation="Vertical" Margin="10">
                            <Image Source="splash.png" HorizontalOptions="Center" />
                            <Label Text="Race Director" FontAttributes="Bold" FontSize="Large" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
                            <Label Text="by Techsportise" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
                            <BoxView HeightRequest="20" HorizontalOptions="FillAndExpand" />
                            <Entry x:Name="email" Text="{Binding Email}" Placeholder="Email address" />
                            <Entry x:Name="password" Text="{Binding Password}" IsPassword="true" Placeholder="Password" />
                            <StackLayout Padding="3" Orientation="Horizontal">
                                <Label Style="{StaticResource EntryFormLabels}" Text="REMEMBER ME" FontSize="Small" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Center" />
                                <Switch IsToggled="{Binding RememberMe}" HorizontalOptions="End" />
                            </StackLayout>
                            <Button x:Name="loginButton" Text="Login" Command="{Binding LoginCommand}" Style="{StaticResource Buttons}" />
                            <ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" Color="#80000000" />
                            <Label Text="{Binding ValidationError}" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" TextColor="Red" FontSize="Small" IsVisible="{Binding Invalid}" />
                        </StackLayout>
                    </ScrollView>
                </StackLayout>
            </StackLayout>
            <ActivityIndicator IsVisible="{Binding IsBusy}"
                               IsRunning="{Binding IsBusy}"
                               Color="Black"
                               VerticalOptions="CenterAndExpand"
                               HorizontalOptions="CenterAndExpand"
                               RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
                                        Property=Width,
                                        Factor=1}"
                               RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
                                        Property=Height,
                                        Factor=1}" />
        </RelativeLayout>
    </ContentPage.Content>
</ContentPage>

Login.xaml.cs

using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechsportiseApp.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TechsportiseApp.API;
using TechsportiseApp.ViewModels;
using TechsportiseApp.Models;
using Newtonsoft.Json;

namespace TechsportiseApp.Views
{
    public partial class Login : ContentPage
    {
        public Login ()
        {
            InitializeComponent ();
            var viewModel = new LoginViewModel();
            BindingContext = viewModel;
        }

        public Login(string email)
        {
            InitializeComponent();
            var viewModel = new LoginViewModel(email);
            BindingContext = viewModel;
        }
    }
}

登录视图模型

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
using TechsportiseApp.API;
using TechsportiseApp.Helpers;
using TechsportiseApp.Models;
using TechsportiseApp.Views;
using Xamarin.Forms;

namespace TechsportiseApp.ViewModels
{
    public class LoginViewModel : INotifyPropertyChanged
    {
        public INavigation Navigation { get; set; }
        public ICommand LoginCommand { get; set; }
        public ICommand HelpCommand { get; set; }
        public ICommand RegisterCommand { get; set; }
        public LoginViewModel()
        {
            LoginCommand = new Command(Login);
            HelpCommand = new Command(Help);
            RegisterCommand = new Command(Register);
            Invalid = false;
        }

        public LoginViewModel(string email)
        {
            LoginCommand = new Command(Login);
            HelpCommand = new Command(Help);
            RegisterCommand = new Command(Register);
            Invalid = true;
            ValidationError = "the account for " + email + " has been created.";
        }


        private bool _isBusy;
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (_isBusy == value)
                    return;

                _isBusy = value;
                OnPropertyChanged("IsBusy");
            }
        }

        private bool _isOffline;
        public bool IsOffline
        {
            get
            {
                _isOffline = !GlobalFunctions.CheckForInternetConnection();
                return _isOffline;
            }
        }

        private bool _rememberMe;
        public bool RememberMe
        {
            get
            {
                if (Application.Current.Properties.ContainsKey("IsRemembered"))
                {
                    _rememberMe = bool.Parse(Application.Current.Properties["IsRemembered"].ToString());
                    return _rememberMe;

                }
                else
                {
                    return _rememberMe;
                }
            }
            set
            {
                if (_rememberMe == value)
                    return;

                _rememberMe = value;
                Application.Current.Properties["IsRemembered"] = _rememberMe;
                OnPropertyChanged("RememberMe");
            }
        }

        private bool _invalid;
        public bool Invalid
        {
            get { return _invalid; }
            set
            {
                if (_invalid == value)
                    return;

                _invalid = value;
                OnPropertyChanged("Invalid");
            }
        }

        private string _validationError;
        public string ValidationError
        {
            get { return _validationError; }
            set
            {
                if (_validationError == value)
                    return;

                _validationError = value;
                OnPropertyChanged("ValidationError");
            }
        }

        private string _email;
        public string Email
        {
            get { return _email; }
            set
            {
                if (_email == value)
                    return;

                _email = value;
                OnPropertyChanged("Email");
            }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set
            {
                if (_password == value)
                    return;

                _password = value;
                OnPropertyChanged("Password");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var changed = PropertyChanged;
            if (changed != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        void Login()
        {
            IsBusy = true;
            var isvalidemail = GlobalFunctions.IsValidEmail(Email);
            var IsThereConnection = GlobalFunctions.CheckForInternetConnection();

            if (IsThereConnection == false)
            {
                Invalid = true;
                ValidationError = "You cannot login whilst you are offline";
                IsBusy = false;
                return;
            }
            else if (Email == "")
            {
                Invalid = true;
                ValidationError = "You must enter an email address";
                IsBusy = false;
                return;
            }
            else if (Password == "")
            {
                Invalid = true;
                ValidationError = "You must enter a password";
                IsBusy = false;
                return;
            }
            else if (isvalidemail == false)
            {
                Invalid = true;
                ValidationError = "You must enter a valid email address";
                IsBusy = false;
                return;
            }
            //We are good to go
            else
            {
                IsBusy = true;
                var LoginStatus = AccountsAPI.Login(Email, Password);
                if (LoginStatus.Code == "OK")
                {
                    var tokenobject = JsonConvert.DeserializeObject<TokenModel>(LoginStatus.Content);
                    Application.Current.Properties["Token"] = tokenobject.Access_token;
                    Application.Current.Properties["IsRemembered"] = RememberMe;
                    string token = Application.Current.Properties["Token"].ToString();
                    if ((bool)Application.Current.Properties["ShowHelpOnStartup"] == true)
                    {
                        App.Current.MainPage = new StartupHelp(true);
                    }
                    else
                    {
                        App.Current.MainPage = new MainMenuMasterDetail();
                    }


                }
                //Error response
                else
                {
                    IsBusy = false;
                    Invalid = true;
                    ValidationError = "Your login has failed. Please check your details and try again.";
                }
            }
        }

        void Register()
        {
            Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new Register());
        }

        void Help()
        {
            Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new LoginHelp());
        }
    }
}

【问题讨论】:

  • 嗨,马修。这是因为您在 viewModel 上运行的代码可能会在运行时冻结屏幕。 Loginmethod 的代码结束时,活动指示器同时显示和隐藏。
  • 尝试将方法更改为异步。它应该可以解决问题
  • 感谢 Diego - 对实现此目的的最佳方法有什么建议吗?我已将void Login() 更改为void DoLogin(),然后添加了一个新方法protected async Task Login() { await Task.Run(() =&gt; DoLogin()); } 但是我在构造函数中使用的命令说异步方法的返回类型错误?
  • 查看我作为答案发布的建议。具有您想要的行为的最清晰的代码。

标签: c# xaml xamarin.forms


【解决方案1】:

尝试将您的 ViewModel 方法 Login 更改为这样:

private void Login()
{
    Task.Run(() => 
    {
        try
        {
            Device.BeginInvokeOnMainThread(() => IsBusy = true);
            var isvalidemail = GlobalFunctions.IsValidEmail(Email);
            var IsThereConnection = GlobalFunctions.CheckForInternetConnection();

            if (IsThereConnection == false)
                throw new Exception("You cannot login whilst you are offline");
            else if (Email == "")
                throw new Exception("You must enter an email address");
            else if (Password == "")
                throw new Exception("You must enter a password");
            else if (isvalidemail == false)
                throw new Exception("You must enter a valid email address");
            //We are good to go
            else
            {
                var LoginStatus = AccountsAPI.Login(Email, Password);
                if (LoginStatus.Code == "OK")
                {
                    var tokenobject = JsonConvert.DeserializeObject<TokenModel>(LoginStatus.Content);
                    Application.Current.Properties["Token"] = tokenobject.Access_token;
                    Application.Current.Properties["IsRemembered"] = RememberMe;
                    string token = Application.Current.Properties["Token"].ToString();
                    if ((bool)Application.Current.Properties["ShowHelpOnStartup"] == true)
                        Device.BeginInvokeOnMainThread(() => App.Current.MainPage = new StartupHelp(true));
                    else
                        Device.BeginInvokeOnMainThread(() => App.Current.MainPage = new MainMenuMasterDetail());
                }
                //Error response
                else
                    throw new Exception("Your login has failed. Please check your details and try again.");         
            }
        }
        catch(Exception ex)
        {

            Device.BeginInvokeOnMainThread(() => 
            {
                ValidationError = ex.Message;
                Invalid = true;
            });
        }
        finally
        {
            Device.BeginInvokeOnMainThread(() => IsBusy = true);
        }
    });
}

【讨论】:

  • 是的,完美!再次感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
  • 1970-01-01
相关资源
最近更新 更多