WPF Datagrid: Loading_Completed Event? - wpf

I have Datagrid and I do something like:
Me.Cursor = Wait
Datagrid.ItemsSource = GetData()
Me.Cursor = Nothing
The problem is that there is a (relatively) large delay between setting the .ItemsSource and the moment when the rows actually rendered. So my cursor is reset to normal much too early.
Is there some kind of event that is raised when the Datagrid is finished loading/rendering the data? I know there is a _LoadingRow event, but it fires during the data load, not when the loading is completed?

I had the same issue (look here), and I solved it by placing this code after I changed the ItemsSource:
Dispatcher.InvokeAsync(() => { System.Windows.Input.Mouse.OverrideCursor = null; },
DispatcherPriority.ApplicationIdle);
It basically waits for the application to become idle before changing the cursor back to default. Using a FrameworkElement.Loaded event was not enough because it wouldn't get raised when I made changes to the ItemsSource, only when the datagrid was first loaded.

You may take a look at BeginInit() and EndInit() methods

Related

In what scenarios might ICommand CanExecute not update?

I've been using Josh Smith's implementation of RelayCommand in a couple of large projects for some years now. However today I've come across a scenario where the CanExecute on one of my commands isn't refreshing. I'm at a loss as to what's causing it - the view-model isn't doing anything that I haven't done dozens of times already.
The VM's constructor creates a couple of commands. The first one is a "start" command:-
StartCommand = new RelayCommand(o => StartAsync(), o => true);
The StartAsync() method looks like this:-
private async void StartAsync()
{
IsRunning = true;
await Task.Run(() => SomeLongRunningProcess(); }
IsRunning = false;
}
There is also a "save" command:-
SaveCommand = new RelayCommand(o => Save(), o => !IsRunning);
('IsRunning' is a bog-standard property, implementing INotifyPropertyChanged. As well as being used for the "CanExecute" delegate, it's also bound to the IsEnabled property of a few controls in the view to enable/disable them).
When I click my "Start" button (bound to 'StartCommand'), the "Save" button is correctly disabled. The b/g process runs to completion, then IsRunning is set to false, but this doesn't trigger the "Save" button to become enabled. It only enables if I click somewhere on my view.
(The controls whose IsEnabled property is bound to the VM IsRunning property do enable and disable correctly, by the way).
I've come across a few SO articles about this, but nothing really explains why this happens. My workaround was to bind the button's IsEnabled property to 'IsRunning', but it's frustrating that this particular view refused to play ball. Any thoughts on what might be causing this? Common sense says it's something specific to this view/VM, but I'm stumped (and I'm not going to post the code here - there's too much of it).
Yes, because the version of RelayCommand you're using is depending on CommandManager.RequerySuggested event and it's not accurate.
Its documentation states that
Occurs when the CommandManager detects conditions that might change
the ability of a command to execute.
Basically it guesses all the possible events where your data could be changed. It can never know when your ViewModel/Model is changed. It isn't listening for the property change notifications.
If you want to react immediately without waiting for CommandManager to guess, you need to fire the ICommand.CanExecuteChanged manually yourself when model is updated.
You saw that the event not fired unless you click the window or something but do note that it could fire several times too

How to set busy cursor for slow WPF controls

There appears to be a lot of resources on showing the busy cursor.
But all the solutions I've managed to find relies on setting the cursor via the view model. (i.e, IsBusy property, disposable WaitCursor).
These methods work well when I know when my data binding/view models will be long-running.
But I don't know how to do this automatically for cases where the bottleneck is the actual WPF user control itself?
For example:
Loading a control is initially lag-free. But once a 3rd party control is used, the control exhibits a 500-ms lag every time it loads.
The binding itself is fast, hence, adding a waitcursor/IsBusy in the view model is useless because it wouldn't know when the control (or any of its logical/visual children) has finished rendering. Nor should it know, as the view model should not be affected by the view's implementation.
Is it possible for the application to automatically set the cursor to busy when one or more WPF controls is busy/slow?
You may need something like this,
var busytimer = new DispatcherTimer(
TimeSpan.FromSeconds(1),
DispatcherPriority.Normal,
delegate
{
Process application = null;
foreach (var process in Process.GetProcesses())
{
if (process.ProcessName == "Your process name")
{
application = process;
break;
}
}
if (!application.Responding)
{
this.Cursor = Cursors.Wait;
}
else
{
this.Cursor = Cursors.Arrow;
}
},
Application.Current.Dispatcher);
Is it possible for the application to automatically set the cursor to busy when one or more WPF controls is busy/slow?
Not really. If your UI thread is blocking, you won't be able to update the cursor until it becomes unblocked anyway, which defeats the purpose.
The binding itself is fast, hence, adding a waitcursor/IsBusy in the view model is useless because it wouldn't know when the control (or any of its logical/visual children) has finished rendering. Nor should it know, as the view model should not be affected by the view's implementation.
If you go with the IDisposable wait cursor solution, you could try scheduling the Dispose() call to occur after the next layout pass:
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Loaded,
new Action(() => waitCursor.Dispose()));

