Search code examples
c#multithreadingasynchronoustask

Task does not complete when showing splash screen


I am developing and application that loads a large amount of front end data when the program is opened. To let the user know the program has started I want to show a splash screen while it checks for the user login as well as load additional information. The issue I am having is I can't get the splash screen to stay visible with out dead locking the application.

public ProgramMain()
{
    InitializeComponent();
    LoadDataAsync().Wait();
    _viewPageManager = new ViewPageManager(MainTabControl);
}

protected async Task LoadDataAsync()
{
    StartUp.StartUpForm progressForm = new StartUp.StartUpForm();
    progressForm.Show();
    // 'await' long-running method by wrapping inside Task.Run
    await Task.Run(() =>
    {                
        AppSetting setting = new AppSetting();
        SqlHelper helper = new SqlHelper(setting.GetConnectionString("cn"));
        if(!helper.HandShake())
        {
            UserLogin userLogin = new UserLogin();
            if(userLogin.ShowDialog() == DialogResult.OK)
            {
                MessageBox.Show("Logged In");
                return Task.FromResult(true);
            }
            else
            {
                return Task.FromResult(false);
            }
        }
        else
        {
            return Task.FromResult(true);
        }
    }
    ).ContinueWith(new Action<Task>(task =>
    {
        //if (this.IsHandleCreated)
        // Close modal dialog
        // No need to use BeginInvoke here
        //   because ContinueWith was called with TaskScheduler.FromCurrentSynchronizationContext()
        progressForm.Close();

    }), TaskScheduler.FromCurrentSynchronizationContext());
}

Solution

  • public class ProgramStartUp : WindowsFormsApplicationBase
    {
         protected override void OnCreateSplashScreen()
         {
             SplashScreen = new StartUp.StartUpForm();            
         }
    
         protected override void OnCreateMainForm()
         {
             var task = Task.Run(() => LoadDataAsync());
             task.Wait();
             MainForm = new ProgramMain();
         }
         
         protected async Task LoadDataAsync()
         {
             // 'await' long-running method by wrapping inside Task.Run
             bool handshakeSuccessful = await Task.Run(() =>
             {
                 AppSetting setting = new AppSetting();
                 SqlHelper helper = new SqlHelper(setting.GetConnectionString("cn"));
                 if (!helper.HandShake())
                 {
                     UserLogin userLogin = new UserLogin();
                     if (userLogin.ShowDialog() == DialogResult.OK)
                     {                                                
                         return true;
                     }
                 }
                 return false;
             });
         }
    }
    

    Edit to keep UI on the same thread

    I am new to using threads so let me know if this is correct.

     protected override void OnCreateMainForm()
    {
    SystemUsers users = new SystemUsers();
    
    var progress = new Progress<LoginStatus>();
    LoginStatus status = new LoginStatus();
    while (status != LoginStatus.Success || status == LoginStatus.Disconnected)
    {
        if (status != LoginStatus.Failure) { status = Task.Run(async () => await users.Login(progress)).GetAwaiter().GetResult(); }
        string userName = "";
        string password = "";
        if (status == LoginStatus.Failure)
        {
            UserLogin userLogin = new UserLogin(users);
            if (userLogin.ShowDialog() == DialogResult.Continue)
            {
                userName = userLogin.UserName;
                password = userLogin.Password;
            }
            else
            {
                status = LoginStatus.Disconnected;
            }
            status = Task.Run(async () => await users.Login(progress, userName, password)).GetAwaiter().GetResult();
            if (status == LoginStatus.Failure)
            {
                MessageBox.Show("Error Incorrect Username or Password Entered", "Invlaid User Information", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
    
    
    MainForm = new StruHubMain(users);
    }
    
    
    public async Task<LoginStatus> Login(IProgress<LoginStatus> progress)
    {
    LoginStatus loginStatus = new LoginStatus();
    loginStatus = LoginStatus.Attempt;
    if (progress != null) { progress.Report(loginStatus); }
    await Task.Run(() =>
    {
        AppSetting setting = new AppSetting();
        SqlHelper helper = new SqlHelper(setting.GetConnectionString("cn"));
        if (helper.HandShake())
        {
            setting.SaveConnectionString("conn", setting.GetConnectionString("conn"));
            _currentUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            if (_users == null && fillUsers)
            {
                FillUsers();
    
            }
            loginStatus = LoginStatus.Success;
        }
        else
        {
            loginStatus = LoginStatus.Failure;
        }
    });
    progress.Report(loginStatus);
    return loginStatus;
    }
    public async Task<LoginStatus> Login(IProgress<LoginStatus> progress,string userName, string password)
    {
    LoginStatus loginStatus = new LoginStatus();
    loginStatus = LoginStatus.Attempt;
    if (progress != null) { progress.Report(loginStatus); }
    await Task.Run(() =>
    {
        AppSetting setting = new AppSetting();
        SqlHelper helper = new SqlHelper(setting.GetConnectionString("cn").Replace("Integrated Security=True", $"User ID={userName};pwd={password};Integrated Security=False"));
        if (helper.HandShake())
        {
            setting.SaveConnectionString("conn", setting.GetConnectionString("cn").Replace("Integrated Security=True", $"User ID={userName};pwd={password};Integrated Security=False"));
            helper = new SqlHelper(setting.GetConnectionString("conn"));
            if (helper.HandShake())
            {
                _currentUser = userName;
                if (_users == null && fillUsers)
                {
                    FillUsers();
    
                }
                loginStatus = LoginStatus.Success;
            }
            else
            {
                loginStatus = LoginStatus.Failure;
            }
        }
        else
        {
            loginStatus = LoginStatus.Failure;
        }
    });
    progress.Report(loginStatus);
    return loginStatus;
    }