在从webservice服务检索数据之前和之后,从ViewModel设置ProgressBar可见性
我正在使用MVVM设计模式和MVVM Light工具包创建Windows Phone 8.1应用程序。
我正在尝试创建一个简单的登录页面,从用户获取用户名和密码并使用web服务进行身份验证。 在这个身份验证过程中,我想在页面顶部显示一个ProgressBar(加载点),以指示正在发生的事情。
我已经成功创建了我的ViewModel并绑定了我的视图,以允许我通过连接到我的登录按钮的命令来控制ProgressBar的可见性。 这可以工作,但是,只有在整个登录过程完成后,UI才会更新以显示ProgressBar,从而使得进度指示符毫无意义。
如何首先设置ProgressBar的可见性(并有UI更新),然后关闭执行我的登录过程?
这里是一些代码:
XAML
<ProgressBar IsIndeterminate="True" Height="1" Visibility="{Binding Login.ProgressVisibility, UpdateSourceTrigger=PropertyChanged, FallbackValue=Collapsed}" />
视图模型
public class LoginViewModel : ViewModelBase { private IDialogService dialogService; private INavigationService navigationService; private string _username; private string _password; private Visibility _progressVisibility = Visibility.Collapsed; /// <summary> /// User login name / email address /// </summary> public string Username { get { return _username; } set { Set(() => Username, ref _username, value); } } /// <summary> /// User password /// </summary> public string Password { get { return _password; } set { Set(() => Password, ref _password, value); } } /// <summary> /// Is loading in progress /// </summary> public Visibility ProgressVisibility { get { return _progressVisibility; } set { Set(() => ProgressVisibility, ref _progressVisibility, value); } } /// <summary> /// Perform login action /// </summary> public RelayCommand LoginCommand { get; private set; } /// <summary> /// constructor /// </summary> /// <param name="dialogService"></param> public LoginViewModel(IDialogService dialogService, INavigationService navigationService) { this.LoginCommand = new RelayCommand(this.Login, CanLogin); this.dialogService = dialogService; this.navigationService = navigationService; } public void Login() { this.ProgressVisibility = Visibility.Visible; //check the credentials have been provided if (this._username == null || this._password == null) { //show dialogue dialogService.ShowMessage("Please enter you login credentials", "Login"); } else { //perform an authentication request with the service ValidateLoginRequest request = new ValidateLoginRequest(); request.Device = new Device(); request.UserName = this.Username; request.Password = this.Password; var response = Task.Run(() => LoginAsync(request)).GetAwaiter().GetResult(); if (response != null) { if (response.IsSuccessful) { navigationService.NavigateTo("Main"); } else { dialogService.ShowMessage(response.ErrorCode + " :" + response.ErrorMessage, "Login"); } } else { //failure dialogService.ShowMessage("ECOM000: An unknown error has occurred", "Login"); } } } async Task<ValidateLoginResponse> LoginAsync(ValidateLoginRequest request) { Model.RuntimeDataService proxy = new Model.RuntimeDataService(); ValidateLoginResponse response = (ValidateLoginResponse)await proxy.PostAsync(request, typeof(ValidateLoginResponse), "ValidateLogin"); return response; } public bool CanLogin() { return true; } }
您正在创建一个后台Task
,然后导致UI线程等待响应。
尝试等待从LoginAsync
方法返回的Task
:
var response = await LoginAsync(request);
这应该允许您的UI线程继续,其余的方法作为回调。
您可能需要将async
关键字添加到Login
方法。
我使用Kotlin加入我自己的CustomBindingAdapter来解决这个问题:
@BindingAdapter("android:visibility") fun setVisibility(view: View, visible: Boolean) { view.visibility = if (visible) View.INVISIBLE else View.VISIBLE }
在你的view.xml里面:首先添加数据变量:
<data> <variable name="loginViewModel" type="yourpackage.viewmodel.LoginViewModel"/> </data> <ProgressBar android:id="@+id/login_progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:visibility="@{!loginViewModel.loginProgress}" />
您的活动必须具有:
val binding = DataBindingUtil.setContentView<LoginViewBinding>(this, R.layout.login_view) val loginViewModel = LoginViewModel(this) binding.setLoginViewModel(loginViewModel)
最后,您的ViewModel需要处理可见性:
var loginProgress = ObservableField<Boolean>() fun doSomething(){ this.loginProgress.set(true) }
并设置loginProgress.set(false)
无论你需要停止progressBar。
我希望这可以帮助别人;)