Skip to Content
0

Long running task halts the SAP UI

Nov 03, 2016 at 02:55 PM

153

avatar image

I am using the C#API for UI and DI for SAP9.2 and working with forms. I created my forms as the SDK Help shows, but I am wondering how can I execute Tasks on different Thread than the UI Thread.

I need to load all the Items from OITM (28k) and run other queries as well, it usually completes in 5 seconds, but for the time it is running the SAP Completely stops being functional, you can't click on any other menu or do anything. Usually, when you write a desktop software with GUI, on one thread runs the GUI and you can do long running processes on a new background Thread and Display a progress bar on the gui. I should achieve something similar, not blocking the whole application. Can I display a progress indicator at least?

My Form calls my Data Access layer, passing the SBOApplication as a parameter, then creating a Company object from that. Than I run the queries and get the result as XML from the recordset (oRecordset.GetAsXML();) and parse the results. Should I just run the Queries on a different Thread myself in my addon and use the "Application.StatusBar.CreateProgressBar(...)" ProgressBar object to display the state?

I tried with the following code:

 Dal.Items itmsDao = new Dal.Items(SBOApp);
            ProgressBar oProgressBar = SBOApp.StatusBar.CreateProgressBar("Termékek betöltése...", 100, false);
            var worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
            {
                BackgroundWorker b = o as BackgroundWorker;
                var itemList = itmsDao.GetItems(itemParamters);
                b.ReportProgress(40);
                var filteredItems = FilterItems(itemList, itemParamters);
                b.ReportProgress(70);
                var arlsitaItems = FillPrices(filteredItems);
                b.ReportProgress(90);
                LoadDataToMainMatrix(arlsitaItems);
            });

            worker.ProgressChanged += new ProgressChangedEventHandler(delegate (object o, ProgressChangedEventArgs args)
            {
                oProgressBar.Value = args.ProgressPercentage;
            });

            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
            {
                oProgressBar.Stop();
            });

           worker.RunWorkerAsync();

The Progress bar is displayed, the addon's UI won't respond to any interaction (i am okay with that), other parts of the SAP UI is working while it is processing. Is there a better way in SAP Addon development (best practice) or my approach should work in production?

Edit: Also, this solution has an error, It only works the first time, the second time it throws an Exception ( $exception {"Progress Bar - has stopped [66000-88]"} System.Runtime.InteropServices.COMException)

api | ui | 9.2 | uiapi | thread
10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

1 Answer

Best Answer
Pedro Magueija Nov 03, 2016 at 03:42 PM
2

Hi Szabolcs,

Usually, when you write a desktop software with GUI, on one thread runs the GUI and you can do long-running processes on a new background Thread and Display a progress bar on the GUI.

This is true, but B1 is not a usual application :(

The API is single threaded and all operations run on the UI thread (you'll notice that B1 itself becomes unresponsive when loading big documents).

In any case, you can run code on different threads (but you'll have to manage it carefully).

I have a "guideline" which is to only perform operations on different threads when they are scoped to the DI API only (such as when creating a batch of documents).

And yes, you can create progress bars to report progress to the user.

Pedro Magueija

LinkedIn | Twitter | Blog

Show 6 Share
10 |10000 characters needed characters left characters exceeded

Okay, so I had a little test today. I am trying to put DI API operations to a Backgroundworker (with the code posted in the question). I have a custom form and a button, which started the event that would run the background worker.

So First I add the eventhandler like this:

    oBtn = (Button)oForm.Items.Item(ID_BUTTON_SEARCH).Specific;
            oBtn.PressedAfter -= new _IButtonEvents_PressedAfterEventHandler(EventSearchPressedAfter);

Then the EventSearchPressedAfter method runs the code I posted.

At first run, it runs perfectly, but after it completed, every button (not just the Search button) stops working and no more event is fired. If I skip the background worker part, it works.

Is the backgroundworker somehow stops the event handler?

0

// cannot edit my comment...

sorry, I copied the remove listeners part. It is actually

oBtn.PressedAfter += ...
0

Also, If I close the form and reopen it, it works again.

0

I moved the

 LoadDataToMainMatrix(arlsitaItems);

method call inside the Completed delegate, because it updates the Matrix and the Completed call is done on the UI thread for a BackgroundWorker. Still the same issue, no event handler is working after the Worker completes.

0

Okay, so what happens with my code is that after the backgroundworker, all the Event listener subscriptions are lost! I used two method to handle event listeners (one of my past questions, you helped me than too, thanks :D). One is 'AddEventListeners()' which does something like this:

oBtn = (Button)oForm.Items.Item(ID_BUTTON_AKTUALIZALAS).Specific;
oBtn.PressedAfter += new _IButtonEvents_PressedAfterEventHandler(EventAktualizalasPressedAfter);

And a 'RemoveEventListeners()' which is the same, it just uses -= insted of += to remove. So I fixed the BackGroundWorker with two things:

  • First: Moved every code that changes the UI to the Completed method of the BackgroundWorker, because it always runs on the Thread that called the worker.
  • Second: At the end of the Completed method, first I executed the Remove, than the AddEventListeners method, to update the subscription.
1

Hi Szalbocs,

Thanks for sharing this, I'm sure it will be useful to everyone.

Pedro Magueija

LinkedIn | Twitter | Blog

0