-5

I have an app which can load XML files and then parse it into some different classes. It has it's own window with button like "Load File"

There are some schematic code what it looks like

///In main window 

public void Button_Click ()
{
   //(VM)DataContext.AddNewFile();
}

///In VM

public void AddNewFile()
{
   //Some code with file dialogs and messages with several methods, one of which leads to AddMyType()
}

private void AddMyType(string file)
{
   //Creates class of myType
}

///In Type class constructor

internal MyType(string file)
{
   //Launches XML parser class to parse data from XML and create from this all MyType
   data = ParseFile(file).Result;
}

///In XML parser class:

internal async Task<MyTypeData>ParseFile(file)
{
   //Main method which launches background tasks and shows messages with progress bar 
   //Should update UI

   _progress = new Progress<int>(number =>
   {
       _message.UpdateProgress(number);
   });

   someData = await Task.Run(()=> BackgroundWorkTask(rawData, _progress));
   _message.Show(_maximumProgressValue); //Shows custom message with progress bar
   someOtherData = await Task.Run(()=> BackgroundWorkTask(otherRawData, _progress));
}

private <T> Task BackgroundWorkTask(rawData)
{
   //Some big work going here and gives some result
}

The problem is that UI freezes.

I tried many variants:

Task.Wait(), await BackgroundWorkTask and tested it with

private <T> Task BackgroundWorkTask()
{
   //Freezes
   while(true){} 
   Task.Delay(x)
   Task.Delay(x).Wait()
   Thread.Sleep(x)
   and await Task.Delay(x) (if private async Task)
}

I guess it's because all main work BEFORE is going in sync mode in main thread. But i really have lots of different operations with data in main thread before start ParseFile() method.

As I understand async method with await Task.Run(()=>); is used to do some work on other thread and shouldn't block UI.

I thought that using async method where I will update UI with await different tasks can help but....

If there a way to solve this problem?

Alex
  • 3
  • 2
  • 8
    You wrote `ParseFile(file).Result;`. It blocks your constructor execution until your ParseFile method completes. – ettudagny Dec 22 '20 at 10:41
  • Guideline: always `await` method calls that return a `Task` and thus call them only from `async` methods that return a Task themselves (except for event handlers that can be `async void`) – Hans Kesting Dec 22 '20 at 10:56
  • 1
    Regarding the constructor with the file parsing code, you may be violating OOP principles, check [how much work should be done in a constructor?](https://stackoverflow.com/questions/293967/how-much-work-should-be-done-in-a-constructor) – Cleptus Dec 22 '20 at 10:56
  • Thank you much! I'll check all your recommendations and share the result. Thx for info about constructor. – Alex Dec 22 '20 at 11:50

1 Answers1

2

Using Result is blocking your UI thread, and because you cannot have an async constructor, you will need to explore an alternative approach, of which there are many:

  1. Pass data rather than a file path to the constructor:

    private async Task AddMyType(string file)
    {
       var data = await ParseFile(file);
       var myType = new MyType(data);
    }
    
    ///In Type class constructor
    
    internal MyType(Data data)
    {
        this.data = data;
    }
    

  1. Use an async factory method in MyType:

    class MyType
    {
        private MyType(Data data)
        {
            this.data = data;
        }
    
        internal static async Task<MyType> CreateAsync(string file)
        {
            var data = await ParseFile(file);
            return new MyType(data);
        }
    }
    

  1. Make methods that use data async:

    class MyType
    {
        Task<Data> dataTask;
    
        internal MyType(string file)
        {
            dataTask = ParseFile(file);
        }
    
        public async Task MethodThatUsesData()
        {
            var data = await dataTask;
            // Use data..
        }
    }
    
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35