Search code examples
c#multithreadingtaskdotnet-httpclientmultitasking

Why first click event of button not working


I have app(net4.7.2) like this: app screen 1

Program is simple, when user presses OK, im sending request to steam market to get informations about item which user entered (item steam market url) to textbox.

But when im trying to send request, first click event of button not working: app screen 2

Code of OK button:

    private void btnOK_Click(object sender, EventArgs e)
    {
        if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
        {
            Helpers.Helper.BuildURL(txtItemURL.Text);

            SteamMarketItem SMI = Helpers.Helper.GetItemDetails();
            lblPrice.Text = SMI.LowestPrice.ToString() + "$";
            pbItemImage.ImageLocation = SMI.ImagePath;

            Helpers.Helper.Kontrollar_BerpaEt();
        }
        else
        {
            Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
        }
    }

Method GetItemDetails():

    public static SteamMarketItem GetItemDetails()
    {
        WinForms.Control.CheckForIllegalCrossThreadCalls = false;

        Task.Run(() =>
        {
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    JavaScriptSerializer serializer = new JavaScriptSerializer();

                    /* Get item info: */
                    var ResultFromEndpoint1 = client.GetAsync(ReadyEndpointURL1).Result;
                    var Json1 = ResultFromEndpoint1.Content.ReadAsStringAsync().Result;
                    dynamic item = serializer.Deserialize<object>(Json1);
                    marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));

                    /* Get item image: */
                    var ResultFromEndpoint2 = client.GetAsync(ReadyEndPointURL2).Result;
                    var Json2 = ResultFromEndpoint2.Content.ReadAsStringAsync().Result;
                    var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];

                    HtmlDocument htmlDoc = new HtmlDocument();
                    htmlDoc.LoadHtml(html);
                    marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[@class='market_listing_item_img']").Attributes["src"].Value + ".png";

                    Kontrollar_BerpaEt();
                }
            }
            catch
            {
                Kontrollar_SifirlaYanlisDaxilEdilib();
            }
        });

        return marketItem;
    }

Class SteamMarketItem:

public class SteamMarketItem
{
    public string ImagePath { get; set; }
    public float LowestPrice { get; set; }
}

When im using Task.Run() first click not working, without Task.Run() working + but main UI thread stopping when request not finished.

I have no idea why this happens, I cant find problem fix myself, I will be glad to get help from you. Thanks.


Solution

  • If you want to use async you need to change your event handler to async so you can use await, please see the following:

    1. Change your Event handler to async void, async void is acceptable on event handler methods, you should try to use async Task in place of async void in most other cases, so change your method signature to the following:

       private async void btnOK_Click(object sender, EventArgs e)
       {
            if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
            {
                Helpers.Helper.BuildURL(txtItemURL.Text);
                //here we use await to await the task   
                SteamMarketItem SMI = await Helpers.Helper.GetItemDetails();
                lblPrice.Text = SMI.LowestPrice.ToString() + "$";
                pbItemImage.ImageLocation = SMI.ImagePath;
    
                Helpers.Helper.Kontrollar_BerpaEt();
            }
            else
            {
                Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
            }
       }
    

    2. You shouldn't need to use Task.Run, HttpClient exposes async methods and you can make the method async, also, calling .Result to block on an async method is typically not a good idea and you should make the enclosing method async so you can utilize await:

       //Change signature to async and return a Task<T>
       public async static Task<SteamMarketItem> GetItemDetails()
       {
           WinForms.Control.CheckForIllegalCrossThreadCalls = false;
           //what is marketItem?? Where is it declared?
           try
           {
               using (HttpClient client = new HttpClient())
               {
                   JavaScriptSerializer serializer = new JavaScriptSerializer();
    
                    /* Get item info: */
                    var ResultFromEndpoint1 = await client.GetAsync(ReadyEndpointURL1);
                    var Json1 = await ResultFromEndpoint1.Content.ReadAsStringAsync();
                    dynamic item = serializer.Deserialize<object>(Json1);
                    marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));
    
                    /* Get item image: */
                    var ResultFromEndpoint2 = await client.GetAsync(ReadyEndPointURL2);
                    var Json2 = await ResultFromEndpoint2.Content.ReadAsStringAsync();
                    var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];
    
                    HtmlDocument htmlDoc = new HtmlDocument();
                    htmlDoc.LoadHtml(html);
                    marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[@class='market_listing_item_img']").Attributes["src"].Value + ".png";
    
                    Kontrollar_BerpaEt();
               }
           }
           catch
           {
                Kontrollar_SifirlaYanlisDaxilEdilib();
           }
           //what is marketItem?? Where is it declared?
           return marketItem;
       }