【问题标题】:Set ProgressBar visibility from ViewModel before and after retrieving data from webservice service在从 web 服务检索数据之前和之后从 ViewModel 设置 ProgressBar 可见性
【发布时间】:2015-12-04 04:08:56
【问题描述】:

我正在使用 MVVM 设计模式和 MVVM Light 工具包创建一个 Windows Phone 8.1 应用程序。

我正在尝试创建一个简单的登录页面,该页面从用户那里获取用户名和密码,并通过网络服务进行身份验证。在此身份验证过程中,我想在页面顶部显示一个 ProgressBar(加载点)以指示正在发生的事情。

我已经成功创建了我的 ViewModel 并绑定了我的 View 以允许我通过附加到我的登录按钮的命令来控制 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;
    }
}

【问题讨论】:

    标签: mvvm windows-phone-8.1 async-await mvvm-light


    【解决方案1】:

    我通过添加自己的 CustomBindingAdapter 使用 Kotlin 解决了这个问题:

    @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 的位置。

    我希望这可以帮助其他人;)

    【讨论】:

      【解决方案2】:

      您正在创建背景Task,然后导致 UI 线程等待响应。

      尝试等待从LoginAsync 方法返回的Task

      var response = await LoginAsync(request);
      

      这应该允许您的 UI 线程继续,该方法的其余部分充当回调。

      您可能需要将 async 关键字添加到 Login 方法中。

      【讨论】:

      • 这很有效,谢谢。我不知道为什么我最终会使用 Task.Run
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-03
      • 2021-10-21
      • 1970-01-01
      • 1970-01-01
      • 2018-02-08
      • 1970-01-01
      相关资源
      最近更新 更多