Who is using BusyIndicators?

This forum is meant for questions and discussions about the X# language and tools
ic2
Posts: 1858
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

Who is using BusyIndicators?

Post by ic2 »

As written in an earlier thread, I added a BusyIndicator from Syncfusion to my program. This show some move graphic element in a window while a task taking an unknown amount of time is being executed. Often you see e.g. clock-like circles where the 'minutes' are moving.

However, I couldn't get a situation where the movement continued when the time consuming task started. I just stopped moving. As Syncfusion has a responsive support I asked them what was wrong and they assembled a full solution from which they said it worked. However, this only seemed to work as they added a Taks.Delay of 5 seconds during which the BusyIndicator moved indeed and then it filled an ObservableCollection of only 34 elements (in a blink of an eye).

When I placed this within a FOR loop so it would add many 1000's of elements the BusyIndicator directly stopped moving once that started. Today I got a new example from them but again this didn't really work as they still had the Task.Delay, then a task with the FOR but loop doing nothing and then a task filling only the 34 elements of the OC, without my FOR loop. Of course I sent them my modified non working sample and maybe in some stage I get a real solution, but in the meantime I wonder if it is possible that their BusyIndicator just never works. Does someone have a BusyIndicator working at all? If so, how?

Below also my modified sample. I now concentrate on the full C# program only to make sure it doesn't fail because (in my application) it runs from X#. From a WPF MainWindow the SfdataGrid_Loaded method is called (residing within a ViewModel). This show the BusyWIndow (a WPF window on which the BusyIndicator is placed) and then should add a small number of elements to the OC databound to a DataGrid x 400.000. It moves a short while but as soon as the FOR loop starts the BusyIndicator stops moving despite all the Async tasks and awaits.

Or maybe someone directly see what this Syncfusion support is doing wrong in the code?

Dick

