【问题标题】:c# Mvvm fill ObservableObject property with data from GET requestc# Mvvm 用来自 GET 请求的数据填充 ObservableObject 属性
【发布时间】:2017-01-15 06:38:27
【问题描述】:

在我的 .Net Mvvm/MVC 学习过程中,我偶然发现了另一个问题。

我正在尝试填充 ObservableObject 属性,以便将其绑定到我的窗口或用户控件。该属性应填充到执行 GET 请求的 private async void 中。

由于您不能在属性中使用await 方法/函数,因此我尝试在方法中填充属性。它总是给NullReferenceException。 在设置换行符并按 F11(跳过)很多次后,我发现除了所需的属性 Events 之外,所有内容都已正确填充。

流程是这样的:

  1. 您选择一个国家 -> selectedItem 绑定到 SelectedCountry
  2. 选择一个“省” -> selectedItem 绑定到 SelectedProvince
  3. 选择一个城市 -> selectedItem 绑定到 SelectedCity

上述每个属性都会触发一个与我的 API 通信的方法,该 API 用于从我的数据库中获取数据并显示/返回它。 ObservableObject 属性“国家、省、市”在这些与 API 通信的方法中设置。此时一切仍然正常。

当设置City 属性时,它调用方法GetOrgsByCity() 填充另一个ObservableObject 属性Orgs。在Orgs 的setter 中,调用了GetFacebookData(FB_IDS)。此方法需要一个List<string> 类型的参数,并且是一个返回LINQ 过滤列表的属性。一切仍然正常。

问题来了:

GetFacebookData(FB_IDS) 方法中,我试图填充一个名为EventsObservableObject 属性,该方法向Facebook 执行GET 请求。 IsSuccessStatusCode 等于 true 并且除了 ObservableObject 属性 Events 之外的所有内容都被正确填充。

我正在“转换”结果列表 (List<FB> data) 与 foreach 以填充 Events。当我运行它时,它会中断并抛出 NullReferenceException。 意思是Events 为null 什么的,因为其他变量都填写正确了。

有没有人知道为什么Events 属性在尝试填充它时会射出NullReferenceException

xaml(绑定的地方):

    <Grid Grid.Row="0" DataContext="{Binding Source={StaticResource PartOneVM}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="30px" />
            <RowDefinition Height="30px" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>

        <TextBlock x:Name="txbCountry" Style="{StaticResource InfoLabelCountry}" />
        <ComboBox x:Name="cboCountry" Style="{StaticResource CountryBox}" ItemsSource="{Binding Countries}" DisplayMemberPath="En_name" SelectedItem="{Binding SelectedCountry}" />
        <TextBlock x:Name="txbGewest" Style="{StaticResource InfoLabelGewest}" />
        <ComboBox x:Name="cboGewest" Style="{StaticResource GewestBox}" ItemsSource="{Binding Provinces}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedProvince}" />
        <TextBlock x:Name="txbCity" Style="{StaticResource InfoLabelCity}" />
        <ComboBox x:Name="cboCity" Style="{StaticResource CityBox}" ItemsSource="{Binding Cities}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCity}" />
    </Grid>
    <Grid Grid.Row="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <ListBox Height="300px" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Events}">

        </ListBox>
    </Grid>

ViewModel 问题发生在底部

public class PartOneVM : ObservableObject, IPage
{
    #region props
    public string Name { get { return "Page One"; } }

    private ObservableCollection<Country> _countries;

    public ObservableCollection<Country> Countries
    {
        get { return _countries; }
        set { _countries = value; OnPropertyChanged("Countries"); }
    }
    private ObservableCollection<Province> _provinces;

    public ObservableCollection<Province> Provinces
    {
        get { return _provinces; }
        set { _provinces = value; OnPropertyChanged("Provinces"); }
    }
    private ObservableCollection<City> _cities;

    public ObservableCollection<City> Cities
    {
        get { return _cities; }
        set { _cities = value; OnPropertyChanged("Cities"); }
    }
    private Country _selectedCountry;

    public Country SelectedCountry
    {
        get { return _selectedCountry; }
        set { _selectedCountry = value; GetProvincesByCountry(); OnPropertyChanged("SelectedCountry"); }
    }
    private Province _selectedProvince;

    public Province SelectedProvince
    {
        get { return _selectedProvince; }
        set { _selectedProvince = value; GetCitiesByProvince(); OnPropertyChanged("SelectedProvince"); }
    }
    private City _selectedCity;

    public City SelectedCity
    {
        get { return _selectedCity; }
        set { _selectedCity = value; GetOrgsByCity(); OnPropertyChanged("SelectedCity"); }
    }
    private ObservableCollection<Org> _orgs;

