Loading the list of items asynchronously in a WPF listbox using Dispatcher - wpf

I am working on creating a WPF solution which uses MVVM pattern to load searched items in a search control asynchronously. The search control which is a WPF usercontrol is created with a textbox to enter search text and search button and a hidden listbox which would be visible when it loads the searched items list in it. This user control is in turn embedded into another WPF view which has a treeview of certain items. This view has a view model in which the logic to load the searched items of the tree view would be loaded in the search control. All the while, this has been happening synchronously without the use of any Dispatcher call. But, after a change request, I would like to make this happen asynchronously in a different thread using Dispatcher.
Could anyone please let me know how to get handle of the Dispatcher of the Search control in the view model class so as to call BeginInvoke on it using MVVM pattern wherein my View model is not aware of the view? Any clue would be highly appreciated.
public ObservableCollection<Details> CatalogSearchResults { get; private set; }
private void ExecuteSearchCommand(object parameter)
{
CatalogSearchResults.Clear();
if (string.IsNullOrEmpty(parameter.ToString())) return;
searchtext = (string)parameter;
searchtext.Trim();
SetSearchResults();
}
private void SetSearchResults()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += LoadResults;
bw.RunWorkerCompleted += this.LoadResultsCompleted;
bw.RunWorkerAsync();
}
private void LoadResults(object sender, DoWorkEventArgs args)
{
IsSearchInProgress = true;
foreach (var category in _rootCategory.Recurse(FindChildren))
{
if (category.CommentDetails != null)
{
//limitation - there is no direct way to add range to observable collection.
//Using linq query would result in two loops rather than one.
foreach (var node in category.Details)
{
if (node.Name.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0
|| node.PrecannedText.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate { CatalogSearchResults.Add(node); });
Thread.Sleep(100);
}
}
}
}
IsSearchInProgress = false;
}
In the xaml, I am biding the Items property of the Search control to the CatalogSearchResults:
<ctrl:SearchControl x:Name="Ctrl" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Top" ToolTip="Search" Command="{Binding SearchCommand}" Grid.ColumnSpan="3"
CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}"
Items ="{Binding CatalogSearchResults}" > </ctrl:SearchControl>
Thanks,
Sowmya

Here's a simple implementation showing how to use BackgroundWorker to update objects on the UI thread while DoWork is running - in this example, there's a ListBox in the UI that's bound to FilteredItems, and ItemsSource is a property of the UserControl of type IEnumerable:
FilteredItems = new ObservableCollection<object>();
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker) sender;
var result = ItemsSource
.OfType<object>()
.Where(x => x.ToString().Contains(_FilterText));
foreach (object o in result)
{
// Pass each object found to bw_ProgressChanged in the UserState argument.
// This updates the UI as each item is found.
bw.ReportProgress(0, o);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// FilteredItems is bound to the UI, but it's OK to update it here because
// the ProgressChanged event handler runs on the UI thread.
FilteredItems.Add(e.UserState);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
}
Note that calling ReportProgress every time you find an item is pretty inefficient, as you're marshalling every item found across threads with an Invoke call. Depending on how long the filtering is actually taking, it may be better to accumulate a bunch of results and pass a List<object> to bw_ReportProgress instead of just a single object.

It depends on a lot of factors (and your description is a bit confusing), but I've given a lengthy answer here that may shed some light on the matter. Basically, using the dispatcher alone will not automatically make the code multi-threaded; you'll need some real multi-threading mechanism like BackgroundWorker or the Task Parallel Library. Depending on how you have things set up and on exactly what you do in the other thread, you may indeed need to invoke some actions on the dispatcher thread - however BackgroundWorker does this automatically in most cases so I'd go with that for simple things. The Task Parallel Library also has special handling for the dispatcher, you should find more info on that on MSDN or any TPL tutorial.
The best advice I'd give if you didn't deal heavily with multi-threading until now is to gather as much information as possible on it, because, as it has been said countless times until now, multi-threading is hard! :)