Code: Select all

        async void SfdataGrid_Loaded(object sender, RoutedEventArgs e)
        {
            this.SfdataGrids.ItemsSource = await (this.DataContext as ViewModel).GetRecords();
        }


        private async void BusyIndicatorWindow()
        {
            var window = new BusyWindow();
            // Show the busy indicator on the UI thread
            await Application.Current.Dispatcher.InvokeAsync(() => window.Show());
        }

        public async Task<ObservableCollection<Model>> GetRecords()
        {
            var gdcsource = new ObservableCollection<Model>();
            this.BusyIndicatorWindow();
			await Task.Run(() =>
			{
			for(double i = 0;i<4000;i+=0.01)
				{
					gdcsource.Add(new Model() { EmployeeName="Robert",EmployeeArea="Torino",EmployeeDesignation="Analysts",EmployeeSalary=10000,EmployeeGender="Male" });
					gdcsource.Add(new Model() { EmployeeName=null,EmployeeArea="Montreal",EmployeeDesignation="SoftwareEngineer",EmployeeSalary=15000,EmployeeGender="FeMale" });
					gdcsource.Add(new Model() { EmployeeName="Nancy",EmployeeArea="Bracke",EmployeeDesignation="Manager",EmployeeSalary=27000,EmployeeGender="FeMale" });
}
}
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

Who is using BusyIndicators?

Post by NickFriend »

Hi Dick,

Haven't used Syncfusion, but try something like this. I don't think you should be using await in your GetRecords method.

Code: Select all

async void SfdataGrid_Loaded(object sender, RoutedEventArgs e)
{
   var window = new BusyWindow();
   window.Show();
   this.SfdataGrids.ItemsSource = await (this.DataContext as ViewModel).GetRecordsAsync();
}

public async Task<ObservableCollection<Model>> GetRecordsAsync()
{
   return Task.Factory.StartNew<ObservableCollection<Model>>(() =>
   {
      var gdcsource = new ObservableCollection<Model>();
      for(double i = 0;i<4000;i+=0.01)
      {
         gdcsource.Add(new Model() { EmployeeName="Robert",EmployeeArea="Torino",EmployeeDesignation="Analysts",EmployeeSalary=10000,EmployeeGender="Male" });
         gdcsource.Add(new Model() { EmployeeName=null,EmployeeArea="Montreal",EmployeeDesignation="SoftwareEngineer",EmployeeSalary=15000,EmployeeGender="FeMale" });
         gdcsource.Add(new Model() { EmployeeName="Nancy",EmployeeArea="Bracke",EmployeeDesignation="Manager",EmployeeSalary=27000,EmployeeGender="FeMale" });
      }
      return gdcsource;
   });
}
Personally I wouldn't set the ItemsSource manually like that. Instead have a property in your viewmodel and let binding do the work. Same with showing and hiding the busy indicator. But that would require a bit of reengineering the style of your viewmodel probably.

HTH

Nick
TerryB1
Posts: 306
Joined: Wed Jan 03, 2018 11:58 am

Who is using BusyIndicators?

Post by TerryB1 »

Hi Dick
Just a stab in the dark. Try declaring i as a float instead of a double.
Terry
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

Who is using BusyIndicators?

Post by NickFriend »

Just a little detail... the async keyword isn't needed on the GetRecordsAsync() method. It should be just

public Task<ObservableCollection<Model>> GetRecordsAsync()

async is when the method itself contains an await call.

Nick
User avatar
ArneOrtlinghaus
Posts: 412
Joined: Tue Nov 10, 2015 7:48 am
Location: Italy

Who is using BusyIndicators?

Post by ArneOrtlinghaus »

Probably Multithreading is needed - or a separate Exe file that shows the Busy-State.
For many years we have such a separate small exe program with a progress bar and an always responding Cancel-Button. There is also a separate window appearing and showing database activity of the program or possible blocking in case that the program does not respond for a longer time. But handshake between the two programs is not easy (still Windows messages) and we still do not have what we would like to have.

Arne
ic2
Posts: 1858
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

Who is using BusyIndicators?

Post by ic2 »

Hello Nick,

Thank you very much for assembling that code.

Originally it didn't compile.

The public async Task<ObservableCollection<Model>> GetRecordsAsync() didn't compile with this error:
Error CS4016 Since this is an async method, the return expression must be of type 'ObservableCollection<Model>' rather than 'Task<ObservableCollection<Model>>'

I read that this could be solved with adding 'await' to like this:

Code: Select all

return await Task.Factory.StartNew<ObservableCollection<Model>>(() =>
Then it does compile fine but (probably because of the await) the BusyIndicator still doesn't move.

Then you added a remark that I do without Async on the GetRecordsAsync() method. I removed that and then I could (must) remove the await keyword too. It compiles fine now! But the end result is the same: it stops moving once it is in the loop.

Syncfusion gave up more or less, today. They wrote "The use of a for loop inside a thread responsible for retrieving data is blocking the UI thread, which is responsible for updating the UI and displaying the busy indicator. " They suggested to add all those gdcsource.Add commands in the loop. The reason for the loop was not to use that in for real but to see if I could actually get this thing to work while in whatever situation which apparently is not possible. In the program I want to use it, a Datagrid is filled with a SQL statement. The way they use the ViewModel is from Syncfusions own DataGrid sample program to which they added the BusyIndicator. Not sure if they have your knowledge about using Databinding properly :cheer:

Unless something comes to your mind which explains why this stil doesn't work I found a simple and working solution, see my reply to Arne.

Dick
ic2
Posts: 1858
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

Who is using BusyIndicators?

Post by ic2 »

Hello Arne,

I had the same idea just before I read your reply and indeed, this finally works! I created a separate WPF solution and compiled an exe showing the window with the BusyIndicator. And then only 4 lines open that window, the BusyIndicator starts and keeps moving and as soon as my query has filled the DataGrid the window is closed. I can even use it in VO I think.

Code: Select all

Local op As Process
op:=Process.Start("BusyWindowExt.exe")
Self:CreateEmailsQuery()
op:Kill()
As I thought you may be right about multithreading and it would be more elegant I saw this example https://www.geeksforgeeks.org/c-sharp-multithreading/. Looks easy enough but it has the same problem. See below.

Despite having a working solution now I find it a poor that there is no simple & working solution in .Net to accomplish the same as by running an exe using a .Net command which has been there for 15 years or more.

Code: Select all

        public MainWindow()
        {
            InitializeComponent();
	Thread t = new Thread(ShowBusy);

            SfdataGrids.Loaded += SfdataGrid_Loaded;
            SfdataGrids.ItemsSourceChanged += SfdataGrids_ItemsSourceChanged;
           
        }
		private void ShowBusy()
		{ 
			var window = new BusyWindow();
			window.Show();

		}
Dick
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

Who is using BusyIndicators?

Post by NickFriend »

Hi Dick,

This sort of scenario ought to work fine in WPF. I think the thing is you're running another window with the indicator on it. Instead just show the BusyIndicator over your datagrid using a property in your viewmodel to show or hide it.

The following code should work. Not sure how your datagrid is declared in xaml, so that bit is pseudo code.

Code: Select all

xmlns:Notification="clr-namespace:Syncfusion.Windows.Controls.Notification;assembly=Syncfusion.SfBusyIndicator.WPF"

<Grid>
    <Notification:SfBusyIndicator Panel.ZIndex="1000" IsBusy="{Binding Path=Working}"/>
    <DataGrid ItemsSource="{Binding Path=Gdcsource}" .... />
</Grid>

void SfdataGrid_Loaded(object sender, RoutedEventArgs e)
{
   var datacontext = (this.DataContext as ViewModel);
   datacontext.LoadData();
}

In your viewmodel.....

private bool _working;
public bool Working
{
   get { return _working; }
   set
   {
       _working = value;
       this.RaisePropertyChanged(()=> Working);
   }
}

private ObservableCollection<Model> _gdcsource;
public ObservableCollection<Model> Gdcsource
{
   get { return _gdcsource; }
   set
   {
       _gdcsource = value;
       this.RaisePropertyChanged(()=> Gdcsource);
   }
}

public async void LoadData()
{
    Working = true;
    Gdcsource = await this.GetRecordsAsync();
    Working = false;
}

private Task<ObservableCollection<Model>> GetRecordsAsync()
{
   return Task.Factory.StartNew<ObservableCollection<Model>>(() =>
   {
      var gdcsource = new ObservableCollection<Model>();
      for(double i = 0;i<4000;i+=0.01)
      {
         gdcsource.Add(new Model() { EmployeeName="Robert",EmployeeArea="Torino",EmployeeDesignation="Analysts",EmployeeSalary=10000,EmployeeGender="Male" });
         gdcsource.Add(new Model() { EmployeeName=null,EmployeeArea="Montreal",EmployeeDesignation="SoftwareEngineer",EmployeeSalary=15000,EmployeeGender="FeMale" });
         gdcsource.Add(new Model() { EmployeeName="Nancy",EmployeeArea="Bracke",EmployeeDesignation="Manager",EmployeeSalary=27000,EmployeeGender="FeMale" });
      }
      return gdcsource;
   });
}
Nick
ic2
Posts: 1858
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

Who is using BusyIndicators?

Post by ic2 »

Hello Nick,

That idea sounds brilliant; getting the required position was also a (minor) issue which would be solved as well with your suggestion.

I still think it should work somehow with the separate window too given the fact that I can accomplish it with running it as an.exe. So Windows is not the problem.

What I have to do now is to keep your suggestion in mind (I make a comment in my code) once I need BusyIndicators in WPF windows. However, my DataGrid is on a Winforms window so I can't do that here. But note that in this thread I tried to get it working on a full .Net5 WPF sample solution from Syncfusion - in case you suspected Winforms :).

Dick
User avatar
wriedmann
Posts: 3757
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Who is using BusyIndicators?

Post by wriedmann »

Hi Dick,
PMFJI, but only a short note.
In Windows Forms (like in the VO GUI) it is possible to show a progress indicator without using multithreading only releasing some processing time to refresh the GUI.
In WPF this is not possibile, and therefore you have to use multithreading for every thing that takes longer than a few seconds to keep the GUI responsive.
Windows Forms is using the Windows API like the VO GUI classes do, and therefore the GUI is message based, and has some sort of "fake" multiprocessing. WPF is a completely newly written GUI layer that works in a completely different manner, and therefore you have to decide what to do in the GUI thread and what to do in background/processing threads. Therefore any busy indicator or progress box can only work if the processing is done in a different thread other than the GUI thread. This can done in several different manners, but the principle is the same.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Post Reply