user3900520 user3900520 - 2 months ago 19
C# Question

How to update WPF UI as database changes

I am writing an application in C#/WPF/Entity Framework DB first that communicates with a proprietary piece of industrial hardware that measures values like flow and temperature from a coolant line. Those values frequently are updated in my SQL Server database with a thread that runs in the background, and then are displayed in my UI.

My concern currently is with how I update my UI to reflect those changes. Here's an example of how I update a datagrid:

I set my datacontext to my viewmodel, and instantiate a thread that runs every second:

_DataContext = new ViewModels.SummaryTable_ViewModel();
this.DataContext = _DataContext;
UIUpdateThread = new Thread(UIUpdaterThread);
UIUpdateThread.IsBackground = true;
UIUpdateThread.Start();


The model my grid is based on is an IList<> that looks like this:

private IList<channel> _channel;
public IList<channel> Channels
{
get
{
return _channel;
}
set
{
_channel = value;
//NotifyPropertyChanged();
OnPropertyChanged("Channels");
}
}


Then every second my UIUpdateThread calls my FillChannels() method which is as follows, then the grid updates based on the propertychanged notification:

using (var DTE = new myEntities())
{
if (DTE.channels.Any())
{
var q = (from a in DTE.channels
where a.setup.CurrentSetup == true
orderby a.ChannelNumber
select a).ToList();

this.Channels = q;
}
}


So my question is this: is there a better, more elegant way to do this? This feels "wrong" for lack of a better term. And it has bad side effects, like resetting the user-set sort on my datagrid every time the thread runs. Also I think it breaks the MVVM pattern, though I'm not certain.

For example I'm thinking what if the thread that talks to my hardware updates a shared Channel object every time it polls the hardware for data, and I just bind my UIs to it, that way I wouldn't have to run this thread (or the other threads on my other UIs that do the same thing), just update based on PropertyChanged notifications. Or is there some other method I'm unaware of completely? When searching for an answer I saw mentions of the unit of work pattern, with which I'm unfamiliar, is that a relevant concept here?

As you can see, I'm not too sure where to go from here, and could really use some help. Sorry for the wall of text, I just wanted to be as thorough as possible. Any assistance would be greatly appreciated. Thanks.

Answer

I think your model should be the one that's notifying your view model when there is new data. You should put whatever mechanism that accomplishes this (like the timer I assume you're using) into the model.

Basically, your model notifies your view model, and your view model notifies the view that there are data changes. If you setup your view correctly -- and use XAML rather than code-behinds, it should all come together automatically via the data bindings between the view and the view model.

Regarding sorting: that should be a separate property of your view model that's bound to the sorting property on your view's grid. Your model should maintain the user-set sort, and apply it appropriately to the updated data before it notifies the view model when data is changed.

I'm not sure if unit of work is applicable here since you're not talking about persisting related rows of data to multiple tables and doing so as one transaction -- you're only reading data. I could be wrong though, as I haven't studied unit of work that much.


To address your questions in the comments:

Let's take a step back. Your view model should only be concerned with logic that's related to the presentation of the associated view. It should not have any data access code or code that performs any business logic or domain-related tasks; that code goes in what is collectively called the "model" (forgive me if you already have this, but it's hard to tell exactly where the code you provided in your question resides).

The model can consist of a number of classes that perform different business logic tasks or that represent your domain/entity POCOs (like your entity framework generated classes).

I'm a little unclear what your Channel object is and how it's separate from your entity class/POCO, i.e. the EF-generated model. But again, your interaction with EF and the maintenance and logic related to your entities should happen in the Model.

So yes, the idea would be that you look for data changes in the model using whatever mechanism you want to -- e.g. a timer (btw there are timers that run synchronously, so don't conflate the notion of "timer" with "thread") or binding directly to the "collection changed" event on your collection of entities. When a change is detected, you would simply do whatever business logic is required for that change in the model (like applying transformations, sorting, logging, etc.) and then notify the view model that the change has occurred.