Modify as necessary. 'Items' is just an observableCollection of strings exposed from the VM
private void SetSearchResults()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += LoadResults;
bw.RunWorkerCompleted += this.LoadResultsCompleted;
bw.RunWorkerAsync();
}
private void LoadResultsCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void LoadResults(object sender, DoWorkEventArgs args)
{
List<string> results = GetResults();
foreach (string result in results)
{
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal, (ThreadStart)delegate { Items.Add(result); } //Dont worry about access to modified closure in this case
Thread.Sleep(100);
}
}
In XAML
<ListBox ItemsSource={Binding Items}/>

All views in the application have the same dispatcher, you can access it with Application.Current.Dispatcher.
But anyway, you don't need the dispatcher to perform operations on a worker thread. You only need it to perform actions on the UI, because UI elements can only be accessed from the UI thread. But even then, you usually don't need to explicitly manipulate the dispatcher. You can update a property of your ViewModel from the worker thread, controls bound to this property will be updated alright, because the PropertyChanged event is automatically marshalled to the UI dispatcher.
What doesn't work is modifying an bound ObservableCollection<T> from a worker thread: you need to do it from the UI thread using Dispatcher.Invoke. You can also use a specialized ObservableCollection<T> that raises event on the UI thread.

Related

WPF Binding: Throttle the binding list's updates

I am binding the xamdatagrid to a list.But since there are too many updates on the list GUI get stuck.How can I stop these updates and refresh the grid after an interval(say 500 ms).Will Reactive extension's throttle method be useful?
You can use the DeferRefresh to defer the binding update until you're done making modifications to the collection:
using (collection.DeferRefresh())
{
// Make changes to the collection
...
}
If the collection is being updated in real time, you could use a timer to update the binding at intervals:
private IDisposable _deferral;
private void refreshTimer_Tick(object sender, EventArgs e)
{
if (_deferral != null)
_deferral.Dispose();
_deferral = collection.DeferRefresh();
}

RadBusyIndicator not showing PRISM/MEF/WPF from ViewModel

I am using MVVM/PRISM/MEF for my WPF application. It has one DataGrid with multiple records, and when one row is double clicked a separate view is added to region with multiple controls on it, the initialization of controls takes about 10 seconds for new screen, so thats why I want to show RadBusyIndicator during that time.
Following in the XAML
<!-- This is Main View -->
<!-- Module: MainModule, ViewModel: MainViewViewModel -->
<telerik:RadBusyIndicator IsBusy="{Binding IsBusy}" BusyContent="{Binding BusyContent}">
<!-- All PRISM regions are here -->
</telerik:RadBusyIndicator>
Its view model is
class MainViewViewModel : ViewModelBase
{
ImportingConstructor]
public MainViewViewModel(IEventAggregator eventAggregator, IRegionManager regionManager, IServiceLocator serviceLocator)
:base(eventAggregator, regionManager, serviceLocator)
{
eventAggregator.GetEvent<BusyStateChangedEvent>().Subscribe(OnBusyStateChanged,ThreadOption.BackgroundThread);
}
#region BusyStateChanged
private void OnBusyStateChanged(bool newState)
{
IsBusy = newState;
}
#endregion
}
And in other view when DataGrid row is double clicked ViewModelBase function is called, as follows
public class ViewModelBase
{
private NavigationItem global_navItem = null;
public virtual void OnNavigationItemChanged(NavigationItem item)
{
changeNav = true;
global_navItem = item;
//Firing event to change the state
EventAggregator.GetEvent<BusyStateChangedEvent>().Publish(true);
//Using BackgroundWorker, but its not showing any Busy Indicator as well
var bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Setting busy indicator to false
EventAggregator.GetEvent<BusyStateChangedEvent>().Publish(false);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//DisplayView function is taking too long
if (global_navItem != null) this.DisplayView(global_navItem);
}
}
public void DisplayView(NavigationItem item)
{
try
{
//This call is taking long as it initializes the View
MyCustomeUserControl view = this.ServiceLocator.GetInstance<MyCustomeUserControl>(item.viewName);
view.Region = this.Region;
}catch(Exception e)
{
}
}
Events are being fired correctly and view is displayed correctly, but my problem is that Busy indicator is not shown at all, when I double click on DataGrid row the GUI become unresponsive, and after some time the new view appears. I am in doubt that this is problem of GUI thread being busy, but what can I do to avoid this, I have used BackgroudWorker already?
EDIT
1- I am raising PropertyChanged event for IsBusy Property. and I have already tried all options for Thread in event subscription. i.e. Thread.BackgroundThread, Thread.UIThread and Thread.PublisherThread. but no change.
2- I have tested Thread.Sleep rather that DisplayView in bw_DoWork, and its showing RadBusyIndicator properly, so it means that GUI controls are being initialized in GUI thread, no matter I have created a BackgroundWorker for it.
Would the indicator appear if you use Thread.Sleep(5000) instead of this.DisplayView(global_navItem)?
I assume showing the view will use the UI thread and this will block the UI no matter you use a BackgroundWorker or not.
Edit:
As it seems like your UI loading operation blocks the UI thread and so your BusyIndicator, you can try to host one of them in a different thread. An approach is explained in this article.
Finally I have found a solution. For reference following post can be seen. I have implemented a child chrome-less window with RadBusyIndicator using the approach discussed in this post.
Creating multiple UI Threads in WPF

