isumit
isumit

Reputation: 2333

From background process open window and take input from user in WPF

In my C# WPF application, I am doing certain async reporting work using BackgroundWorker. I am able to update the UI from backgroundWorker using ProgressChanged event.

But, I need to ask for user inputs during the process, at certain points in background process I need to open window asking for user's input, depending on that input the background process will further continue.

Can I open some window from background process and then continue the process after user has responded on that window ?

Upvotes: 0

Views: 902

Answers (2)

Arman Nayyeri
Arman Nayyeri

Reputation: 159

You basically have 2 options:

  1. (Best Practice) As noted by others, best practice is to use a chain of async / await to do your work asynchronously, instead of background worker. You just have to put all your background job code inside an async method and call it using await keyword.

Here is an example which can be used to prompt an indefinite number of prompts and continue the job afterwards.

    //You can bind this to a Button or any other WPF event,
    // the point is that it should be run from UI thread
    private async void JobStartEvent()
    {
        JobResult jobResult = new JobResult
        {
            JobStatus = JobStatus.NotStarted
        };
        while (jobResult.JobStatus != JobStatus.Done)
        {
            jobResult = await DoLongJob(jobResult.ContinuationInfo);

            if (jobResult.JobStatus == JobStatus.UserPromptRequired)
            {
                jobResult.ContinuationInfo.PromptResult = PromptAndGetResult(jobResult.PromptInfo);
            }
        }
    }

    private async Task<JobResult> DoLongJob(JobContinuationInfo continuationInfo)
    {
        //Do long stuff here
        // continue the job using "continuationInfo"

        //When prompt needed, Do:
        {
            return new JobResult
            {
                JobStatus = JobStatus.UserPromptRequired,
                PromptInfo = new PromptInfo(), //Fill with information required for prompt
                ContinuationInfo = new ContinuationInfo() //Fill with information required for continuing the job (can be a delegate to a local function to continue the job)
            };
        }

        //When done, Do:
        {
            return new JobResult { JobStatus = JobStatus.Done};
        }
    }

    private async JobResult DoLongJob()
    {
        return JobResult = 
    }

    private enum JobStatus
    {
        NotStarted,
        UserPromptRequired,
        Done
    }

    internal class JobContinuationInfo
    {
        public PromptResult PromptResult { get; set; }
        // Other properties to continue the job
    }

    private class JobResult
    {
        public JobStatus JobStatus { get; set; }
        public PromptInfo PromptInfo { get; set; }
        public JobContinuationInfo ContinuationInfo { get; set; }
    }

Read More: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

  1. To use background worker, you can use Dispatcher.Invoke method and pass the window creation code there. The window creation will be done in the UI thread, but the background worker thread will wait for it to be executed, get the result (which can be your prompt's result) and then continue executing.
    System.Windows.Threading.Dispatcher.Invoke<ResultType>(async () =>
    {
        return PromptUserAndGetResult();
    });

Read More: https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatcher.invoke

Upvotes: 0

jhilgeman
jhilgeman

Reputation: 1583

You should split it up into different background workers. When you get to a point in your process that requires user input, finish/complete your background worker and then collect the input on the UI thread and then kick off the next worker with the input.

I would recommend using the Task / async / await methodology for this instead of a background worker. It will make this kind of process far easier to write and also understand:

private void async RunTheJob()
{
    // Run Part1 async and wait for the result
    var result1 = await Part1();

    // Now collect your UI input based on result1
    var uiInput = ......;

    // Run Part2 async and wait for the result
    var result2 = await Part2(uiInput);
}

private Task<Part1ReturnObjectTypeHere> Part1()
{
    Part1ReturnObjectTypeHere result = null;
    ...do async work here to populate result...
    return result;
}

Upvotes: 1

Related Questions