Egor Osaulenko
Egor Osaulenko

Reputation: 149

Correct usage of BackgroundWorker in WPF



I am trying my best with mastering Threading in C# and in WPF particularly.

Background of the problem:
I have a MVVM application, which have a long authentication process (few seconds long). I want to keep the UI responsible (keep app window dragable and resizable, as well - showing preloader with a gif element). To achieve that - I have separated all the UI elements and DB retrievals. After that - I have implemented the A background worker into the Login View.

Here is the code of the Login View:

    private void ValidateLogin()
    {
        Usuario user = new Usuario();
        MainWindow mw = Window.GetWindow(this) as MainWindow;
        mw.preloaderShow();

        BackgroundWorker bw = new BackgroundWorker();

        bw.DoWork += (o, args) =>
        {   
            user = _viewmodel.Login(tbLogin.Text, tbPassword.Password); //TimeConsuming DataRetrieval from DB
        };
        bw.RunWorkerCompleted += (o, args) =>
        {   
            if (user != null)
            {
                if (user.Activo == 0)
                {
                    mw.preloaderHide();
                    CustomMessageBox WrongLoginMessage = new CustomMessageBox("El usuario esta inactivo.");
                    WrongLoginMessage.ShowDialog();
                }
                else
                {
                    AppSession.Instance.SetValue("currentuser", user);
                    btnProceed.Visibility = Visibility.Visible;
                    mw.preloaderHide();
                }
            }
        };

        bw.RunWorkerAsync();
    }

Problem:
Obviously I am falling into a deadlock, on the bw.DoWork, because "The calling thread cannot access this object because a different thread owns it.".

Questions:
1. Since I am not updating any UI in the bw.DoWork() (lets consider this as a fact at this moment), - why is the Background worker busy? I mean - as I understood, that the whole conception was introduced to run processes in a separated Thread with as less pain as possible?
2. How to retrieve the User from DB while keeping the UI responsive? Maybe BackgroundWorker is not the best concept for achieving this goal (Task / TaskFactory)?

Will be very appreciated with helping me on this one. Thank you in advance.

Upvotes: 2

Views: 2371

Answers (2)

remarkies
remarkies

Reputation: 123

private async void ValidateLogin()
{
    MainWindow mw = Window.GetWindow(this) as MainWindow;
    mw.preloaderShow();

    Task<Usuario> taskLogin = Login(tbLogin.Text, tbPassword.Password);
    await taskLogin;

    Usuario user = taskLogin.Result;

    if (user != null)
    {
        if (user.Activo == 0)
        {
            mw.preloaderHide();
            CustomMessageBox WrongLoginMessage = new CustomMessageBox("El usuario esta inactivo.");
            WrongLoginMessage.ShowDialog();
        }
        else
        {
            AppSession.Instance.SetValue("currentuser", user);
            btnProceed.Visibility = Visibility.Visible;
            mw.preloaderHide();
        }
    }
}

public Task<Usuario> Login(string loginText, string password)
{
    return Task.Run(() =>
    {
        return new Usuario();
    });
}

Use Task instead of backgroundworker.

Upvotes: 1

Rohit Vats
Rohit Vats

Reputation: 81233

Simple thumb rule is you cannot access UI element from background thread. You are accessing textbox and password control in DoWork which results in an exception.

Fetch user name and password on UI thread and you are good.

string userName = tbLogin.Text;
string password = tbPassword.Password;
bw.DoWork += (o, args) =>
{   
   user = _viewmodel.Login(userName, password); 
};

Upvotes: 2

Related Questions