C# WPF class property to label

I have the following class:
class MyTimer
{
class MyTimerInvalidType : SystemException
{
}
class MyTimerNegativeCycles : SystemException
{
}
private Timer timer = new Timer(1000);
private int cycles = 0;
public int Cycle
{
get
{
return this.cycles;
}
set
{
if(value >= 0)
this.cycles = value;
else
throw new MyTimerNegativeCycles();
}
}
private void timer_Tick(object sender, ElapsedEventArgs e)
{
try
{
this.Cycle--;
}
catch
{
this.Cycle = 0;
timer.Stop();
}
}
public MyTimer()
{
this.Cycle = 20;
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Start();
}
}
In my MainWindow class I have a List I add a MyTimer to when a button is pressed:
private List<MyTimer> timers = new List<MyTimer>();
private void testbtn_Click(object sender, RoutedEventArgs e)
{
timers.Add(new MyTimer());
}
I tried to pass a label to the MyTimer class as a ref and update it but that won't work (can't access UI elements from another thread).
What is a good way to show the MyTimer.Cycle in a label so that it updates everytime the value is changed?
I must be able to "bind" each MyTimer to a different label from the code (or not bind it to a label at all).
You should use the BeginInvoke or Invoke method of the Dispatcher property of your label to change anything on your label or call any of it's methods:
private void timer_Tick(object sender, ElapsedEventArgs e)
{
try
{
this.Cycle--;
this.label.Dispatcher.BeginInvoke(new Action(
() => { label.Text = this.Cycle.ToString(); } ));
}
catch
{
this.Cycle = 0;
timer.Stop();
}
}
See Remarks section of the Dispatcher class or Dispatcher property.
The easiest solution to your problem is to use DispatchTimers. Dispatch timers use the windows message queue instead of a thread to dispatch timer tick events. This will make it so you don't have cross threading issues. Just keep in mind you are no longer working on a different thread and could lockup the UI if you do anything computationally expensive. Also due to the nature of dispatching on the message queue the timing is less accurate.
In WPF, you'd have a ViewModel (C#) associated with your View (XAML).
Read up on this if you're not familiar with MVVM.
Then the ViewModel would expose a property (let's call it Cycle) on which the View would bind:
<Label Content="{Binding Cycle}" />
Then if the value in the ViewModel has to be updated from another thread, do it like this:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
//Update here
}));
That will execute the update logic on the UI thread.
If you're new to WPF I'd strongly suggest that read a bit about DataBinding and Data Templating.
To start, the simplest way do display windows data in older UI models (like Windows Forms) has always been to have code in the code-behind set some property of the UI. This has changed drastically with WPF and the goal now is to have the UI look at business objects (like your MyTimer) and set the UI accordingly.
First we need to expose your business objects to the xaml of your application.
Me.DataContext = new MyTimer();
This sets the data context for the Window/UserControl to be the a new MyTimer(); Because the DataContext property is automatically based from a parent UI element to a child UI elelement (unless the child defines it's own DataContext), every element in your Window/UserControl will now have a DataContext of this object.
Next we can create a binding to a property of this object. By default all bindings are relative to the DataContext of the control from which it's located.
<Label Content="{Binding Cycle}" />
So in the previous example the binding was on the content property of the label. So in this case it will automatically set the Content to the value of the "Cycle" property from the DataContext (MyTimer)!
There is however one catch. If you run this sample as is WPF will take the value when the form loads but it won't update the label ever again! The key here to updating the UI is to implement the INotifyPropertyChanged interface.
This interface simply tells any listeners whenever a property (such as your Cycles) changes. The great thing is that Bindings automatically support this interface and will automatically propagate changes when your source implements INotifyPropertyChanged.
public class MyTimer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int cycles;
public int Cycles
{
get
{
return cycles;
}
set
{
if (cycles < 0)
{
throw new ArgumentOutOfRangeException("value", "Cycles cannot be set to a number smaller than 0.");
}
else if(value <> cycles)
{
cycles = value;
if (PropertyChanged != null)
{
PropertyChanged(Me, new PropertyChangedEventArgs("Cycles"))
}
}
}
}
//insert your constructor(s) and timer code here.
}
And voila! Your timer will now update the UI with it's cycles property.
You however also noted that you were storing your MyTimer objects in a list. If you were to instead put them inside an ObservableCollection (the default implementation of INotifyCollectionChanged - the collection variant of INotifyPropertyChanged) you can do other neat tricks:
In your Window/UserControl constructor:
ObservableCollection<MyTimer> timers = New ObservableCollection<MyTimer>();
timers.Add(New MyTimer());
DataContext = timers;
Then you can display them all at once in your xaml:
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label>
<TextBlock Text="{Binding StringFormat='Cycles Remaining: {0}'}" />
</Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Is Dispatcher not required in MVVM patern with WPF?

