【问题标题】:Async Wait issues with view updation in UWPUWP 中视图更新的异步等待问题
【发布时间】:2017-11-19 15:05:42
【问题描述】:

在我的 UWP 应用中,我正在根据 API 调用返回的数据更新我的 UI。 MyPage 是我显示从 API 调用返回的数据的页面,我就是这样做的:

public sealed partial class MyPage : Page
{
    private MyViewModel _viewModel;
    private MyApiResponse _result;
    private string _documentId;

    public DocumentDetailsPage()
    {
        this.InitializeComponent();            
        _viewModel = new MyViewModel();            
    }

    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);    
        _documentId = (string)e.Parameter;
        await GetDocumentDetails();
    }

    private async Task GetDocumentDetails()
    {   
        //THIS WORKS, BUT USING await INSTESD OF .Result DOESN'T UPDATE THE VIEW
        _result =  new MyApiCall(_documentId).GetResponseAsync().Result;
        PrepareViewModel();        
    }

    private void PrepareViewModel()
    {       
        viewModel.Type = _result.response.type;
        viewModel.VolumeNumber = _result.response.volumeNumber;
    }
}

这是向 API 发出 Http-POST 请求的类:

public class MyApiCAll
    {
        ...
        ...

        public async Task<MyApiResponse> GetResponseAsync()
        {
            MyApiResponse responseObject;
            using(HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.TryAddWithoutValidation("ABC", ABC);
                var response =   await _client.PostAsync("someURL", null).ConfigureAwait(false);
                var responseJson =  await response.Content.ReadAsStringAsync();
                responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<MyApiResponse>(responseJson);        
            }
            return responseObject;  
        }
    }

这是我的观点:

<StackPanel Style="{StaticResource ContainerStackPanel}"
            Visibility="{x:Bind viewModel.Type, Converter={StaticResource ConverterNameHere}, Mode=OneWay}">
    <TextBlock Text="ABC"/>
    <TextBlock Text="{x:Bind viewModel.Type, Mode=OneWay}"/>
</StackPanel>

<StackPanel Style="{StaticResource ContainerStackPanel}"
            Visibility="{x:Bind viewModel.volumeNumber, Converter={StaticResource ConverterNameHere}, Mode=OneWay}">
    <TextBlock Text="ABC"/>
    <TextBlock Text="{x:Bind viewModel.volumeNumber, Mode=OneWay}"/>
</StackPanel>

这工作得很好,只是它会阻塞 UI,所以当我从方法调用中删除 Result 并添加这样的等待时:

_result = await new DocumentDetailsApiCall(_documentId).GetResponseAsync();

然后,我的 UI 只是不显示绑定值,它保持空白。我可以确认 viewModel 属性确实得到了更新。

我没有在我的 ViewModel 属性上实现INotifyPropertyChanged(我尝试实现它,没有改变任何东西)

更新按照建议,我已将 INPC 添加到我的 viewModel 并继承我的 ViewModel 类

class MyViewModel : INotifyPropertyChanged
{
    private string _type;
    public string Type
    {
        get { return _type; }
        set
        {
            _type = value;
            this.OnPropertyChanged();
        }
    }

    private string _volumeNumber;
    public string VolumeNumber
    {
        get { return _volumeNumber; }
        set
        {
            _volumeNumber = value;
            this.OnPropertyChanged();
        }
    }        

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    { 
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

问题依然存在,我无法使用await 运算符。 .Result 仍然有效。

【问题讨论】:

  • 除非你有 INPC,否则它绝对不会工作;也许您的实施不正确?还; x:Bind 是怎么回事?
  • 1) 视图模型应该实现INotifyPropertyChanged 来更新视图。这就是拥有它们的全部目的。 2) 移除阻塞调用.Result 3) 确保视图中使用的属性名称与视图模型属性名称匹配。即volumeNumber,除非那是一个错字。
  • 如果没有一个好的minimal reproducible example 可靠地重现问题,就不可能提供实际的答案。但请注意{x:Bind} 的默认模式是OneTime。您在绑定的Text 属性上正确设置了OneWay,但不是Visibility 属性。因此,一旦设置了初始绑定值,对属性值的更改就不会影响可见性。
  • 因为在没有 INotifyProperryChanged 的​​情况下使用 .Result 而不是 await 可以完美工作,我认为没关系(该方法也是第一个也是唯一一个将值分配给 viewmodel 的地方)。我将做出建议的更改并更新我的答案。
  • 尝试拨打Bindings.Update()?

标签: c# uwp async-await


【解决方案1】:

由于调用Bindings.Update() 解决了您的问题,您的绑定表达式应该是正确的,但不知何故属性更改通知失败了。

我不打算在这里猜测真正出了什么问题,而是解释什么时候应该使用Bindings.Update(),以及什么时候应该使用INPC + OneWay 绑定。

Bindings.Update() 只适用于x:Bind,与传统绑定不同,如果您的 UI 很少需要更新新数据,则不必在属性中实现 INPC,实际上,它实际上要便宜得多并且执行OneTime 绑定(调用Bindings.Update())比使用OneWay 绑定实现INPC 性能更高。

因此,以下内容适用于 x:Bind -

<TextBlock Text="{x:Bind MyText}" Style="{StaticResource SubheaderTextBlockStyle}" />

public string MyText { get; set; }

private void Button_Click(object sender, RoutedEventArgs e)
{
    MyText = "new text!!";
    Bindings.Update();
}

单击按钮后,TextBlock 将填充新文本!!。注意这里我使用默认的OneTime 绑定(即{x:Bind MyText}),MyText 只是一个普通的 CLR 属性。

这只是因为我最后调用了Bindings.Update(),这会强制重新初始化OneTime 绑定。

但是,就像我之前所说的,请仅在您的 UI很少需要更新时考虑使用上述方法。大多数时候情况并非如此,因此您仍然会实现INPC 并编写OneWay 绑定,根本不需要使用Bindings.Update()

【讨论】:

    猜你喜欢
    • 2021-10-11
    • 1970-01-01
    • 2018-12-09
    • 1970-01-01
    • 2022-10-25
    • 2012-10-26
    • 2020-05-14
    • 2021-05-24
    • 2021-04-13
    相关资源
    最近更新 更多