Updating an ObservableCollection in a separate thread - c#

In a WPF application an ObservableCollection is filled and updated by LINQ to SQL queries. Then UI objects are updated using values from this ObservableCollection.
Is it possible and reasonable that operations of updating this ObservableCollection by LINQ to SQL queries were executed in a separate thread?
If yes, will, in this case, it be one and the same instance of this ObservableCollection? (I mean, if it is not the same one for taking values from LINQ datacontext and the one for giving values to update UI, then I will not be able to update UI)

.Net 4.5 provides a solution within the BindingOperations class.
You can now use the BindingOperations.EnableCollectionSynchronization method as follows:
private readonly object _personCollectionLock;
private ObservableCollection<Person> _personCollection;
public ObservableCollection<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock);
}
I have only just tried this in my development environment but everything seems to be working correctly now when I update the collection from a background thread.
There is a more in-depth discussion of this solution at: http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification
The MSDN entry for this method is at: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

With the built-in ObservableCollection<T> class, you can't change the content from a separate thread if the UI is bound to the collection, it throws a NotSupportedException (but change notification for properties of collection items works fine). I wrote an AsyncObservableCollection<T> class to handle this case. It works by invoking the event handlers on the UI synchronization context

Trying to understand your question here:
Scenario 1
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A.
2. Periodically, more data is retrieved from database and added to A. Old data is removed from A.
Scenario 2
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A.
2. Periodically, data in A is updated with new data from database (no add/remove).
With Scenario 1, you're going to have to use the UI thread. The UI thread owns the ObservableCollection and you'll get an exception if you try to use it in another thread.
With Scenario 2, thumbs up. As long as you don't try to add or remove items from the collection itself, you can update the item as much as you want in a background thread.

I did not receive the same System.NotSupportedException, but unfortunately my UI did not Update properly if I set the my ObservableCollectiony<MyType> from another thread.
The only thing that worked for me is presented by https://www.codeproject.com/Tips/1111432/Update-data-to-WPF-control-from-another-thread:
In ViewModel
private SynchronizationContext _syncContext = SynchronizationContext.Current;
private ObservableCollection<PackageModel> _packageModelList;
public ObservableCollection<PackageModel> PackageModelList
{
get => _packageModelList;
set
{
if (_packageModelList == value)
return;
packageModelList = value;
RaisePropertyChanged("PackageModelList");
}
}
In other thread
_syncContext.Send(x => { PackageModelList = OtherThreadPackageModels; },null);
Hope this helps others.

Related

WPF<-> entity binding