I am starting a new thread and trying to update UI elements through properties defined in my View Model and I am able to do it without any error, but if I try to update UI elements through code-behind, it throws the known UI access error("The calling thread cannot access this object because a different thread owns it."). First question would be ..Whats the difference between the two approaches ? Second question would be when I would use Disptacher in ViewModel ideally ?
Code Behind
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread th = new Thread(new ThreadStart(delegate()
{
textbox.Text = "Rajib";
}
));
th.Start();
}
//inside XAML
<TextBox x:Name="textbox" Text="{Binding UserInput, Mode=TwoWay}" />
MVVM
public string UserInput
{
get { return _UserInput; }
set { _UserInput = value; OnPropertyChanged("UserInput"); }
}
//Called through a ICommand property on a button click
public void ExecuteCommand(object obj)
{
InvokeCallThroughAnonymousDelegateThread();
}
private void InvokeCallThroughAnonymousDelegateThread()
{
ThreadStart start = delegate()
{
UserInput = "Calling from diff thread";
};
new Thread(start).Start();
}
Any attempt to update the UI must be done within the dispatcher thread. However, for property change events, WPF automatically dispatches for you when the event is raised from a background thread. You can read more about this on Bea Costa's (former WPF data binding PM) blog:
http://bea.stollnitz.com/blog/?p=34
They were going to do the same for INotifyCollectionChanged events but never got around to it in prior releases. For 4.5 they will now be synchronizing collection changed events automatically in addition to INotifyPropertyChanged.
The NotifyPropertyChanged has its thread context changed by WPF through the event, but your code behind doesn't change the thread context to the UI Thread. In your codebehind, use this instead:
Task.Factory.StartNew(() =>
{
// Background work
}).ContinueWith((t) => {
// Update UI thread
}, TaskScheduler.FromCurrentSynchronizationContext());
Regarding when to use the Dispatcher directly, I have a mid-sized project where I haven't used the Dispatcher in any ViewModel. I have used it to deal with Xaml resources, weak event handling, and it is used inside of MefedMVVM and Prism, which I also use.

Update UI from ViewModel class (MVVM pattern) in WPF