"BindingSource cannot be its own data source" - error when trying to reset the binding source from a method in another class

We are binding a DataGridview using BindingSource. So in the main thread we have given like this.
class1BindingSource = new BindingSource();
class1BindingSource.DataSource = class1List;
this.dataGridView1.DataSource = class1BindingSource;
After that i have a placed a background worker in the form and is triggering in a button click.
i.e. in the button click
this.backgroundWorker1.RunWorkerAsync()
In the BackgroundWorker DoWork Event i am trying to update the BindingSource and there by trying to update the DataGridview.
So the BindingSource reset is done in a method in another class.
DoWork Event
Class2 cl2 = new Class2();
cl2.UpdateBindingSource(class1BindingSource);
UpdateBindingSource Method
public void UpdateBindingSource(BindingSource bs)
{
Class1 c1 = bs.Current as Class1;
for (int i = 0; i < 1000; i++)
{
lock (bs.SyncRoot)
{
c1.MyProperty1 = i;
bs.ResetItem(0);
}
}
}
Now i am getting an exception like BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.
If i am doing this in my DoWork Event then i can reset the item in the control thread itself using BeginInvoke method.
But actually i am trying to simulate our application scenario. So i want to solve this in this format.
Can any one help me on this.
The problem is that you can't update a BindingSource within a thread other than the gui thread. This is due the fact, that the BindingSource will fire some events which will then be received by your data grid view which will then start to update itself, which will fail cause it won't be done on the gui thread.
So right before you call RunWorkerAsync() you should call class1BindingSource.SuspendBinding() and within your RunWorkerCompleted you should call class1BindingSource.ResumeBinding().
Also ensure that within your DoWork you won't call any methods on the binding source (like you did with bs.ResetItem(0)).
And also remove this lock statement. It simply doesn't make any sense (in your example) and if you really need it (in your real code) consider using some private object _Gate = new Object(); within your class to avoid any deadlocks from the outer world, cause bs.SyncRoot is publicly available.
I had the same problem:
- BindingSource that had elements with INotifyPropertyChanged
- A separate Task that updated the elements.
The suggested solutions SuspendBinding etc didn't work. BindingSource should have done something like IsInvokeRequired.
Luckily Ivan Stoev came with the brilliant idea of subclassing the BindingSource and do something similar as IsInvokeRequired. Thank you Ivan!
Link: Update BindingSource from a different Task
UpdateBindingSource() does not take much time, so no need to use backgroundworker. You can invoke UpdateBindingSource() in the main thread.
Also, keep datagridview manipulation in the main thread.

UI freezed instead of showing BusyIndicator in Silverlight

