0

I can't await my task when i load window. I have three viewmodel, with three initialize, and this three initialize have 1 async method to return data from database, and 1 synchronous method. If i try load this data i have exception (ViewModel return null) or my load animation (binded to property) set hidden.

TLDR: Window.Load don't wait when task is complete (like synchronus method been in this task), and fire next.

I search for answer in google, and stack but nothing work for me and CogAnim set False before.

I try doing this in Task.Factory like this

    public MainWindow()
    {
        CogAnim = true;
        InitializeComponent();
        DataContext = this;
        Loaded += MainWindow_Loaded;
    }

    public bool CogAnim { get; set; }

    private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        var t = await Task.Factory.StartNew(async () =>
        {
            await Task.Run(() => ViewModelOne = new ViewModelOne());
            await Task.Run(() => ViewModelTwo = new ViewModelTwo());
            await Task.Run(() => ViewModelThree = new ViewModelThree());
        }).ContinueWith((t1) => ViewModelOne.Initialize()).ContinueWith((t2) => ViewModelTwo.Initialize()).ContinueWith((t3) => ViewModelThree.Initialize());
        t.Wait();
        CogAnim = false;
        OnPropertyChanged("CogAnim");
    }

And try without this await Task.Run(() => {modelname}).

How properly create ViewModels, and use Initialize in it without freeze window? Only work when i do it like this but this freeze gui and load animation dont work

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    ViewModelOne = new ViewModelOne();
    ViewModelTwo = new ViewModelTwo();
    ViewModelThree = new ViewModelThree();
    await ViewModelOne.Initialize();
    await ViewModelTwo.Initialize();
    await ViewModelThree.Initialize();
    CogAnim = false;
    OnPropertyChanged("CogAnim");
}

This Initialize method look like this (maybe there is problem):

public async Task Initialize()
{
    await Task.Delay(2000);
    var t = Repo.GetOne();
    foreach(var a in t)
    {
        CollectionOne.Add(a);
    }

    await Select();
}
public async Task Select()
{
    var t = await Repo.GetOneAsync();
    foreach(var a in t)
    {
        CollectionOne.Add(a);
    }
}
Wieszak
  • 1
  • 1
  • I think you can get your answer here https://stackoverflow.com/questions/8242375/is-it-possible-to-call-async-page-load-in-asp-net-4-5 – Akbar Asghari Jan 18 '20 at 02:57
  • Why is there `Repo.GetOne()` in `Initialize` instead of `await Repo.GetOneAsync()`? What's the purpose of the delay? – Clemens Jan 18 '20 at 07:50
  • @Clemens Can you please reopen this question? The code has several issues that are not addressed by the link you've provided. `Task.Wait` is a potential dead lock. But furthermore is the complete control flow of the threads flawed: the outer `Task` returns a `Task` which needs to be `Unwrap()` to be properly awaitable. Also when the intent is to execute the inner tasks in parallel they need to be awaited with `Task.WhenAll` otherwise they won't execute parallel but synchronized which would be pretty pointless in this scenario he uses `Task.Run` to create instances). – BionicCode Jan 18 '20 at 10:12
  • @Clemens Also continuations are always executed on a background thread which is why (the initial problem this question addresses) the event handler continues execution _before_ the continuation completed. I think it would make sense to open this question for a proper solution or answer. – BionicCode Jan 18 '20 at 10:12
  • Please read my previous comments. They contain the solution to your problem. Either move the complete code that follows the `ContinueWith()` into this continuation method or move the continuation code out, so that it will be executed after the first `await` has completed. – BionicCode Jan 18 '20 at 10:21
  • The first version of MainWindow_Loaded is certainly complete nonsense. However, the second one looks totally ok, and there is nothing to improve. Just perhaps what I was asking above. – Clemens Jan 18 '20 at 10:22
  • @Clemens This is just a show project. I make it to show where i have problem. In work im on intership and i must use synchronous, and asynchronous methods to return data from two database so i make there too two method. – Wieszak Jan 18 '20 at 13:21
  • @BionicCode What about if i make somethind like this to unwrap this task? await Task.Run(async () => { var vm1 = new ViewModelOne(); await vm1.Initialize(); Dispatcher.Invoke(delegate { ViewModelOne = vm1; }); }); Because my second method Freeze UI – Wieszak Jan 18 '20 at 13:23
  • And is there any time consuming code in the view model constructors? Otherwise it wouldn't make sense to wrap the constructor calls in Task.Run. If there is such code, move it to the Initialize methods. – Clemens Jan 18 '20 at 13:24
  • @Clemens In constructors no. In Constructor i only create new list, and observablecollection to use it. I only need to await this Initialize methods on second thread, and dont freeze UI so loding screen can work – Wieszak Jan 18 '20 at 13:24
  • Then `await Task.Run(() => ViewModelOne = new ViewModelOne());` is totally pointless. The second version of MainWindow_Loaded is exactly how it should look like. – Clemens Jan 18 '20 at 13:25
  • Assuming that CollectionOne is an ObservbleCollection that is filled with many elements in Initialize, you may perhaps improve performance by first creating an ordinary List before creating the ObservableCollection and passing the List to its constructor. This would required that the CollectionOne property setter fires a change notification. – Clemens Jan 18 '20 at 13:37
  • @Clemens if you have time, i move my project to Github so you can check it yourself. https://github.com/WieszKto/Wrapper Last commit work good with dispatcher but dont know if i make it properly This second method like you say its good, but freeze UI so i need another option – Wieszak Jan 18 '20 at 13:42
  • I won't do that. If there is anything relevant, post it here. – Clemens Jan 18 '20 at 13:45

0 Answers0