I'm using the MVVM pattern in my first WPF app and have a problem with something quite basic I assume.
When the user hits the "save" button on my view, a command gets executed that calls the private void Save() in my ViewModel.
The problem is that the code in "Save()" takes some time to execute, so I'd like to hide the "Save" button in the UI view before executing the large chunk of code.
The problem is that the view doesn't update untill all code is executed in the viewmodel.
How can I force the view to redraw and process the PropertyChanged events before executing the Save() code?
Additionally, I would like a reuseable way, so that I can easily do the same thing in other pages as well.. Anyone else made something like this already? A "Loading..." message?
If it takes a long time, consider using a separate thread, for example by using a BackgroundWorker, so that the UI thread can stay responsive (i.e. update the UI) while the operation is performed.
In your Save method, you would
change the UI (i.e. modify some INotifyPropertyChanged or DependencyProperty IsBusySaving boolean which is bound to your UI, hides the Save button and maybe shows some progress bar with IsIndeterminate = True) and
start a BackgroundWorker.
In the DoWork event handler of your BackgroundWorker, you do the lengthy saving operation.
In the RunWorkerCompleted event handler, which is executed in the UI thread, you set IsBusySaving to false and maybe change other stuff in the UI to show that you are finished.
Code example (untested):
BackgroundWorker bwSave;
DependencyProperty IsBusySavingProperty = ...;
private MyViewModel() {
bwSave = new BackgroundWorker();
bwSave.DoWork += (sender, args) => {
// do your lengthy save stuff here -- this happens in a separate thread
}
bwSave.RunWorkerCompleted += (sender, args) => {
IsBusySaving = false;
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
}
}
private void Save() {
if (IsBusySaving) {
throw new Exception("Save in progress -- this should be prevented by the UI");
}
IsBusySaving = true;
bwSave.RunWorkerAsync();
}
You're using MVVM pattern, so your Save Button's Command is set to an instance of the RoutedCommand object which is added to the Window's CommandBindings collection either declaratively or imperatively.
Assuming that you do it declaratively. Something like
<Window.CommandBindings>
<CommandBinding
Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}"
CanExecute="Save_CanExecute"
Executed="Save"
/>
</Window.CommandBindings>
For the handler of Executed routed event, your Save() method, on entry, you set a variable to false, on return you set it back to true. Something like.
void Save(object sender, ExecutedRoutedEventArgs e)
{
_canExecute = false;
// do work
_canExecute = true;
}
For the handler of the CanExecute routed event, the Save_CanExecute() method, you use the variable as one of the condition.
void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _canExecute && _others;
}
I hope I am clear. :)
You could always do something like this:
public class SaveDemo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set
{
if (_canSave != value)
{
_canSave = value;
OnChange("CanSave");
}
}
}
public void Save()
{
_canSave = false;
// Do the lengthy operation
_canSave = true;
}
private void OnChange(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
}
Then you could bind the IsEnabled property of the button to the CanSave property, and it will automatically be enabled/disabled. An alternative method, and one I would go with would be to use the Command CanExecute to sort this, but the idea is similar enough for you to work with.
You can accomplish this by the following code..
Thread workerThread = null;
void Save(object sender, ExecutedRoutedEventArgs e)
{
workerThread = new Thread(new ThreadStart(doWork));
SaveButton.isEnable = false;
workerThread.start();
}
do all your lengthy process in dowork() method
in some other method...
workerThread.join();
SaveButtton.isEnable = true;
This will cause to run save lengthy process in another thread and will not block your UI, if you want to show an animation while user click on save button then show some progress bar like iPhone etc... give me feedback i'll try to help you even more.
Late answer, but I figured it'd be good to input a bit as well.
Instead of creating your own new thread, it would probably be better to leave it up to the threadpool to run the save. It doesn't force it to run instantly like creating your own thread, but it does allow you to save threading resources.
The way to do that is:
ThreadPool.QueueUserWorkItem(Save);
The problem with using this approach, as well, is that you're required to have your "Save()" method take in an object that will act as a state. I was having a similar problem to yours and decided to go this route because the place that I'm working is very Resource-Needy.

Resources