This is a side-related question to this other question:
BackgroundWorker in Silverlight ViewModel
I have a TabControl where I load many TabItems when the user selects menu options. I load this Tabs by binding the TabControl ItemsSource to an ObservableCollection. When I add a new TabItem to this Collection, it is shown perfectly.
The problem is I've realized that since user press a button until the tab is created (ViewModel and View creation takes a couple of seconds), the screen is freezed.
I've tried to set "IsBusy" before calling the "loadTab" but it doesn't shows up... I've tried almost everything with async calls but the UI thread is in use and it throws an exception when I create the new tab control.
Is there any trick I'm loosing??? Any ideas??? Thanks in advance.
have you seen this post?
http://www.dotnetspark.com/kb/3524-doesnt-your-girlfriend-deserves-more-time.aspx
It helps when you avoid heavy stuff in the load event and make Visible=true after you finish to load all your resources, so in that sense you avoid the user feeling tempted to click something that is not ready yet.
Not sure if it helps, but how about this idea?
public void DoStuff(Object values)
{
//your values object could be anything,
//they might even be some objects from your form
//as long as you dont modify them in the other thread
imgLoading.Visible=true;
var client = new Proxy();
client.OnWorkCompletedAsync +=client_OnCompleted() ;
client.Work(values);
}
void client_OnCompletedAsync(object sender, EventArgs e)
{
imgLoading.Visible=false;
//now you can update the UI with other stuff
}

WPF Work-In-Progress animation not displaying for data binding updates

I have a helper class I've written which can be used to run a long running task on my GUI. What it does is use styles to display a "working" animation and fades out the content so while the task is running, the user can see that something is in progress.
My problem is that when the long running task completes, it fades the content back in and hides the working animation - which is what it should do, but because I am using MVVM and primarily data binding for all my content display, the updates to the GUI components happen separately to the long running task. ie the data binding OnPropertyChanged("") events fire and then these are picked up by the GUI thread AFTER the long running task completes. But the problem is the Worker Animation closes when the long running task completes, but BEFORE the data bindings update.
So the end result is you get the worker animation displaying as expected while the task runs, but the data binding update takes a good 4-5 seconds or even longer for large datasets for all the tree data and during this time, the application is not in "working animation mode" and just freezes.
Is there a way I can have my worker animation continue to run not only for the Long running Method, but for the associated data binding updates from OnPropertyChanged as well?
Consider using BusyIndicator from Extended WPF toolkit. It should provide functionality you described. It has IsBusy property which you can bind to property in your ViewModel and set it to False after all work is done.
You can always change the style of BusyIndicator same way as you do with other controls. In my solutions I always use this control along with BackgroundWorker class from System.ComponentModel and I usually set IsBusy=false at the end of RunWorkerCompleted
private void LongRunningMethod()
{
this.IsBusy = true;
var worker = new BackgroundWorker();
worker.DoWork += this.LongMethodDoWork;
worker.RunWorkerCompleted += this.RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void LongMethodDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
...
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
...
this.IsBusy = false;
}
Thanks all for the answers. I've actually come across a solution that may be a bit controversial as some would construe it is a little bit of a hack, but it does exactly what I want it to do and there seems to be no other way to do it, so to me that is a code solution, not a hack.
I'm using the WPFBackgroundProgressIndicator open source project I downloaded from codeproject (I think) which has the option to show the busy indicator in the main content with or without a fade out, or as a popup and it runs as a background thread which is ideal and why I chose it.
The problem was that when you run a long running method, the code execution completes synchronously but all the binding OnPropertyChanged("") updates run asychronously and queue on the Dispatcher thread, so your work method completes before the WPF controls have a chance to call the Getters of the dependency properties, to retrieve the new value. What you need to do is effectively "block" until all the Dispatcher events have completed and that is why not everyone will like this solution as it "blocks", but then that is exactly what I am trying to do. I WANT to block the application until the full update has completed as I dont want the user to be able to do anything visually while data is still rendering, so that is my requirement. Clean blocking is preferable to messy interaction.
So the solution, believe it or not, is a single line of code just after the work method call. It is as follows.
Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
Which as you can see effectively queues a new task on the Dispatcher thread and blocks current code execution until it finishes, but as you give it the lowest priority, this call will wait until all OTHER dispatcher execution finishes, ie all rendering completes. Once render is complete, this line will be executed and you will exit with all rendering complete. The full method I have used it in context is below. I welcome your thoughts and discussion on this approach.
public void LongRunningTaskWithFade(BusyDecorator busy, Action longTask)
{
if (loading) return;
loading = true;
busy.FadeTime = TimeSpan.Zero;
busy.IsBusyIndicatorShowing = true;
// in order for setting the opacity to take effect, you have to delay the task slightly to ensure WPF has time to process the updated visual
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
longTask();
Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
}
finally
{
HideBusyDisplay(busy);
}
}), DispatcherPriority.Background);
}

Resources