WPF Share and Update lists between different windows - c#

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.

Related

Updating listviews when db updates

I'm new at MVVM and I have a problem with updating lists.
I have 2 windows and ListViews in it. They are connected to a property "Tasks". When I add a new row to my db I need to refresh ListViews. I've done it, but only for 1 window.
adding a new row to a db table
private void OnAddTaskExecuted(object p)
{
tasks tsk = new tasks()
{
taskname = "1",
description = "",
date = DateTime.Now,
empID = 2
};
Core.db.tasks.Add(tsk);
Core.db.SaveChanges();
Tasks = new ObservableCollection<tasks>(Core.db.tasks);
//it updates only in the window from which I'm adding the row
}
viewmodel ctor
public MainWindowViewModel()
{
AddTask = new RelayCommand(OnAddTaskExecuted, p => true);
Tasks = new ObservableCollection<tasks>(Core.db.tasks);
}
So after clicking a btn I have this situation. ListView updates only in window where I click, but not in another (the new tasks is the first one)img
P.S. I have 2 same windows, I just making a new same window by btn click. That is just for a test. I'm actually creating a big project with lots of pages in it, and I need to update every Collection that have tasks in it.
Your issue is that you have two different Window instances with separate instances of ObservableCollection<tasks> Tasks
There are several ways in dotnet to structure your data in a persistent manner. Here are some:
Assuming both Windows are of the same type, the easiest way is to just make Tasks a static field.
Create a separate class, eg. called "TaskManager", with a field Tasks, and use that. If TaskManager is a static class, you don't have to bother with organizing any instances. Alternatively, there are concepts like Singletons. You can also store your data, or objects containing your data inside App.xaml.cs, where you can then access it via Application.Current.SomeField
If you want to reuse the same Window instance instead of storing your data somewhere else, that really depends on how you navigate to your Window. If you open your Window with something like this...
MyWindow window = new MyWindow();
window.Show();
... then you'll have to store your WindowInstance in a persistent manner as explained above, instead of instanciating a new one with new MyWindow() every time you want to show it.

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;
}

ObservableCollection and CollectionView

I'm writing an application that reads data from a local db and display them in a listbox(I'm working in WPF).
I've a DVD object, where its properties are the columns of the db. This DVD object also implements INotifyPropertyChanged. "MyDVDs" is the table that refers to db. Once created these object, I create a class that inherits from ObservableCollection and takes data from "MyDVDs" in the constructor. However I don't need only to add, remove and update data from the listbox, but I also need to sort and filter them. Here is the code for ObservableCollection:
class ObservableDVD : ObservableCollection<DVD>
{
private ICollectionView collection;
public ObservableDVD(MyDVDs e)
{
foreach (DVD d in e.DVDs)
{
this.Add(d);
}
Collection = CollectionViewSource.GetDefaultView(this);
}
public ICollectionView Collection
{
get { return collection; }
private set { collection = value; }
}
}
I wanted to know, this is a good way?? Or can I do better?
In the MainWindow of the project, when Load_Window event fires, I assign the Collection property to listbox.ItemSource(in MainWindow code-behind I declare a private field that obviously refers to an ObservableDVD Object). I have some buttons that allow me to do the operations I tell you before.In the event headler of the buttons, I directly update and modify the ObservableDVD Object, not its property Collection. However, the Collection property also reflects those changes.
Why this behavior occurs?
It's ok for me, but I can't understand why it's happens. Is because of the notifications?
The property Collection has a reference to the view of the ObservableDVD. Being a reference means pointing to the same data in memory.
ObservableCollection Class Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
The Collection you are specifying is just a "view" of the ObservableDVD collection. Which means that both are really pointing to the same data in the memory, they're not 2 separate things. A "view" can be a subset of items when you apply filters to a collection, for instance.
Otherwise said, your ObservableDVD contains your "Data Table" for the entire dataset while the ICollectionView lets you manipulate which records/objects are visible to the user through custom logic.

Updating an ObservableCollection in a separate thread

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.

Using a Queue as datasource

I would like to display items from a Queue in a Gridview in Windows Forms. I can set the datasource attribute of the Gridview to the Queue, but it won't be automatically updated. I know I can use the BindingList class, but then I lose my Queue functionality.
Is there any way to combine the two classes, or do I have to implement one of the behaviours in a derived class?
What I'm doing is processing a list of items, I want to show the remaining ones in a grid. The data should not be changed by the user, but I want the GridView to be updated as the contents of the Queue change.
Example:
In form:
Proccessor pro = new Processor();
gridview.DataSource = pro.Items;
In class:
class Proccessor {
Queue<DataBlock> _queue = new Queue();
public Queue<DataBlock> Items {
get {
return _queue;
}
}
public void AutoProcess() {
while (_queue.Count > 0) {
Process(_queue.Dequeue());
}
}
private void Process(DataBlock db) { ... }
}
The whole purpose of a Queue is that entries can only be added in one place. So the idea of binding this to a UI grid so it can be updated is, uh, interesting - how should the UI look?
You'll definitely have to consider your own custom collection, or as you say, derive from BindingList and handle e.g. CancelNew accordingly. See the MSDN article for details.
I would subclass Queue as QueueForDisplay. The constructor would take a view control. I would override the Enqueue and Dequeue methods. In those overrides, I would update the view control. If you don't like the tight coupling, you could simply subclass Queue as QueueWithEvents and provide OnEnqueue and OnDequeue events.

Categories