How to add Usercontrol to ItemsControl using multithread? - wpf

I want to set items(UserControl) to ItemsControl using multithread. My code likes this
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(SetItemsControl));
thread.Start();
void SetItemsControl()
{
IDictionary<string, object> list = GetUserControlList(); // this function return list of UserControl
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate()
{
mylistcontrol.ItemsSource = list;
}));
}
And it breaks at my initialize function of my usercontrol
The calling thread must be STA, because many UI components require this.
How can i fix it??

The right way to do it is to update the collection that is bound to ItemsControl.ItemsSource. In this scenario you do not touch the visual element from another thread - you update the collection that is bound to it. The collection being updated tells the binding to refresh and that's when data comes to UI, and it happens already in UI thread so it's ok. Note that collection should implement INotifyCollectionChanged interface to be able to do that

Related

"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.

Updating a TreeView ItemsSource in another thread

I have a function like this:
public void UpdateList()
{
BindList = Model.GetList();
TreeView1.ItemsSource = BindList;
}
UpdateList() needs to be called (and the tree view list therefore reloaded) every time the user performs some action. It currently works, it's just very slow so I want to do it in another thread.
I am quite new to WPF... on WinForms programs I used to make delegates and check if InvokeRequired, etc. But I have found this does not work in WPF.
So if I want to call UpdateList() such that it runs in another thread, and the ItemsSource is updated from that other thread, how should I do it?
Thanks
Change Model.GetList() function to property of type ObservableCollection<> For example:
public ObservableCollection<DataItem> List { get; set; }
Call TreeView1.ItemsSource = Model.List only once. It gives you advantages of WPF binding.
Create a BackgroundWorker to load the data in DoWork handler. Load data to temporary collection and copy this collection to Model.List at RunWorkerCompleted handler.
If you want to update your tree while data is loading you may look at this approach: WPF update binding in a background thread
Read and learn MVVM pattern to understand the main idea of WPF developing

wpf mvvm binding List<int> to Listbox from background thread

I'm pretty new to wpf and mvvm so this may be a easy question but I'm hoping someone can explain it to me. I have a class in my model that all it does is polls processes and if it see that "calc" is running it adds it process id to a List. If you close calc.exe it removes it from the List. If the List changes it fires and event. In my ViewModel i subscribe to that event and update my property that fires off PropertyChanged. Now if my Property is a List in the view model then my binding in my view does not update correctly. If I changed my List to an ObservableCollection in my Model and ViewModel then i get a cross thread issue. However, if i leave my List in my model and change my property in my ViewModel to a ObservableCollection and copy the values of the the list into an observable collection everything works as expected.... I don't get it, do i really need to do the copy to get the binding in my ListBox to work properly?
Take a look at the DispatcherNotifiedObservableCollection that Cinch implements. It will automatically use the Dispatcher if need be. Read this article to see the code for it:
http://www.codeproject.com/KB/WPF/CinchIII.aspx#ObsCol
You need to update the observable collection from the dispatch thread.
Something like (not technically/syntactically correct, but psuedo-close)
Dispatcher.BeginInvoke( () => theList.Add( theThing ) );
A better way to achieve this functionality, is expose the Model via a property in the ViewModel, and bind the ListBox to Model.ObservableList. And use John's suggestion when adding and removing items to this list. This will not require you to copy the values from list to an observablecollection.
So your viewModel code would like
public class ViewModel
{
public Model Model { get { return _model; } }
}
The binding in xaml:
<ListBox ItemsSource=Model.ObservableList/>
where the ObservableList property returns your ObservableCollection.
As all UI elements in WPF have thread affinity (to the Dispatcher thread), any updates to the UI should be marshaled via the Dispatcher. You can marshal calls via the Dispatcher, using Dispatcher.Invoke. In this case, your view, viewModel and Model reside in the Dispatcher thread, so any call to update the UI via the viewModel or Model, would require you to call Dispatcher.Invoke.

wpf BackgroundWorker - Regarding updating UI

I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.

WPF MVVM update collections so that UI updates

I want to update my UI. Should I use BackgroundWorker? Do I put the BackgroundWorker in the MainWindowViewModel and instantiate the repositories again, or do I put it in the OrdersQueueViewModel and do something with the properties?
The UI just displays the contents of lists created by LINQ. The lists are ObservableCollection and are properties of the OrdersQueueViewModel. I have a ViewModel MainWindowViewModel that creates a collection ViewModels, so that I can bind to that collection from the MainWindow.xaml (view).
MainWindowViewModel.cs:
public MainWindowViewModel()
{
_printQueueRepos = new OrdersPrintQueueRepository();
_holdQueueRepos = new OrdersHoldQueueRepository();
_linesToPickRepos = new LinesToPickRepository();
_linesPerHourRepos = new LinesPerHourRepository();
//create an instance of viewmodel and add it to the collection
OrdersQueueViewModel viewModel = new OrdersQueueViewModel(_printQueueRepos, _holdQueueRepos, _linesToPickRepos, _linesPerHourRepos);
this.ViewModels.Add(viewModel);
}
MainWindow.xaml:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:OrdersQueueViewModel}">
<vw:OrdersQueueView></vw:OrdersQueueView>
</DataTemplate>
</Window.Resources>
Example of a property in the OrderQueueViewModel that uses a repository:
public ObservableCollection<LinesToPick> LinesToPick
{
get
{
return new ObservableCollection<LinesToPick>(_linesToPickRepos.GetLinesToPick());
}
}
So I haveLinesToPick bound in the OrdersQueueView, and as the database updates the lists should change in the UI. I'v spent some time reading about BackgroundWorker, but I'm not quite sure what to do to update the lists. I'm hoping because they are ObservableCollections I can just "refresh" them and they will use INotifyPropertyChanged and update the UI automatically. Very new to all this, trying to get my head around it, thanks in advance for any help.
EDIT: Using James's suggestion I have ended up with this In my OrdersQueueViewModel. However I am getting the error "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread", when the code gets to .Clear() on the 2 lists, which is what I thought the dispatcher was used for. Any suggestions?
Action workAction = delegate
{
_worker = new BackgroundWorker();
_worker.DoWork += delegate
{
LinesThroughput.Clear();
LinesToPick.Clear();
//refresh LinesToPick
foreach (var item in _linesToPickRepos.GetLinesToPick())
{
LinesToPick.Add(item);
}
//refresh LinesThroughput
List<LinesThroughput> Lines = new List<LinesThroughput> (_linesPerHourRepos.GetLinesThroughput());
foreach (var item in GetLinesThroughput(Lines))
{
LinesThroughput.Add(item);
}
};
_worker.RunWorkerAsync();
};
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, workAction);
You can do it either way - in the MainWindowViewModel or one of the child view models. I would choose based on which way produces lower coupling and higher cohesion between components. (Lower coupling - fewer dependencies. Higher cohesion - things go together that belong logically together.)
And BackgroundWorker is a reasonable technique. Just remember to dispatch to the UI thread to update the collection. As for your ObservableCollection code... That needs some work. Don't reinstantiate the ObservableCollection. Do something like this:
public ObservableCollection<LinesToPick> LinesToPick { get; private set; } // Don't forget to nstantiate in ctor
public void Refresh()
{
LinesToPick.Clear();
foreach(var item in _linesToPickRepos.GetLinesToPick())
{
LinesToPick.Add(item);
}
}
By keeping the same ObservableCollection that was databound, your UI will automatically pick up changes to the collection. If you replace the collection, you lose the binding to it and your UI won't update until you notify it that the property containing the collection changed. Much easier to just keep the same collection.

Resources