    public ObservableCollection<Org> Orgs
    {
        get { return _orgs; }
        set { _orgs = value; GetFacebookData(FB_IDS); OnPropertyChanged("Orgs"); }
    }
    public List<string> FB_IDS
    {
        get { if (Orgs == null) return null; return (from s in Orgs where s.CityID.Equals(SelectedCity.ID) select s.FB_ID).Distinct().ToList<string>(); }
    }
    private ObservableCollection<FB> _events;
    public ObservableCollection<FB> Events
    {
        get { return _events; }
        set { _events = value; OnPropertyChanged("Events");  }
    }
    #endregion

    #region ctor
    public PartOneVM()
    {
        GetCountries();
    }
    #endregion

    #region methodes
    private async void GetCountries()
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync("http://localhost:58564/api/country");
            if (response.IsSuccessStatusCode)
            {
                string json = await response.Content.ReadAsStringAsync();
                Countries = JsonConvert.DeserializeObject<ObservableCollection<Country>>(json);
            }
        }
    }
    private async void GetProvincesByCountry()
    {
        if (SelectedCountry != null)
        {
            using (HttpClient client = new HttpClient())
            {
                HttpResponseMessage response = await client.GetAsync("http://localhost:58564/api/province/GetProvincesByCountry/" + SelectedCountry.ID);
                if (response.IsSuccessStatusCode)
                {
                    string json = await response.Content.ReadAsStringAsync();
                    Provinces = JsonConvert.DeserializeObject<ObservableCollection<Province>>(json);
                }
            }
        }
    }
    private async void GetCitiesByProvince()
    {
        if (SelectedCountry != null && SelectedProvince != null)
        {
            using (HttpClient client = new HttpClient())
            {
                HttpResponseMessage response = await client.GetAsync("http://localhost:58564/api/city/GetCitiesByProvince/" + SelectedCountry.ID + "/" + SelectedProvince.ID);
                if (response.IsSuccessStatusCode)
                {
                    string json = await response.Content.ReadAsStringAsync();
                    Cities = JsonConvert.DeserializeObject<ObservableCollection<City>>(json);
                }
            }
        }
    }
    private async void GetOrgsByCity()
    {
        if (SelectedCountry != null && SelectedProvince != null && SelectedCity != null)
        {
            using (HttpClient client = new HttpClient())
            {
                HttpResponseMessage response = await client.GetAsync("http://localhost:58564/api/org/GetOrgsByCity/" + SelectedCountry.ID + "/" + SelectedProvince.ID + "/" + SelectedCity.ID);
                if (response.IsSuccessStatusCode)
                {
                    string json = await response.Content.ReadAsStringAsync();
                    Orgs = JsonConvert.DeserializeObject<ObservableCollection<Org>>(json);
                }
            }
        }
    }
    private async void GetFacebookData(List<string> fb_ids)
    {
        var l = fb_ids
       .Select((x, i) => new { Index = i, Value = x })
       .GroupBy(x => x.Index / 50)
       .Select(x => x.Select(v => v.Value).ToList())
       .ToList();

        for (var i = 0; i < l.Count; i++)
        {
            string ids = string.Join(",", l[i]);
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(@"https://graph.facebook.com/v2.8/");
                HttpResponseMessage response = await client.GetAsync("?ids=" + ids + "&fields=id,name,events.limit(60)&access_token=<acces_token>");
                if (response.IsSuccessStatusCode)
                {
                    string json = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<IDictionary<string, FB>>(json);
                    List<FB> data = result.Select(item => item.Value).ToList();

                    if (data != null)
                    {
                        foreach (FB item in data)
                        {
                            if (item != null)
                            {
                                Events.Add(item); // THROWS NullReferenceException !!!
                            } 
                        }
                    }
                }
                else
                {
                 // error handling   
                }
            }
        }
    }
    #endregion
}

由于其他一切正常,我认为没有必要包含所有模型。

【问题讨论】:

    标签: c# wpf mvvm binding


    【解决方案1】:

    首先-永远不要使用'async void'-这有时可能只适用于事件处理程序,但在这里不适用。使用“异步任务”,然后您将能够在 GetFacebook 方法(或其他异步方法之一)真正完成后使用 ContinueWith 构造调用 OnPropertyChanged。

    第二 - 事件正在抛出异常,因为它没有被初始化。你应该改变:

    private ObservableCollection<FB> _events = new ObservableCollection<FB>();
    

    如果您再次调用 GetFacebookData,请不要忘记清除此列表。

    【讨论】:

    • 谢谢,初始化成功。我没有考虑到这一点,因为所有其他 ObservableObjects 似乎不需要在填充之前进行初始化......而且我从异步任务开始但遇到了麻烦,因为它必须在循环中发生。我会再试一次任务。谢谢
    • 您知道为什么我的列表框保持空白,而该属性的计数为 61 吗?
    • 哪个属性?活动?
    • 是的,绑定了这个属性的列表框保持为空
    猜你喜欢
    • 2015-11-15
    • 1970-01-01
    • 2019-06-08
    • 2020-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多