views:

123

answers:

4

Hello,

i have a problem with threading in WPF. I want to have created a complex user interface and then i want to add it to my main-window. While this complex user interface is creating i want to show a progress bar in my main window. I think this only could be made with threads. But there is a problem. The created element can't be added to main window because it is created in a separate thread. Do anybody know if there is a possibility to transfer UIElements that are created in a background-thread to the main-thread. If i try in a simple way it says that the object can't be accessed because it is in a separate thread. I have already made my progress-bar thread safe.

I hope following example explain a bit better what i want to to.

StackPanel tcForm = new StackPanel();
Semaphore loadedSema = new Semaphore(0,1);
Thread thread = new Thread(new ThreadStart(delegate(){
           //Formular should be created in background               
           tcForm.Children.Add(new Formular());
           ProgressBar.AddProgress();
           //...other things
           loadedSema.Release();
}));
thread.start();
loadedSema.WaitOne();

new Formular() runs a very long time, so i thought about creating in background.

It is also impossible to add Formular to a variable and then add it main thread.

//this is also impossible

//in background-thread
form = new Formular

//in main-thread
tcForm.Children.Add(form);

I hope that there is a way to this. Would be nice if there are some advices,

Thanks, Martin

+1  A: 

If all you want to do is update a progress bar (or similar) then you should create all the UI up front in the main thread and then send status updates from the background thread to the main thread using the form's Invoke method (e.g send an int to say the progress between 0 and 100%)

I don't think there is a way to share windows forms controls between threads.

EDIT: Ok, I now see that this is tagged WPF. The answer remains the same, but instead of invoking on the control, you use the Dispatcher class (each graphical element holds a reference to the proper dispatcher). You can also use the BackgroundWorker class as suggested in another answer.

Isak Savo
that is the way currently work. But the problem if you use this way is that the progress-bar has to be in another window. And this window can't be child of the main window.
martin
A: 

I don't know much about WPF, but I just learned about the BackGroundWorker which will propably do the work for you.

http://dotnetperls.com/backgroundworker

You just have to handle the WorkerCompleted event which is raised after the task and should run in the main thread. It can also report the progress to update your progress bar.

Example:

    void worker_Start()
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork +=
            new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

        worker.RunWorkerAsync("MyArg");

    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
         e.Result = new Formular();
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.Controls.Add((Control)e.Result);
    }
SchlaWiener
if i use the background-worker i get the same problem as if i use threading. This is comprehensable, because backgroundworker is also a thread. The difference between thread an backgroundworker is that backgroundworker is a existing thread from thread pool
martin
If you create a BackgroundWorker in the main thread and attach to the WorkerCompleted event, the code within this event handler should be executed in the main thread (at least that works for me). I added an example.
SchlaWiener
+2  A: 

You should use Dispatcher.Invoke method

...
        this.Dispatcher.Invoke(delegate{ tcForm.Children.Add(new Formular());
           ProgressBar.AddProgress();};)
....

EDIT
You can create Fromular() object before dispatching, if it is working long

var f = new Formular();
this.Dispatcher.Invoke(delegate{ tcForm.Children.Add(f);
               ProgressBar.AddProgress();};)
ArsenMkrt
the call of new Formular() is the long-running-method. So this makes no sense because the ui freezes while new Formular() is running.
martin
see edited post
ArsenMkrt
i think this works for me
martin
A: 

No - that is not possible. The WPF is mainly single-threaded.

However, you should wonder why the creation of the interface takes that much time. The WPF is quite fast. "Ordinary" control creation should not be a bottleneck.

Maybe you can optimze other object creations; try profiling your application.

And try building your UI from bottom to top: The last action should be adding your control to your form.

winSharp93