1

I have a number of time-consuming tasks in my program such as loading large DataGrids, updating multiple Databases etc. etc. What I would like to do is display a "Please wait..." MessageBox or Form whilst these tasks are running. I've had a look at BackgroundWorker but can't seem to get my head round it.

To minimize the amount of code needed, ideally I would like a method that can be called from anywhere as this will be used in many places in my program, though I know that might be tricky.

public CompanyManagement()
{
    InitializeComponent();
    FillDataGrid(); // This method takes all the time
}

private void FillDataGrid()
{
    //Display a message until this has completed
    var _cDS = new CompanyDataService();
    var Companies = new ObservableCollection<CompanyModel>();
    Companies = _cDS.HandleCompanySelect();
    CompanyICollectionView = CollectionViewSource.GetDefaultView(Companies);
    CompanyICollectionView.SortDescriptions.Add(new SortDescription("CompanyName", ListSortDirection.Ascending));
    DataContext = this;

    cancelButton.Visibility = Visibility.Hidden;

    if (compNameRad.IsChecked == false && 
        compTownRad.IsChecked == false && 
        compPcodeRad.IsChecked == false)
    {
        searchBox.IsEnabled = false;
    }
    dataGrid.SelectedIndex = 0;
}

Is there a way to have a completely separate method to call? I guess this would be hard as there is no way then for that method to know when the tasks have been completed, unless it is surrounding the code to be checked.

EDIT: using Matthew's example;

    public CompanyManagement()
    {
        InitializeComponent();
        var wait = new PleaseWait("My title", "My Message", () => FillDataGrid());
        wait.ShowDialog();
    }
CBreeze
  • 2,925
  • 4
  • 38
  • 93
  • What did you try when you looked at `BackgroundWorker`? – Nikhil Vartak Dec 15 '15 at 08:56
  • See [here](http://stackoverflow.com/a/5483644/106159) how to use BackgroundWorker. Then write a simple form to which you pass an `Action` that you run from the background worker. In your implementation of `worker_RunWorkerCompleted()`, close the form. Then show the form modally. – Matthew Watson Dec 15 '15 at 09:07
  • And you need to do the time consuming work in a different thread otherwise your window will freeze. – Nawed Nabi Zada Dec 15 '15 at 09:08
  • 3
    Forget about `BackgroundWorker`, this is better suited to `async` and `await`. – Mike Eason Dec 15 '15 at 09:09
  • @MikeEason how does `async` and `wait` better suit this? – CBreeze Dec 15 '15 at 09:22
  • If you see the answer in the following replies, please, mark it as an answer. Please, read this post: http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work – StepUp Dec 18 '15 at 08:59

1 Answers1

2

Here's an approach you can take if you're using .Net 4.5 or later (so you can use await and async.

1) Create a Window class called "PleaseWait" and add to it a Label called label.

2) Add the following field and constructor to the class:

readonly Action _action;

public PleaseWait(string title, string message, Action action)
{
    _action = action;
    InitializeComponent();
    this.Title = title;
    this.label.Content = message;
}

3) Handle the window's loaded event and name the handler loaded, then implement it as follows:

private async void loaded(object sender, RoutedEventArgs e)
{
    await Task.Run(() => _action());
    this.Close();
}

Now when you want to show a "please wait" message, create a PleaseWait object passing to it the title, the message and an Action that encapsulates the code you want to run. For example:

private void button_Click(object sender, RoutedEventArgs e)
{
    var wait = new PleaseWait("My title", "My Message", myLongRunningMethod);
    wait.ShowDialog();
}

private void myLongRunningMethod()
{
    Thread.Sleep(5000);
}

Or (using a Lambda):

private void button_Click(object sender, RoutedEventArgs e)
{
    var wait = new PleaseWait("My title", "My Message", () => Thread.Sleep(5000));
    wait.ShowDialog();
}

This is just a basic outline. There's more work for you to do, including making the "PleaseWait" dialog look nice and disabling the user's ability to close it manually.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Hi Matthew, i have added an edit to my original answer that is attempting to use your example. The window does load, and then close, however after it throws an exception: `An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll. Additional information: The invocation of the constructor on type 'View.Companies.CompanyManagement' that matches the specified binding constraints threw an exception.` – CBreeze Dec 15 '15 at 11:35
  • I think the issue is trying to do that from inside the `CompanyManagement()` constructor. You should probably do it from a `loaded` event handler. – Matthew Watson Dec 15 '15 at 11:48
  • I've tried moving it to the Page's `loaded` event but now get: `Additional information: The calling thread cannot access this object because a different thread owns it.`! – CBreeze Dec 15 '15 at 11:51
  • Ah I see. The code I posted will only work for code which isn't trying to update the UI. It's a lot harder to do this if the UI is being updated - the Please Wait dialog would need to run on a different message pump. [Some guidance here](https://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/). – Matthew Watson Dec 15 '15 at 12:49
  • So from what i can take from that I need to create a new `Thread` that the message dialogue runs on? – CBreeze Dec 15 '15 at 13:14
  • @CBreeze Kind of, but you'd have to use a different way of telling the message dialog to close. Unfortunately the approach I outlined above won't work for this. – Matthew Watson Dec 15 '15 at 13:40
  • OK thanks for your help. Could you point me in the direction of any further resources or would you recommend starting a new question for this ? – CBreeze Dec 15 '15 at 13:48
  • @CBreeze This seems like it might be useful: http://gettinggui.com/creating-a-busy-indicator-in-a-separate-thread-in-wpf/ – Matthew Watson Dec 15 '15 at 14:23