-1

I have a problem. I created the following ViewModel:

public class TemplateListViewModel
{
    public double WidthHeight { get; set; }

    public ICommand LoadTemplates => new Command(MyHandler);
    public int CurrentTemplateCountReceived;
    public bool HitBottomOfList = false;
    public ObservableCollection<TemplateSource> sourceList { get; set; }


    public TemplateListViewModel()
    {
        CurrentTemplateCountReceived = 0;
        sourceList = new ObservableCollection<TemplateSource>();

        var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
        var width = mainDisplayInfo.Width;
        var density = mainDisplayInfo.Density;
        var ScaledWidth = width / density;

        WidthHeight = (ScaledWidth / 2);

        loadingTemplates += onLoadingTemplates;
        LoadTemplateList();
    }

    private event EventHandler loadingTemplates = delegate { };

    private Task LoadTemplateList()
    {
        loadingTemplates(this, EventArgs.Empty);
        return null;
    }

    private async void onLoadingTemplates(object sender, EventArgs args)
    {
        if (HitBottomOfList == false)
        {
            List<Template> templateList = await App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived);

            if (templateList != null)
            {
                foreach (var template in templateList)
                {
                    ImageSource source = ImageSource.FromUri(new Uri("http://alexander.vreeswijk.eu/memento/" + App.TemplateSource + template.FileName));
                    TemplateSource templateSource = new TemplateSource { Id = template.Id, Source = source, WidthHeight = WidthHeight, FileName = template.FileName };
                    sourceList.Add(templateSource);
                }

                CurrentTemplateCountReceived = sourceList.Count;
            }
            else
            {
                HitBottomOfList = true;
            }
        }
    }

    bool handling = false;

    public async void MyHandler()
    {
        // already handling an event, ignore the new one
        if (handling) return;

        handling = true;

        await LoadTemplateList();

        handling = false;
    }
}

And here is the RestService.cs:

HttpClient client;
public string ErrorMessage { get; set; }

public RestService()
{
    client = new HttpClient();
    client.MaxResponseContentBufferSize = 256000;
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
}

public async Task<List<Template>> GetTemplates(User user, int offset)
{
    var postData = new List<KeyValuePair<string, string>>();
    postData.Add(new KeyValuePair<string, string>("un", user.Username));
    postData.Add(new KeyValuePair<string, string>("pw", user.Password));
    postData.Add(new KeyValuePair<string, string>("offset", offset.ToString()));
    var content = new FormUrlEncodedContent(postData);
    var weburl = "mysite.org/myapp/get_templates.php";
    List<Template> response = await PostResponseTemplates(weburl, content);

    return response;
}

public async Task<List<Template>> PostResponseTemplates(string weburl, FormUrlEncodedContent content)
{
    var response = await client.PostAsync(weburl, content);
    var json = await response.Content.ReadAsStringAsync();

    if (json != "Nothing")
    {
        var jObject = JObject.Parse(json);
        var templatePropery = jObject["Templates"] as JArray;
        List<Template> templateList = new List<Template>();

        foreach (var property in templatePropery)
        {
            List<Template> propertyList = new List<Template>();
            propertyList = JsonConvert.DeserializeObject<List<Template>>(property.ToString());
            templateList.AddRange(propertyList);
        }

        var sourcePropery = (JObject)jObject["Source"];
        foreach (var property in sourcePropery)
        {
            string tempplateSource = property.Value.Value<string>();
            App.TemplateSource = tempplateSource;
        }

        return templateList;
    }
    else
    {
        ErrorMessage = json;
        return default(List<Template>);
    }
}

The ICommand LoadTemplates => new Command(MyHandler); gets called when the CollectionView almost hit the bottom of the list, so it would get more data. Then I created the MyHandler to check if there is already an event busy with the webcall to get more data, because it fires the event multiple times. Finally it executes the onLoadingTemplates which gets the data from my webpage.

Now on first run with the LoadTemplateList(); in the constructor everything works great, but when it executes it like: await LoadTemplateList();, when the event is triggered it crashes on the following line in the RestService.cs: var response = await client.PostAsync(weburl, content); with the error:

'Object reference not set to an instance of an object.'

Can someone tell my why this is happening when it gets data when the event is triggered?

deczaloth
  • 7,094
  • 4
  • 24
  • 59
A. Vreeswijk
  • 822
  • 1
  • 19
  • 57
  • I recommend this read as well: https://josefottosson.se/you-are-probably-still-using-httpclient-wrong-and-it-is-destabilizing-your-software/ – Joel Wiklund Jan 11 '20 at 10:29
  • And this https://stackoverflow.com/questions/12144077/async-await-when-to-return-a-task-vs-void – Joel Wiklund Jan 11 '20 at 10:33

2 Answers2

1

I would risk to bet on the current CollectionView bug.

It's not the "called second time" case but the renderer just crashes when you change the already loaded bound ObservableCollection (and the window with CollectionView is hidden).

My first thought was that somewhere in the renderer it was executing on the the calling non-UI thread. But even when you try to modify the bound ObservableCollection from UI-thread the renderer still crashes.

You can be so kind to report this bug i had no time to do it, actually just using other customized listviews.

Nick Kovalsky
  • 5,378
  • 2
  • 23
  • 50
0

Doing some detective work here.

Since you said that you are getting a NullReferenceException

Object reference not set to an instance of an object.

in the line

var response = await client.PostAsync(weburl, content);

where neither weburl or content can be null since were created previously in the GetTemplates method, it only remains the possibility that client is null in the former line: if that is the case, you should make sure you are setting the following properties in your client after it is created:

httpClient.DefaultRequestHeaders.ConnectionClose = false;
HttpWebRequest.KeepAlive = true;

There is a nice post on HttpClient BestPractices in the MSFT docs.

Hope this helps!

deczaloth
  • 7,094
  • 4
  • 24
  • 59