I have problem with updating data from my datasource(database through entity fw) to wpf-windows. I generate files using entity framework, so i'm accesing data from datebase this way:
public partial class sampleWindow : Window
{
myEntity en = new myEntity();
public sampleWindow()
{
InitializeComponent();
Bind();
}
private void Bind()
{
var list = from o in en.table select o;
someDatagrid.ItemsSource = list.ToList();
}
This method, firstly, was adequate for my program, i was refreshing 'Bind' method after i was doing some operations on database, so the data in my datagrids or combos was fresh. The problem occurs when i was changing database in diffrent wpf-windows. I have read that I should implement observable interface and use load instead of itemsSource. I tried to do it but i'm begginer and my attempts faild miserably. Could someone tell me step by step, what i should do?
You need a Singleton to manage your data, combined with using an ObservableCollection to expose the data. When the collection is changed by any view, it will notify any subscribers to the observation and they will automatically update.
See: Example of bindable list in XAML app (first part)
Example of Singleton
You would want to use a singleton for the instance of your entity as The Sharp Ninja mentioned. His article in the link he posted does a good job of explaining. You will want to use an observable collection to bind your ItemSource to. When an item is added or removed from an Observable collection the UI is automatically notified. The problem you are going to have is that there is not a .ToObservableCollection()
extension method build in to .net so you will have to implement your own.
I use this extension method
public static ObservableCollection<T> ToObservableCollection<T>(
this IEnumerable<T> enumeration)
{
return new ObservableCollection<T>(enumeration);
}
So now your bind method can set your ItemSource to the observable collection
private void Bind()
{
var list = from o in en.table select o;
someDatagrid.ItemsSource = list.ToObservableCollection();
}
There are so many and better ways (MVVM pattern) to accomplish this than your approach. To keep it simple it can be accomplished this way:
//Gets the Load() extention method available for DbSet
using System.Data.Entity;
private void Bind()
{
myEntity.table.Load();
/*Local returns an obvervable collection convenient for data binding.
This is a synchronized local view of your data. It means any item added , deleted and updated will be reflected in your controls.*/
var obsColl = myEntity.table.Local;
someDatagrid.ItemsSource = obsColl;
}

Proper way to automatically update bound datagrid (non ugly way)

I'm looking for a proper way to automatically update a bound datagrid in a "non ugly way". I have a wpf application with a datagrid which i have bound to a datatable:
MeasurementResultsDataGrid.ItemsSource = _main.CurrentMeasurement.MeasurementDataTable.AsDataView();
Pretty straightforward! Next is that I have used the interface INotifyPropertyChanged to synchronize the model with the UI:
public event PropertyChangedEventHandler PropertyChanged;
public MeasurementDataSet.MeasurementDataTable MeasurementDataTable
{
get
{
return _measurementDataTable;
}
set
{
_measurementDataTable = value;
NotifyPropertyChanged("MeasurementDataTable");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
A thread is adding new rows to the datatable and giving no errors whatsoever. As a little expected, the DataGrid is not updating/"refreshing" as the thread updates the table. What is the best way to do this? I rather not use a separate timer/thread to update the datagrid with : datagrid.refresh(); & datagrid rebind..
I have searched a lot on this, and it seems i am on the right track, but missing i think some crucial parts!
First of all don't bind to the return value of a method, because then the return value gets copied and will never change.
Bind your ItemsSource to a Property of a Type which implements INotifyPropertyChanged.
In your case, I suggest to use a ObservableCollection. There you have the benefit that if the items stored inside the collection are implementing INotifyPropertyChanged and beeing changed the UI gets updated without calling refresh() or rebind().
You could add one property:
public DataView MeasurementDataTableView
{
get
{
return MeasurementDataTable.AsDataView();
}
}
Then use this as the ItemsSource (not sure if this will work, if not, I think you can just set it with the XAML code using binding):
MeasurementResultsDataGrid.SetBinding(DataGrid.ItemsSourceProperty, "MeasurementDataTableView") ;
So that you can add NotifyPropertyChanged("MeasurementDataTableView") after adding new rows to the datatable.
After long searching and trying the suggested answers, i have implemented the second part of the answer of #coder0815 and used the Observable Collection instead of the DataTable. It seems that now i don't have to implement the INotifyCollectionChanged interface and the UI gets updated automatically!
Only watch out if you try to update the collection if you created the collection on the UI Thread, what most of the time happens in WPF. In WPF < 4.5 you have to use delegates to surpass the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread" exception. If you have WPF > 4.5, you can use the following:
Create a variable on the UI thread, for instance:
private SynchronizationContext _uiContext;
In your creating of the UI, initialize the value:
_uiContext = SynchronizationContext.Current;
Now you can easily use to update the collection using the following:
_uiContext.Send(x => YOUR_METHOD(), null);
Replace the YOUR_METHOD with the method you have on the collection!

When and how to call asynch method in ViewModel in windows phone wp8

I have a windows phone (wp8) app using MVVM pattern which contains a view with a pivot control and is binded to a ViewModel (PivotMainViewModel).
Each pivot item contains a different view (datatemplate) and is binded to its own viewmodel (PivotItemViewModel1, PivotItemViewModel1, etc...) and all of this is working well.
My PivotMainViewModel is initialized via xaml binding but as it needs a parameter, it is also "initialized" via the OnNavigatedTo event of the view.
PivotItemViewModel4 pivotItemViewModel4 = Resources["PivotItemViewModel4"] as PivotItemViewModel4;
if (selectedRow.Id > 0)
{
pivotItemViewModel4.InitializeDocumentDetails(selectedRow);
}
but I'm not sure about how to deal with the web service requests which I want to make asynchronously. I need to make a web service request for each individual pivot item "view" and load data the minute it is returned but I need to do this as soon as the pivot is initialized but I'm not sure about the following:
I need to fill data in each of the pivot item "views" and therefore their relevant viewmodels but I'm not sure whether I should make all the asynchronous call from PivotMainViewModel or from each individual viewmodel defined for each of the pivot item.
Whether I call this from the PivotMainViewModel or I call this from each of the pivot item viewmodel, when should I call this?
Should I call this from the constructor:
public PivotItemViewModel1
{
this.Document = GetDocument();
}
or the Get part of my property when its internal variable is null for example? i.e.
public Document Document
{
get { return this._document ?? GetDocument(); }
}
or other?
If I'm supposed to call this from the constructor of the individual viewmodel, how do I do this? I can't use async on a constructor nor can I set async on a property.
What is the best way to make an async call to a web service when dealing with MVVM.
Again, I'm not sure which is best:
4.1 Display a progress bar of some sort, request all 4 requests and wait for all of them to complete and then hide a progress bar
4.2 Display a progress bar of some sort within each individual views and hide them accordingly as each request gets completed
Last problem... Hanging. I've ready numerous articles on the async and hanging but they always describe the problem when dealing with a click event, but I don't understand how to deal with this when you don't have a click event but when you want to call this when a viewmodel is initialized. If you know how, can you please explain and provide a simple example.
There are different concerns to keep in mind:
Code structure. In general you want to restrict the scope of views and view-models, for a modular structure. (This is also called encapsulation.)
UX responsiveness. You don't want to make the user wait, and if they do have to wait, then show them a progress bar / status indicator.
Data usage. The user may be on a data connection, so you want to be conservative with data usage.
So, the question of:
loading all data immediately, or
loading only when the user swipes to the given view
presents a trade-off between #2 and #3. It's really a judgement call, depending on what kind of feel you want the app to have.
I'm not sure whether I should make all the asynchronous call from PivotMainViewModel or from each individual viewmodel defined for each of the pivot item
I do think that #1 above implies the latter -- each view model should own its own data if possible, for the sake of encapsulation.
Edit To use async on a property, you have to use the setter, not the getter. Hook to the view-model's "initialize" event (or similar):
public Document Document
{
get { return this._document; }
set
{
if (this._document == value)
return;
this._document = value;
RaisePropertyChanged("Document");
}
}
public async Task<Document> GetDocument
{
// ...
}
private async Task LoadData()
{
Document = GetDocument();
}
public void Initialize()
{
LoadData();
}

WPF: property similar to WinForms Modified

In WinForms controls like a TextBox have property Modified that gets value "true" after changing the control's content and may be set to "false" manually. Their WPF analogues seem not to have such property (neither IsModified in new naming style). So do I have to handle their modifying events myself or there's some more convenient way?
For example I have few textboxes and a function, which combines their contents into one document for preview. Opening the preview I want to keep an old content for the document, if none of the textboxes was changed or to call the function to produce new document's content if at least one textbox was edited.
In WPF it's easier to control everything through ViewModel/Model... This might be too much/not what you're looking for. But through experience, I feel that the pattern below pays off in easy usage.
Wrap your simple data class (with all the properties that it is using now/in your question now) in a class/Model that implements IEditableObject, INotifyPropertyChanged and possibly IEquitable. Lets call your class Data.
In a wrapper class create fields:
Data _current;
Data _proposed;
Data _previous;
IEditableObject requires you to implement BeginEdit(), EndEdit() and CancelEdit().
in them you need to control the state _current, proposed, and previous. For example,
public void CancelEdit()
{
_current = _previous;
_proposed = null;
}
public void EndEdit()
{
_previous = _proposed;
}
public void BeginEdit()
{
_proposed = _current;
}
You might need more logic in methods above, so this is just an example. The key of knowing if your object has changes is implementing a flag, lot's of people call it IsDirty:
pubic bool IsDirty { get { return _current != _previous; } }
Now the user of this class can easily check the state. Oh, and on more thing each property would have the following mechanism:
public string Example
{
get { return _current.Example;}}
set
{
if(_current.Example == value) return;
BeginEdit();
_current.Example = value;
RaisePropertyChanged (() -> Example);
}
}
What's nice about implementing IEditableObject, all controls respond to it, DataGrid is a good example and also you can easily return to the original state by cancelling edit.
Anyway, there are lots of samples that you should browse for. I just hope to can get you started onto that path...
P.S. this pattern was used before WPF came out, its super common in WinForms as well
WPF doesn't have that because UI is not Data and therefore your UI is not the right place to store information about whether your data has changed or not.
Crappy dinosaur winforms doesn't allow a clean and true separation between UI and application logic/data and therefore has all sorts of horrible hacks in order to mash together these completely separate concepts.
You must learn to develop correctly, using the MVVM pattern. Then you will realize there's no sense in placing state data on any UI elements.

WPF Share and Update lists between different windows

I am trying to create a scheduler in WPF. I have a central static list in main window, which is initialized on load by a backgroundworker. There also is a dispatchtimer in mainwindow, whose interval is recalculated everytime the list changes.
public static List<ListViewcls> TODOdatalst = null;
public static void RefreshdblList()
{
if (ApplicationState.GetValue<bool>("dbDetected"))
{
TODOdatalst = DataAccess.ReadAllTODODataFromDataBase();
InitialiseDailyReminders();
}
}
Now there is another window which contains a listview which is bound to an observable collection, which is derived from the static list in mainwindow. whenever an item is added, updated or deleted from the list, the list in both the windows is manually refreshed via static functions.
private static readonly ObservableCollection<ListViewcls> TO_DOViewlst = new ObservableCollection<ListViewcls>();
public void RefreshView()
{
MainWindow.RefreshdblList();
if (MainWindow.TODOdatalst != null)
InitialiseListView(MainWindow.TODOdatalst);
else
InitialiseListView(DataAccess.ReadAllTODODataFromDataBase());
}
so is this approach proper ?? It works for now but suppose in the future, i have one more window which will also access the list but then i will be managing the refresh of data between 3 windows....that does not figure right. Anyone can suggest some better way, that i can keep a central repository and whenever it is updated all other lists get updated.
When one uses MVVM there is an option to use one ViewModel (the class which acquires and holds the data which is used as Data Contexts for a window/page(s)) between multiple windows and this scenario is common.
I would opt to share the instantiated ViewModel (or class which houses your data) between all windows and pass it in during window creation to have the window/page's data context bound to that one VM. That way the satellite window(s) are using the same data as the main and not having to do any update tricks as shown. Plus any new windows simply bind to the observable collection which everyone else is binding to.

Categories