Page 1 of 2
Who is using BusyIndicators?
Posted: Thu Apr 13, 2023 12:27 pm
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" });
}
}
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 9:09 am
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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 10:30 am
by TerryB1
Hi Dick
Just a stab in the dark. Try declaring i as a float instead of a double.
Terry
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 12:53 pm
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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 4:05 pm
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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 7:34 pm
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 "T
he 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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 7:48 pm
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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 8:26 pm
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
Who is using BusyIndicators?
Posted: Fri Apr 14, 2023 8:34 pm
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
Who is using BusyIndicators?
Posted: Sat Apr 15, 2023 4:39 am
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