I have a question about Reactive ui, its bindings, and how it handles ui updates. I always assumed that using ReactiveUi would take care of all ui updates on the ui thread. But I recently found out this isn't always the case.
In short the question is: How can I use reactiveui to two-way-model-bind a viewmodel and a view, and assure that updating the ViewModel doesn't crash when run on a different thread than the ui-thread? Without having to manually subscribe to changes and update explicitely on the uiThread, as that defeats the purpose of reactiveui, as well as making it harder to encapsulate all logic in the PCL.
Below I've provided a very simple (Android) project using Xamaring and Reactiveui, to do the following:
Button with the text 'Hello World'
Clicking on it appends 'a' to the button's text.
I let the Activity implement IViewFor, and I use a ViewModel deriving from ReactiveObject, containing the text that I want to change.
I bind the Activity's button.Text to the ViewModel.Text, to let reactiveui deal with all changes and ui updates.
Finally, I add a function to the button's onclick to append 'a' to the ViewModel.
The issue I have is the following:
button.Click += delegate
{
this.ViewModel.Text += "a"; // does not crash
Task.Run(() => { this.ViewModel.Text += "a"; }); // crash
};
Directly appending 'a' is not an issue. However, adding 'a' on a different thread results in the well-known Java exception: Exception: Only the original thread that created a view hierarchy can touch its views.
I understand the exception and where it's coming from. In fact, if I were to append the 'a' on a different thread, I already had it working with simply not binding the text. But rather by subscribing to changes, and using the RunOnUiThread-method to make changes to the ui. But this scenario kind of defeats the purpose of using ReactiveUi. I really like the clean coding way of the simple statement 'this.Bind(ViewModel, x => x.Text, x => x.button.Text);', but if this has to run on the uiThread, I can't see how to make it work.
And naturally this is the bare mininum to show the problem. The actual problem as to why I bring this up is because I want to use the 'GetAndFetchLatest'-method from akavache. It gets data asynchroniously and caches it, and executes a function (being updating the ViewModel). If the data is already in the cache, it will execute the ViewModel-update with the cached result AND do the computationlogic in a different thread, and then call the same function again once it's done (resulting in the crash, because that's on a different thread, updates the ViewModel, which results in the crash).
Note that even though explicitely using RunOnUiThread works, I really don't want (can't even) to call this within the ViewModel. Because I have a more complex piece of code in which a button simply tells the ViewModel to go fetch data and update itself. If I were required to do this on the uiThread (i.e. after I got data back, I update the ViewModel), then I can't bind iOS to the same ViewModel anymore.
And lastly, here's the entire code to make it crash. I've seen the Task.Run-part sometimes work, but if you add some more tasks and keep updating the ViewModel in them, it's bound to crash eventually on the UI-thread.
public class MainActivity : Activity, IViewFor<MainActivity.RandomViewModel>
{
public RandomViewModel ViewModel { get; set; }
private Button button;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
this.button = FindViewById<Button>(Resource.Id.MyButton);
this.ViewModel = new RandomViewModel { Text = "hello world" };
this.Bind(ViewModel, x => x.Text, x => x.button.Text);
button.Click += delegate
{
this.ViewModel.Text += "a"; // does not crash
Task.Run(() => { this.ViewModel.Text += "a"; }); // crash
};
}
public class RandomViewModel : ReactiveObject
{
private string text;
public string Text
{
get
{
return text;
}
set
{
this.RaiseAndSetIfChanged(ref text, value);
}
}
}
object IViewFor.ViewModel
{
get
{
return ViewModel;
}
set
{
ViewModel = value as RandomViewModel;
}
}
}
This has been already discussed here and there, and the short answer is "as designed, for performance reasons".
I'm personally not really convinced by the later (performance is usually a bad driver when designing an API), but I'll try to explain why I think this design is correct anyway:
When binding an object to a view, you usually expect the view to come and peak (read) at your object properties, and it's doing so from the UI thread.
Once you acknowledge that, the only sane (as in thread-safe and guaranteed to work) way to modify this object (which is being peaked into from the UI thread) is to do so also from the UI thread.
Modifications from other threads may work, but only within specific conditions, that devs usually don't care about (up until they get UI artifacts, in which case they ... perform a refresh...).
For instance if you're using INPC, and your property values are immutable (e.g. string), and your view won't feel bad about observing a value change before it receives the notification of it (simple controls probably are ok with it, grids with filtering/sorting capabilities are probably not ok, unless they completely deep-copy their source).
You should design your ViewModel with the fact that it lives in the UI context in mind.
With Rx, that means having .OnserverOn(/* ui sheduler */) right before ViewModel modification code.
Related
I have done a fair amount of C# programming, but it's been all very small stuff, and coming from a C background (not C++) in the embedded space I haven't fully embraced the OO approach, and I'm happy to say I'm trying to change that.
I'm rewriting some serial comm code I've used over and over into a Class Module that I can just drop into future projects and instantiate. I have everything working except one thing: a logging function where I write the com characters to a textbox and the indices from a ring buffer into labels. I can make that all work, actually, but I was hoping to "generalize" more and pass one of any number of things with a ".Text" property for the ring buffer indices (for example, a Label, a TextBox, or a ToolStripStatusLabel).
Here’s the example, say I have a form with a text box, a label, and a ToolStripStatusLabel. The GUI is on one thread and my class module is running on another one (mostly because it is dealing with the serial port, which is perhaps inconsequential to the question?)
Anyway, lets say I have a modular variable in my class (declared as “Object”?) and I want to create a method in the object to pass in a refence to any one of those three UI elements, each one of which has the “.Text” property to which I can write.
The Class module has a delegate to invoke that will allow it to write to another gui element on the form called txtLog which is visually logging the data. At the same time I want to write something to this other passed-in UI object (say I want to display the index variable from the ring buffer).
It works fine if I stick to a Label (or any one of them) and declare everything as a Label:
===================
Up at the top, the modular variable to hold the control reference:
System.Windows.Forms.Label inPtrLbl;
And then a method to pass the assignment into the class:
public void TurnOnLogging(System.Windows.Forms.TextBox location, System.Windows.Forms.Label inLbl, System.Windows.Forms.Label outLbl)
{
comLogging = true;
logBox = location;
inPtrLbl= new System.Windows.Forms.Label();
inPtrLbl = inLbl;
}
Because the class and the form are on different threads, you need to use the Invoke stuff:
private delegate void UpdateUiTextDelegate(byte text, Int32 ptr);
“Receive” which runs for the event that fires when a char is received looks like this (“esLink” is what I named my serial port inside this class) and you can see the Invoke of “WriteData” happening to write the char into the textbox’s .Text property, which also “grants the right” (I know that’s’ the wrong thing to say) to write the text into the label on the same UI thread in the “WriteData” function below it:
private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte recieved_data;
// Collecting the characters received to our 'buffer' (string).
while (esLink.BytesToRead > 0)
{
recieved_data = (byte)esLink.ReadByte();
// add it to the circular buffer
rxBuffer[nextBufferInputPoint] = recieved_data;
if (++nextBufferInputPoint >= RX_BUFFER_SIZE)
nextBufferInputPoint = 0;
// then put it in the text box for logging
logBox.Invoke(new UpdateUiTextDelegate(WriteData), recieved_data, nextBufferInputPoint);
}
}
private void WriteData(byte text, Int32 ptr)
{
// Assign the value of the recieved_data to the TextBox and label.
if (comLogging)
{
logBox.Text += (char)text;
inPtrLbl.Text = ptr.ToString();
}
}
So, all this works like a charm, really. As long as I declare the variable in the class to be the same type as what I’m passing in. But I want to pass (almost) anything with a .Text property to it so I have more flexibility in designing my GUI. I tried declaring the passed item as an Object, it gets there but the IDE complains that the object doesn’t have a .Text property. I tried declaring it as something with a .Text property and then “changing” it with a “new” but that didn’t work either. I said, ok, I’ll limit it to three types and create overloaded methods for the three types. The problem there is I could only make that work if I declared the three different types at the top and only used one (and set some kind of control variable to decide which one to use when writing to the UI control).
I’m thinking there has to be an easier way. In principle, I want to declare a generic object that I can turn into anything based on what I pass in and access its .Text property. At the very least, creating an overloaded method for each type (realistically there might be 4 or 5 different types only) would be acceptable (but not ideal) and I could live with that.
(I hope I have explained this well, sorry if not...)
Thanks,
-Vin
Honestly it's a little weird for a serial port library to have a dependency on a UI control (see separation of concerns). I'd suggest you set things up so the caller can pass a delegate instead.
Action<char> _logHandler;
public void TurnOnLogging(Action<char> logHandler)
{
comLogging = true;
_logHandler = logHandler;
}
Then, when you have data to log, call the delegate.
private void WriteData(byte text)
{
if (comLogging)
{
_logHandler((char)text);
}
}
This way the caller can decide how the contents are displayed. They can use a control that has a Text property, or a different type of control if they want to. Or maybe they might not want to use a textbox, or a winforms control at all, but maybe log it to a file.
obj.TurnOnLogging( x => TextBox1.Text += x.ToString() );
Or
obj.TurnOnLogging( x => SomeOtherControl1.Caption += x.ToString() );
Or
obj.TurnOnLogging( x => _logger.Write(x) );
You might also consider getting rid of your unusual mechanism in favor of something more idiomatic, such as a custom event.
You may define the type (of the parameter?) as "Control" (System.Windows.Forms.Control), as most UI control classes are derived from this class. Actually, the Control class has really a large number of properties, such as "Text", "Location", "Size", "Parent", etc.
See https://learn.microsoft.com/dotnet/api/system.windows.forms.control
How do I setup an event loop (main loop) in a UWP app?
My goal is to have an app that has a main page with a continuously updating calculation, that then continuously updates an image on the page. While that is constantly happening, the user can click a button to change the calculation behavior, or to go to a second page and edit related data.
The user can then click back to the main page and see the image and calculation restart and continuously update. (The calculation is complex, so it should go as fast as possible and use up as much app time as possible).
If there is a way to accomplish this without an event loop I would like to know that also, but so far I have not found a way.
With an event loop, I can simply update the calculation and UI every time through the loop. But I have not found any way to do so. This question asked nearly the same thing, but was never directly answered and had a different use case anyway.
The closest I have found to a solution is to grab the CoreDispatcher from the CoreWindow and use the RunIdleAsync() method to create a loop:
public MainPage()
{
this.InitializeComponent();
Windows.UI.Core.CoreWindow appwindow = Windows.UI.Core.CoreWindow.GetForCurrentThread();
Windows.UI.Core.CoreDispatcher appdispatcher = appwindow.Dispatcher;
//create a continuously running idle task (the app loop)
appdispatcher.RunIdleAsync( (dummyt) =>
{
//do the event loop here
.
.
.
if (appdispatcher.ShouldYield()) //necessary to prevent blocking UI
{
appdispatcher.ProcessEvents(Windows.UI.Core.CoreProcessEventsOption.ProcessAllIfPresent);
}
});
}
The main problem with this is that you can't switch between pages (you get a system exception from dispatching events within an already dispatched event).
Second, this is very messy and requires maintaining extra state in the event loop. Besides, why should I have to go through these contortions just to have some calculations happening while the app is waiting for user input?
Is there a way to do this (besides switching to a C++ DirectX app)?
I don't know about setting up your own event loop, but there is no reason to do so.
What you are talking about sounds like a great case for Tasks. You would start a calculation Task whenever your user did something, having it report its progress via standard C# events if you need mid-operation updates. Those updates would modify properties in your view model which the binding system would then pick up.
You could also make your calculation code cancellable so changes can abort a previous calculation.
All of this involves pretty standard UWP concepts; no need for a special event loop. That you are even considering that makes me think you need to study MVVM and multi-threading/tasks; you are still thinking in a very "Win-Forms" kind of way.
If we're talking about some event loop, or stream, .Net has a great library named Rx, or Reactive Extensions, which may be helpful for you. You can set up a simple flow, something like this:
var source = Observable
// fire event every second
.Interval(TimeSpan.FromSeconds(1), Scheduler.DispatcherScheduler)
// add timestamp for event
.Timestamp()
// gather system information to calculate
.Select(GetSystemInfo);
Note that the events right now are on UI thread, as you need to access the controls. Now you have two options: use Rx for background processing too or use TPL Dataflow' TransformBlock for processing your system information into new image (it can be Observer and Observable at a time). After that you need to get back to UI thread.
First option:
var source = Observable
// fire event every second
.Interval(TimeSpan.FromSeconds(1), DispatcherScheduler.Current)
// add timestamp for event
.Timestamp()
// gather system information to calculate
.Select(GetSystemInfo)
// expensive calculations are done in background
.Select(x => x.ObserveOn(DefaultScheduler.Instance))
.Select(x => Expensive(x))
.Select(x => x.ObserveOn(DispatcherScheduler.Current))
.Select(x => UpdateUI(x));
You probably should split this chain into several observers and observables, still the idea is the same, more information here: Rx Design Guidelines.
Second option:
var action = new TransformBlock<SystemInfo, ImageDelta>(CalculateDelta,
new ExecutionDataflowBlockOptions
{
// we can start as many item processing as processor count
MaxDegreeOfParallelism = Environment.ProcessorCount,
});
IDisposable subscription = source.Subscribe(action.AsObserver());
var uiObserver = action.AsObservable()
.Select(x => x.ObserveOn(DispatcherScheduler.Current))
.Select(x => UpdateUI(x));
I want to note that UWP and MVVM pattern do provide a possibility to work with binding between UI and ObservableCollection, which will help you to notify user in most natural way.
I have to create a WPF UI, which subsribes to real time Fx Rate(Currency + rate) updates and displays them in a grid (roughly 1000 updates per second, which means each row in the grid could get updated upto 1000 times per second).The grid would have atleast 50 rows at any point in time.
Towards this, I have created a Viewmodel which subscribes to the update events, and store those updates inside a concurrent dictionary with key as symbol and value as a RateViewModel object. Then I have another observable collection which has all those rateviewmodel objects, and bind that to a grid.
Code:
public class MyViewModel
{
private readonly IRatesService ratesService;
private readonly ConcurrentDictionary<string, RateViewModel> rateDictionary;
private object _locker = new object();
public MyViewModel(IRatesService ratesService)
{
this.ratesService = ratesService;
this.ratesService.OnUpdate += OnUpdate;
rateDictionary = new ConcurrentDictionary<string, RateViewModel>();
RateViewModels = new ObservableCollection<RateViewModel>();
}
private void OnUpdate(object sender, RateUpdateEventArgs e)
{
RateViewModel exisistingRate;
if (!rateDictionary.TryGetValue(e.Update.Currency, out exisistingRate))
{
exisistingRate = new RateViewModel(new Rate(e.Update.Currency, e.Update.Rate));
rateDictionary.TryAdd(e.Update.Currency, exisistingRate);
return;
}
lock (_locker)
{
exisistingRate.UpdateRate(e.Update.Rate);
}
Application.Current.Dispatcher.BeginInvoke(new Action(() => SearchAndUpdate(exisistingRate)));
}
public ObservableCollection<RateViewModel> RateViewModels { get; set; }
private void SearchAndUpdate(RateViewModel rateViewModel)
{
//Equals is based on Currency
if (!RateViewModels.Contains(rateViewModel))
{
RateViewModels.Add(rateViewModel);
return;
}
var index = RateViewModels.IndexOf(rateViewModel);
RateViewModels[index] = rateViewModel;
}
}
I have 4 questions over this:
Is there a way I can eliminate the ObservableCollection, as it's leading to 2 different datastructures storing the same items - but still have my updates relayed to the UI?
I have used Concurrent Dictionary, which leads to locking the whole update operation. Is there any other clever way of handling this rather than locking the whole dicitionary or for that matter any datastructure?
My UpdateRate method also locks - all my properties on my RateviewModel is read only except the price, as this is getting updated. Is there a way to make this atomic, please note that the price is coming in as a double.
Is there a way I can optimize the SearchAndUpdate method, this is kind of related to 1st. At the moment I believe it's an O(n)operation.
Using .NET 4.0 and have omitted INPC for brevity.
*EDIT:*Could you please help me in rewriting this in a better manner taking all the 4 points into account? Psuedocode will do.
Thanks,
-Mike
1) I wouldn't worry about 50 extra refs floating around
2) Yes, lockless data structures are doable. Interlocked Is your friend here and they are pretty much all one offs. ReaderWriterLock is another good option if you aren't changing what items are in your dictionary often.
3) Generally, if you are dealing with more data more data than the UI can handle you are going to want to do the updates in the background, only fire INPC on the UI thread, and more importantly have a facility to drop UI updates (while still updating the backing field). Basic approach is going to be something like:
Do an Interlocked.Exchange on the backing field
Use Interlocked.CompareExchange to set a private field to 1, if this returns 1 exit becuase there is still a pending UI update
If Interlocked.CompareExchange returned 0, invoke to the UI and fire your property changed event and update you throttling field to 0 (technically there is more you need to do if you care about non x86)
4) SearchAndUpdate Seems superfluous... UpdateRate should be bubbling to the UI and you only need to Invoke to the UI thread if you need to add or remove an item to the observable collection.
Update: here is a sample implementation... things are little more complicated because you are using doubles which don't get atomicity for free on 32 bit CPUs.
class MyViewModel : INotifyPropertyChanged
{
private System.Windows.Threading.Dispatcher dispatcher;
public MyViewModel(System.Windows.Threading.Dispatcher dispatcher)
{
this.dispatcher = dispatcher;
}
int myPropertyUpdating; //needs to be marked volatile if you care about non x86
double myProperty;
double MyPropery
{
get
{
// Hack for Missing Interlocked.Read for doubles
// if you are compiled for 64 bit you should be able to just do a read
var retv = Interlocked.CompareExchange(ref myProperty, myProperty, -myProperty);
return retv;
}
set
{
if (myProperty != value)
{
// if you are compiled for 64 bit you can just do an assignment here
Interlocked.Exchange(ref myProperty, value);
if (Interlocked.Exchange(ref myPropertyUpdating, 1) == 0)
{
dispatcher.BeginInvoke(() =>
{
try
{
PropertyChanged(this, new PropertyChangedEventArgs("MyProperty"));
}
finally
{
myPropertyUpdating = 0;
Thread.MemoryBarrier(); // This will flush the store buffer which is the technically correct thing to do... but I've never had problems with out it
}
}, null);
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate {};
}
Mike -
I would approach this a little differently. You really dont need an Observable Collection unless new Fx rows are being added. Observable Collection as you know only gives you built-in change notification in that scenario. If you have a list of 50 rows (for example) and the Fx object (which represents each individual row) is updated 1000 times a second - then you can very well use the INotifyPropertyChanged on the Fx Properties on the Object and let that mechanism update the UI as they change. My line of thought is - this is a simpler approach for UI updates rather than move them from one collection to another
Now with regards to your second point - 1000 updates in a second (to an existing FX object) - which technically is unreadable from a UI perspective - the approach I have taken is freeze and thaw - which means you essentially intercept the InotifyPropertyChanged (as its firing to the UI) and keep it frequency based - so for example - every 1 sec - whatever my status of all FX objects is (refresh the UI). Now within that second - whatever updates happen to the FX properties - they keep overwriting on themselves - and the latest/correct value when the 1 second interval happens - is shown to UI. That way - data being shown to UI is always correct and relevant when its displayed to UI.
There are a couple of factors to take into account, especially if the number of displayed rates will change dynamically. I'm assuming the 1000 updates/sec are coming from a thread other than the UI thread.
The first is that you will need to marshall the updates to the UI thread - done for you for updates to an existing ViewModel, not done for you for new/deleted ViewModels. With a 1000 updates a second you probably want to control the granularity of the marshalling to the UI thread and the context switching that this entails. Ian Griffiths wrote a great blog series on this.
The second is that if you want your UI to remain responsive you probably want to avoid as many gen 2 garbage collections as possible which means minimising the pressure on the GC. This might be an issue in your case as you create a new Rate object update for each update.
Once you start to have a few screens that do the same thing you'll want to find a way to abstract this updating behaviour out into a common component. Other wise you'll be sprinkling threading code through your ViewModels which is error prone.
I've created an open source project, ReactiveTables, which addresses these three concerns and adds a couple of other features such as being able to filter, sort, join your model collections. Also there are demos showing how to use it with virtual grids to get the best performance. Maybe this can help you out/inspire you.
History of the problem
This is continuation of my previous question
How to start a thread to keep GUI refreshed?
but since Jon shed new light on the problem, I would have to completely rewrite original question, which would make that topic unreadable. So, new, very specific question.
The problem
Two pieces:
CPU hungry heavy-weight processing as a library (back-end)
WPF GUI with databinding which serves as monitor for the processing (front-end)
Current situation -- library sends so many notifications about data changes that despite it works within its own thread it completely jams WPF data binding mechanism, and in result not only monitoring the data does not work (it is not refreshed) but entire GUI is frozen while processing the data.
The aim -- well-designed, polished way to keep GUI up to date -- I am not saying it should display the data immediately (it can skip some changes even), but it cannot freeze while doing computation.
Example
This is simplified example, but it shows the problem.
XAML part:
<StackPanel Orientation="Vertical">
<Button Click="Button_Click">Start</Button>
<TextBlock Text="{Binding Path=Counter}"/>
</StackPanel>
C# part (please NOTE this is one piece code, but there are two sections of it):
public partial class MainWindow : Window,INotifyPropertyChanged
{
// GUI part
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(doProcessing);
thread.IsBackground = true;
thread.Start();
}
// this is non-GUI part -- do not mess with GUI here
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property_name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
long counter;
public long Counter
{
get { return counter; }
set
{
if (counter != value)
{
counter = value;
OnPropertyChanged("Counter");
}
}
}
void doProcessing()
{
var tmp = 10000.0;
for (Counter = 0; Counter < 10000000; ++Counter)
{
if (Counter % 2 == 0)
tmp = Math.Sqrt(tmp);
else
tmp = Math.Pow(tmp, 2.0);
}
}
}
Known workarounds
(Please do not repost them as answers)
I sorted the list according how much I like the workaround, i.e. how much work it requires, limitations of it, etc.
this is mine, it is ugly, but simplicity of it kills -- before sending notification freeze a thread -- Thread.Sleep(1) -- to let the potential receiver "breathe" -- it works, it is minimalistic, it is ugly though, and it ALWAYS slows down computation even if no GUI is there
based on Jon idea -- give up with data binding COMPLETELY (one widget with databinding is enough for jamming), and instead check from time to time data and update the GUI manually -- well, I didn't learn WPF just to give up with it now ;-)
Thomas idea -- insert proxy between library and frontend which would receiver all notifications from the library, and pass some of them to WPF, like for example every second -- the downside is you have to duplicate all objects that send notifications
based on Jon idea - pass GUI dispatcher to library and use it for sending notifications -- why it is ugly? because it could be no GUI at all
My current "solution" is adding Sleep in the main loop. The slowdown is negligible, but it is enough for WPF to be refreshed (so it is even better than sleeping before each notification).
I am all ears for real solutions, not some tricks.
Remarks
Remark on giving up with databinding -- for me the design of it is broken, in WPF you have single channel of communication, you cannot bind directly to the source of the change. The databinding filters the source based on name (string!). This requires some computation even if you use some clever structure to keep all the strings.
Edit: Remark on abstractions -- call me old timer, but I started learning computer convinced, that computers should help humans. Repetitive tasks are domain of computers, not humans. No matter how you call it -- MVVM, abstractions, interface, single inheritance, if you write the same code, over and over, and you don't have way to automatize the things you do, you use broken tool. So for example lambdas are great (less work for me) but single inheritance is not (more work for me), data binding (as an idea) is great (less work) but the need of proxy layer for EVERY library I bind to is broken idea because it requires a lot of work.
In my WPF applications I don't send the property change directly from the model to the GUI. It always goes via a proxy (ViewModel).
The property change events are put in a queue which is read from the GUI thread on a timer.
Don't understand how that can be so much more work. You just need another listener for your model's propertychange event.
Create a ViewModel class with a "Model" property which is your current datacontext. Change the databindings to "Model.Property" and add some code to hook up the events.
It looks something like this:
public MyModel Model { get; private set; }
public MyViewModel() {
Model = new MyModel();
Model.PropertyChanged += (s,e) => SomethingChangedInModel(e.PropertyName);
}
private HashSet<string> _propertyChanges = new HashSet<string>();
public void SomethingChangedInModel(string propertyName) {
lock (_propertyChanges) {
if (_propertyChanges.Count == 0)
_timer.Start();
_propertyChanges.Add(propertyName ?? "");
}
}
// this is connected to the DispatherTimer
private void TimerCallback(object sender, EventArgs e) {
List<string> changes = null;
lock (_propertyChanges) {
_Timer.Stop(); // doing this in callback is safe and disables timer
if (!_propertyChanges.Contain(""))
changes = new List<string>(_propertyChanges);
_propertyChanges.Clear();
}
if (changes == null)
OnPropertyChange(null);
else
foreach (string property in changes)
OnPropertyChanged(property);
}
This isn't really a WPF issue per se. When you have a long-running operation that updates a set of data rapidly, keeping the UI updated - any UI, whether it's WPF or WinForms or just VT100 emulation - is going to present the same problem. UI updates are comparatively slow and complex, and integrating them with a fast-changing complex process without hurting that process requires a clean separation between the two.
That clean separation is even more important in WPF because the UI and the long-running operation need to run on separate threads so that the UI doesn't freeze while the operation is running.
How do you achieve that clean separation? By implementing them independently, providing a mechanism for periodically updating the UI from within the long-running process, and then testing everything to figure out how frequently that mechanism should be invoked.
In WPF, you'll have three components: 1) a view, which is the physical model of your UI, 2) a view model, which is the logical model of the data that is displayed in the UI, and that pushes changes in the data out to the UI through change notification, and 3) your long-running process.
The long-running process can be almost completely unaware of the UI, so long as it does two things. It needs to expose public properties and/or methods so that the view model can examine its state, and it needs to raise an event whenever the UI should be updated.
The view model listens to that event. When the event is raised, it copies state information from the process to its data model, and its built-in change notification pushes those out to the UI.
Multithreading complicates this, but only a bit. The process needs to run on a different thread than the UI, and when its progress-reporting event is handled, its data will be copied across threads.
Once you've built these three pieces, the multithreading is very straightforward to accomplish using WPF's BackgroundWorker. You create the object that's going to run the process, wire its progress-reporting event up with the BackgroundWorker's ReportProgress event, and marshal data from the object's properties to the view model in that event handler. Then fire off the object's long-running method in the BackgroundWorker's DoWork event handler and you're good to go.
A user interface that changes faster than the human eye can observe (~25 updates/sec) is not a usable user interface. A typical user will observe the spectacle for at most a minute before giving up completely. You are well past this if you made the UI thread freeze.
You have to design for a human, not a machine.
Since there are too many notifications for the UI to handle, why not just throttle the notifications a bit? This seems to work fine:
if (value % 500 == 0)
OnPropertyChanged("Counter");
You could also limit the frequency of the notifications, using a timer:
public SO4522583()
{
InitializeComponent();
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(50);
_timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
DataContext = this;
}
private bool _notified = false;
private DispatcherTimer _timer;
void _timer_Tick(object sender, EventArgs e)
{
_notified = false;
}
...
long counter;
public long Counter
{
get { return counter; }
set
{
if (counter != value)
{
counter = value;
if (!_notified)
{
_notified = true;
OnPropertyChanged("Counter");
}
}
}
}
EDIT: if you cannot afford to skip notifications because they're used by other parts of your code, here's a solution that doesn't require big changes in your code:
create a new property UICounter, which throttles the notifications as shown above
in the Counter setter, update UICounter
in your UI, bind to UICounter rather than Counter
A layer between UI and the library is necessary. This will ensure that you will be able to do interaction testing and also allow you to swap out the library with another implementation in future without much change. This isn't a duplication, but a way of providing an interface for UI layer to communicate. This layer will accept objects from library, convert them to specific data transfer objects and pass them onto another layer which will have the responsibility to throttle the updates and convert them to your specific VM objects.
My opinion is that VMs should be as dumb as possible and their only responsibility should be to provide data to views.
Your qestion sounds similar to slow-down-refresh-rate-of-bound-datagrid.
At least the answers are similar
Have a shadow copy of your data bound to the gui element instead of binding the original data.
Add an eventhandler that update the shadow-copy with a certain delay from the original data.
You need to disconnect the source of the notifications from the target for the notifications. The way you have it set up now, every time the value changes, you go through an entire refresh cycle (which I believe is blocking your processing function from continuing as well). This is not what you want.
Provide an Output stream to your processing function which it would use to write its notifications.
On the monitoring side, attach an input stream to that outputstream and use it as the data source for your UI component. This way there isn't any notification event handling going on at all - the processing is running flat out as fast as it can, outputting monitor data to the output stream you provide. Your monitor UI is simply rendering whatever it receives in the input stream.
You will need a thread to continuously read from the input stream. If no data is available, then it should block. If it reads some data, it should dump it into the UI.
Regards,
Rodney
OK, please disregard what has gone before. I'm not getting the errors anymore, so it seems my problem is with getting a Chart to update when I change the values to which the Chart is data bound.
//Disregard below here
Hi all. I have a WinForms application that has a panel, panel1. A background thread creates some other controls that then get added to panel1 like so
panel1.Controls.AddRange(myArrayOfControls);
This works great and I can see my controls get added. But, when new data comes in on another thread, I update values in the controls' parent objects and then need to Refresh() to get the display to update with the new values. Calling Refresh() in either context, the thread where the data comes in or the objects that receive the updated data causes an InvalidOperation exception because Invoke is required. I've tried using Invoke in my model objects and also the thread where the data is incoming and can't seem to shake the error.
If anyone has some guidance I'd greatly appreciate it.
UPDATE: Here's a little more info. I didn't think it would require it, but I was wrong. :)
I have an object class MyObject. This MyObject class gets created in a thread called topologyThread. Data comes in on dataThread. Instances of MyObject have a Panel instance variable and the Panel has child Controls including two Charts from the System.Windows.Forms.DataVisualization.Charting namespace. So, as data comes in on dataThread, I update the respective data values in the MyObject objects and then need to refresh the Charts to show the updated data.
I do know the data is processing fine. In my MyObject class, I'm logging the new values to Console in the setter for the property and see the new values show up.
You must do both operations (refresh and updating of control's parent object) from the main UI thread. If you are modifying a control from a background thread and not getting an exception that is bad luck because it is definitely an error.
The best way to do this is to use
theControl.Invoke(new MethodInvoker(MyUpdateMethod));
If you have a sample of how the update is done, we can give a better sample on how to properly call it from the background thread.
JaredPar is a pretty good answer. I would like to add to it a bit as to the reason your code sort of works.
With windows forms you can talk to the UI thread from other threads. This is really bad practice in all cases.
The catch is that when you do it, it is hard to catch because sometimes the UI will work as if nothing is wrong. item will get added or changed and the UI will reflect the changes. However other times running the same exact code, it will not work.
That is the catch with touching the UI from any thread other then the UI thread. The out come is inconsistent and that is why is very bad practice.
God I wish I could comment. :)
JaredPar's answer is good. It can cause problems in some instances (notably when the method is invoked before the form is finished being constructed). Here's a somewhat more robust implementation (using extension methods)
public static class ControlInvokeExtensions
{
public static void InvokeOnHostThread(Control host, MethodInvoker method)
{
if (IsHandleCreated)
Invoke(new EventHandler(delegate { method(); }));
else
method();
}
}
now you can call it this way
panel1.InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));
or if you're in the form:
InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));