1

I am having troubles filling a listbox from a backgroundworker thread. I currently have the following code:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var lockedUsers = new List<UserPrincipal>();
        using (var context = new PrincipalContext(ContextType.Domain, "domain", smtu, smtp))
        {
            GroupPrincipal grp = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Domain Users");
            foreach (var userPrincipal in grp.GetMembers(false))
            {

                var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userPrincipal.SamAccountName);
                if (user != null)
                {
                    if (user.IsAccountLockedOut())
                    {
                        listBox1.Items.Add(@"domain\ " + user);
                    }
                }
            }
        }
    }

This returns the exception saying I cannot write to the main UI, which is correct. But I have been unable to find a way around it. I've tried the following, and although it didn't give any errors, it didn't fill the listbox.

    List<string> listusers = new List<string>();


    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var lockedUsers = new List<UserPrincipal>();
        using (var context = new PrincipalContext(ContextType.Domain, "domain", smtu, smtp))
        {
            GroupPrincipal grp = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Domain Users");
            foreach (var userPrincipal in grp.GetMembers(false))
            {

                var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userPrincipal.SamAccountName);
                if (user != null)
                {
                    if (user.IsAccountLockedOut())
                    {
                        listusers.Add(@"domain\" + user);
                    }
                }
            }
        }
    }

    private async void timerlocked_Tick(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
        listBox1.DataSource = listusers;
    }

Any ideas?

  • 3
    Instead of the timer's Tick event, use the BackgroundWorker's Completed event to set the DataSource property with that listusers collection. – LarsTech Oct 29 '19 at 14:04
  • 2
    Possible duplicate of [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) – Ivan García Topete Oct 29 '19 at 14:16

2 Answers2

1

The DoWorkEventArgs that gets passed to the DoWorkEventHandler delegate has a Result property

Gets or sets a value that represents the result of an asynchronous operation.

The Result that you set will be sent along to the RunWorkerCompleted event through the RunWorkerCompletedEventArgs that get passed to that delegate.

So, to solve this, you could build a List<string> inside of the DoWork event, set the Result property to that list, handle the RunWorkerCompleted event and access the list from the RunWorkerCompletedEventArgs.Result.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var result = new List<string>();
    var lockedUsers = new List<UserPrincipal>();
    using (var context = new PrincipalContext(ContextType.Domain, "domain", smtu, smtp))
    {
        GroupPrincipal grp = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Domain Users");
        foreach (var userPrincipal in grp.GetMembers(false))
        {

            var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userPrincipal.SamAccountName);
            if (user != null)
            {
                if (user.IsAccountLockedOut())
                {
                    result.Add(@"domain\ " + user);
                }
            }
        }
    }
    e.Result = result;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        // Error handling.
    }
    if (e.Cancelled)
    {
       // If you support cancellation...
    }
    listBox1.Items.Clear();
    listBox1.Items.AddRange((e.Result as List<string>).ToArray());
}
Joshua Robinson
  • 3,399
  • 7
  • 22
0

To access the UI thread from a background worker, you can either do your work in the RunWorkerCompleted event of the BackgroundWorker or - if you want to access the UI while the worker is running, have a look at Control.Invoke.

The later could look like this:

if (user.IsAccountLockedOut())
{
    listBox1.Invoke(() => listBox1.Items.Add(@"domain\ " + user));
}
germi
  • 4,628
  • 1
  • 21
  • 38