I'm creating a spectrum analyzer and I want to make it gridded. My spectrum will be painted over a panel in a windows forms application. When I draw lines inside panel_Paint function which is tied to panel's paint event, It redraws 2048/48000 times a second because it is a real time FFT spectrum with the FFT size of 2048 and sampling rate of 48000. Here is my way of drawing lines inside the paint function:
private void panel1_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < panel1.Width; i+= panel1.Width / 20)
{
for (int j = 0; j < panel1.Height; j+= panel1.Height / 20)
{
g.DrawLine(Pens.Black, i, j, i, panel1.Height + j);
g.DrawLine(Pens.Black, i, j, panel1.Width + i, j);
}
}
}
Here is the spectrum after lines:
I like how spectrum looks but while computation there is no time for the spectrum while waiting for the lines to be redrawn.
Note: The above image is an instant(single fft block) screen shot of the spectrum. As I mentioned above, spectrum is not readable while performing real-time application.
Any help will be appreciated, thank you in advance.
Making a real-time application with graphics is not trivial. Several points:
Painting at 2048 Hz is not usefull for the viewer, the screen usually refreshes at 60 Hz because our eyes cannot observe faster changes.
The paint event is meant to paint everything in one go, so gridlines and the FFT graph.
You can trigger the paint event by calling Control.Invalidate() or in this case Control.Refresh() might give a more pleasant refresh. See: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invalidate
It might be necessary to move the code that collects the signal and calculates the FFT spectrum to a background thread, where you have much more control over its timing and can process the data uninterrupted.
So a possible way of organizing the application would be like this:
Have a background thread do the heavy work and collect data in a buffer data structure.
Put a timer on the UI Form that calls Control.Refresh() periodically. See: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer
Have the paint event handler read all the data prepared in the buffer and clear it. And then use that data to paint a FFT graph. Combining all the data from multiple FFT blocks.
Make sure the buffer can contain multiple FFT bocks by using a suitable data structure like a ConcurrentQueue<T> where T is of a Type that contains one FFT block. See: https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1
There is a lot of information around about how to communicate from a background thread to a foreground thread like the one that is handling the paint events. So I'm not going to duplicate that here. But done right it makes your application run smooth and responsive.
Combining multiple FFT blocks into one graph drawing is a challenge. So, at first, keep that part simple by for example discarding all but the most recent block. Then when everything else works improve on the design of this part.
Also start with a paint timer set to 20 Hz or there about and increase it from there. Because the graphics system might not like to many paint actions.
Turns out I used the wrong tools for this job, when processing real time data, you should make computations really fast before displaying it. And when It came to displaying, double buffering the parent display tool works like "magic".
As I mentioned in the question above, I was using panel over the form while drawing the spectrum, when I removed the panel and changed the whole form to a UserControl, I was able to do double buffering that I couldn't do in the panel and the whole problem was solved.
When the DoubleBuffered property is set to true, the current drawing data is written to a second buffer before being drawn on the surface on which the drawing will be made, then the data in this second buffer is quickly written to the memory where the drawing will be made. Voila! Flickering is prevented.
The code inside the UserControl's load function:
this.DoubleBuffered = true;
Related
I am currently converting some of my old games implemented using C# (FW version 4.7.2) WinForms to Direct3D using C++.
Currently, all my real-time graphics games implement the OnPaint override, draw into the Graphics object retrieved from the PaintEventArgs parameter and invalidate right after drawing the current frame. (I don't know if this is not recommended, but it works great). These apps use double buffering of course. This causes 100% utilization of one core, which is OK.
The first obstacle I came accross when porting my games to C++ and Direct3D is the refresh rate of the window even if I had done the same thing by implementing the WndProc() and calling InvalidateRect() after drawing.
I followed this quick start guide: https://learn.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart
The problem is,
With my windows forms apps, the refresh rate is around 60 fps drawing over a full-size background image, 300-400 fps when drawing on an empty background. The following example, drawing only a rectangle, produces an amazing 2,500 fps:
public class Surface : Form
{
private int fps = 0;
private int lastFPS = 0;
private double lastSeconds = 0;
private Stopwatch chronometer = Stopwatch.StartNew();
Font font = new Font("Courier New", 10f);
public Surface()
{
BackColor = Color.Black;
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.White, 100, 100, 100, 100);
e.Graphics.DrawString("FPS: " + lastFPS, font, Brushes.White, 5, 5);
fps++;
if (chronometer.Elapsed.Seconds > lastSeconds)
{
lastSeconds = chronometer.Elapsed.Seconds;
lastFPS = fps;
fps = 0;
}
Invalidate();
}
}
Needless to say, I was expecting a much better frame rate when porting to DirectX, but the problem is that the WM_PAINT event is not fired frequently enough and I am stuck at 100 fps even if I don't draw anything, and cpu utilization is around 10% only.
I only added the following line to the source code of the quick start guide:
case WM_PAINT:
{
pDemoApp->OnRender();
ValidateRect(hwnd, NULL);
InvalidateRect(hwnd, NULL, TRUE); // THIS IS THE LINE I'VE ADDED
}
What am I doing wrong?
I read that the rendering target of Direct 2D is automatically double buffered,
So the question is, why the WM_PAINT event gets fired only 100 times per second?
Note: Checked the windows forms Control.Invalidate() method source, and what it does is to call InvalidateRect (or RedrawWindow() if child controls are to be invalidated as well)
NOTE: Am I using direct 2d drawing incorrectly here? Should I continuously draw on a separate thread and let DirectX refresh the rendering target as it sees fit?
I've found the issue.
The following statement, while creating the render target for Direct2D, uses the default present option D2D1_PRESENT_OPTIONS_NONE, which causes the rendering engine to wait for the vSync, thus the maximum frames per second on my laptop equals 60 Hz (the refresh speed of the laptops monitor)
// Create a Direct2D render target.
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
By changing the present option to immediate, the WM_PAINT message is received right after calling InvalidateRect()
D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
So I was too quick to ask this question, because having a refresh rate more than the refresh rate of the monitor doesn't have any effect and what I get from this is that the Direct2D engine is optimizing cpu usage by waiting for the next refresh of the actual screen, which makes good sense.
Trying to draw more than the monitor refresh rate looks like a waste of CPU cycles.
Need to consider implementing a good timing mechanism, because winforms and GDI is most of the time slower than the monitor refresh rate (when you have many sprites and geometries on the screen), and now we are faster than that and should adapt.
InvalidateRect(hwnd,.. itself fires WM_PAINT message on a window with hwnd handle. You've just created a circular reference (race).
I understand double buffering a panel basically means all the drawing is done to a buffer and then the buffer is copied directly to the drawing surface.
I am drawing to a Bitmap, and then drawing that Bitmap to my panel, using Graphics.DrawImage, so that I have the Bitmap saved and can make updates to it without having to re-do all the draw operations.
Is this the same as Double Buffering the panel, as far as the flickering of the panel is concerned?
Or, should I be double buffering and re-doing all my drawing, if that means I will get lower flicker?
Edit:
I am plotting points with DrawLine, the points are generated dynamically and I don't store a point once I am done plotting it. If I do Double Buffer, I will also have to incur the memory overhead of storing all the points I plot.
The optimal solution will mostly depend on what you draw, how many operations, how many pixels involved and the total size of the drawing surface.
One common example is a drawing program, where the user piles strokes upon strokes, each consisting of hundreds of points..
In general it is recommended to let the system take care of double-buffering the control you draw on. It will do a better job than you can hope for..
So you should re-draw in the Paint event and not try to implement your own buffered bitmap drawing in order to get rid of flicker.
The flicker will go away but with a huge number of drawing operations this will be slow and cause lags.
To avoid lags you can combine the best of both methods:
draw the graphics until they get too many; it depends on the drawing calls (basically the number of pixels involved) and the speed of your system whether you can afford a few hundred or tens of thousands of drawing calls before you notice lags, say after N calls.
Then cache the first N drawing calls by drawing into a bitmap which you then set as the Panel's BackgroundImage. All drawing from N+1 will still be drawn onto the panel's surface in the Paint event. When you reach 2*N you create another version of the caching image, start surface drawing at 2*N+1 and so forth..
Double buffer should work a bit differently. Lets say while you modify buffer A, it should draw using buffer B; then when you start modifying buffer B, it should read from A.
So, the idea is to not write the buffer being read. Therefore, using external buffer and copying it to drawing buffer doesnt seem to be same as double buffer. Actually, it is possible to write buffer by copying external one while it is being read.
I have WinForms application. I made an user control, which draws a map from coordinates of ca 10k lines. Actualy, not all lines are straight ones, but when the map is zoomed out fully - Bezier curves are irrelevant and are replaced with straight lines.
When the map is zoomed, I have smaller number of lines and curves, so the drawing is fast enough (below 15ms). But when it's zoomed out fully - I need to draw all lines (because all fit into viewport). This is painfully slow. On my very fast machine it takes about 1000ms, so on slower machines it would be an overkill.
Is there a simple way to speed up the drawing?
I use Graphics object for drawing and I set Graphics.Scale property to my map fit into my control.
Does this slow things down?
I use Graphics.TranslateTransform() to ensure the whole map is visible.
Both scale and translate is set only once in OnPaint() event handler.
Then there is a loop which draws ca 10k lines. And I just see them drawing on the screen.
Maybe WPF container would help?
Well, I could probably simplify the map to merge some lines, but I wonder if it's worth the effort. It would complicate the code greatly, would introduce much more calculations, use extra memory and I don't know if at the end of the day it would be considerably faster.
BTW, I tested that processing of all lines (converting from one structure to another with some aditional calculations) takes ca 10ms on my machine. So - the drawing alone costs 100x more time.
EDIT:
Now here's the new problem. I've turned double buffering on with:
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
Here's my messy OnPaint() handler:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Splines == null) return;
var pens = new[] {
new Pen(TrackColor),
new Pen(TrackColor),
new Pen(RoadColor),
new Pen(RiverColor),
new Pen(CrossColor)
};
var b = Splines.Bounds;
Graphics g = e.Graphics;
g.PageScale = _CurrentScale;
g.TranslateTransform(-b.Left, -b.Top);
int i = 0;
foreach (var s in Splines) {
g.DrawLine(pens[s.T], s.A, s.D);
if (++i > 100) break;
//if (s.L) g.DrawLine(pens[s.T], s.A, s.D);
//else g.DrawBezier(pens[s.T], s.A, s.B, s.C, s.D);
}
foreach (var p in pens) p.Dispose();
}
Take my word the code works, if I only remove OptimizedDoubleBuffer from styles. When double buffering is on the handler executes properly, each DrawLine is executed with correct params. But the graphics is not displayed. CPU usage during resizing is next to zero. Like all DrawLine calls were ignored. What's happening here?
In a related post I've seen recently but can't find, the OP claimed to have seen a large speed-up when switching his control to use double-buffering. Apparently there's a substantial hit for drawing stuff to the screen.
Another thing you could try is decimating the point lists in the lines you draw when zoomed out. Instead of doing the decimation each frame, you could do it only once each time the zoom is changed.
Try double buffering as a possible solution or try to reduce the number of lines. Only testing will give you an answer for your application.
Winforms Double Buffering
Double buffering with Panel
The feasibility of this really depends on if you're using anti-aliasing, if the thing can rotate, if the thickness has to be very accurate, etc.
However you can always draw all the lines into a bitmap, then simply redraw the bitmap unless the map data itself has actually changed. Of course then you get into having different bitmaps for different zoom levels, hiding and showing them, multiple bitmaps in a grid for the high details etc.
It's definitely not ideal, but if you really do need to draw thousands of lines on a 20ms refresh though.. it might be your only real option.
Or you could use lower level of drawing, outside GDI+. one such example is SlimDX. This wrapper allows you to create a directX device write from your windows controls and forms. Once DirectX is in action, the speed can increase up to several times.
2ndly, when drawing on win panel even with DoubleBuffered enabled, you always have to Invalidate the panel which asks the Environment to call the OnPaint event which actual draws using the system provided Graphics object. This invalidation usually requires a timer with fire rate more than 30 to five you a feeling of smooth playback. Now, when the load increases, the subsequent timer event is delayed since everything is happening under a single thread. And the timer must Yield the thread for around 25ms after every fire (windows OS limitation). Cross Thread access ia not allowed, using which a System.Threading.Timer could have prevent this jitter.
See this link for an example where I have tried to transfer my existing GDI code to DirectX. The code uses a lot of graphics attributes which i have incorporated in the wrapper which can draw on both GDI and DirectX.
https://drive.google.com/file/d/1DsoQl62x2YeZIKFxf252OTH4HCyEorsO/view?usp=drivesdk
I'm currently working on a game and I wish to have a main menu with background image.
However, I find the method Graphics.DrawImage() really slow. I have made some measurement. Let's assume that MenuBackground is my resource image with resolution 800 x 1200 pixels. I will draw it onto another 800 x 1200 bitmap (I render everything to a buffer bitmap first, then I scale it and finally draw it onto screen - that's how I deal with the possibility of multiple players' resolutions. But it shouldn't affect it in any way, see the next paragraph).
So I've measured the following code:
Stopwatch SW = new Stopwatch();
SW.Start();
// First let's render background image into original-sized bitmap:
OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground,
new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight));
SW.Stop();
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds");
The result is quiet surprising to me - the Stopwatch measures something between 40 - 50 milliseconds. And because the background image is not the only thing to be drawn, the whole menu takes about over 100 ms to display, which implicates observable lag.
I have tried to draw it to Graphics object given by Paint event, but the result was 30 - 40 milliseconds - not much changed.
So, does it mean, that Graphics.DrawImage() is unusable for drawing bigger images? If so, what should I do to improve the performance of my game?
Yes, it is too slow.
I ran into this problem several years ago while developing Paint.NET (right from the start, actually, and it was rather frustrating!). Rendering performance was abysmal, as it was always proportional to the size of the bitmap and not the size of the area that it was told to redraw. That is, framerate went down as the size of the bitmap went up, and framerate never went up as the size of the invalid/redraw area went down when implementing OnPaint() and calling Graphics.DrawImage(). A small bitmap, say 800x600, always worked fine, but larger images (e.g. 2400x1800) were very slow. (You can assume, for the preceding paragraph anyway, that nothing extra was going on, such as scaling with some expensive Bicubic filter, which would have adversely affected performance.)
It is possible to force WinForms into using GDI instead of GDI+ and avoid even the creation of a Graphics object behind the scenes, at which point you can layer another rendering toolkit on top of that (e.g. Direct2D). However, it's not simple. I do this in Paint.NET, and you can see what's required by using something like Reflector on the class called GdiPaintControl in the SystemLayer DLL, but for what you're doing I'd consider it a last resort.
However, the bitmap size you're using (800x1200) should still work OK enough in GDI+ without having to resort to advanced interop, unless you're targeting something as low as a 300MHz Pentium II. Here are some tips that might help out:
If you are using an opaque bitmap (no alpha/transparency) in the call to Graphics.DrawImage(), and especially if it's a 32-bit bitmap with an alpha channel (but you know it's opaque, or you don't care), then set Graphics.CompositingMode to CompositingMode.SourceCopy before calling DrawImage() (be sure to set it back to the original value after, otherwise regular drawing primitives will look very ugly). This skips a lot of extra blending math per-pixel.
Make sure Graphics.InterpolationMode isn't set to something like InterpolationMode.HighQualityBicubic. Using NearestNeighbor will be the fastest, although if there's any stretching it may not look very good (unless it's stretching by exactly 2x, 3x, 4x, etc.) Bilinear is usually a good compromise. You should never use anything but NearestNeighbor if the bitmap size matches the area you're drawing to, in pixels.
Always draw into the Graphics object given to you in OnPaint().
Always do your drawing in OnPaint. If you need to redraw an area, call Invalidate(). If you need the drawing to happen right now, call Update() after Invalidate(). This is a reasonable approach since WM_PAINT messages (which results in a call to OnPaint()) are "low priority" messages. Any other processing by the window manager will be done first, and thus you could end up with lots of frame skipping and hitching otherwise.
Using a System.Windows.Forms.Timer as a framerate/tick timer won't work very well. These are implemented using Win32's SetTimer and result in WM_TIMER messages which then result in the Timer.Tick event being raised, and WM_TIMER is another low priority message which is sent only when the message queue is empty. You're better off using System.Threading.Timer and then using Control.Invoke() (to make sure you're on the right thread!) and calling Control.Update().
In general, do not use Control.CreateGraphics(). (corollary to 'always draw in OnPaint()' and 'always use the Graphics given to you by OnPaint()')
I recommend not using the Paint event handler. Instead, implement OnPaint() in the class you're writing which should be derived from Control. Deriving from another class, e.g. PictureBox or UserControl, will either not add any value for you or will add additional overhead. (BTW PictureBox is often misunderstood. You will probably almost never want to use it.)
Hope that helps.
Although this is an ancient question and WinForms is an ancient Framework, I would like to share what I have just discovered by accident: drawing a Bitmap into a BufferedGraphics and rendering it afterwards to the graphics context provided by OnPaint is way faster than drawing the Bitmap directly to OnPaint's graphics context - at least on my Windows 10 machine.
That's surprising because intuitively I had assumed that it would be slightly slower to copy data twice (and so I thought that this is usually only justified when one wants to do double-buffering manually). But obviously there is something more sophisticated going on with the BufferedGraphics object.
So create a BufferedGraphics in the constructor of the Control that shall host the Bitmap (in my case I wanted to draw a fullscreen bitmap 1920x1080):
using (Graphics graphics = CreateGraphics())
{
graphicsBuffer = BufferedGraphicsManager.Current.Allocate(graphics, new Rectangle(0,0,Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height));
}
and use it in OnPaint (while voiding OnPaintBackground)
protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = graphicsBuffer.Graphics;
g.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);
graphicsBuffer.Render(e.Graphics);
}
instead of naively defining
protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);
}
See the following screenshots for a comparison of the resulting MouseMove event frequency (I am implementing a very simple bitmap sketching control). At the top is the version where the Bitmap is drawn directly, at the bottom BufferedGraphics is used. I moved the mouse at about the same speed in both cases.
GDI+ is probably not the best choice for games. DirectX/XNA or OpenGL should be preferred as they utilize whatever graphics acceleration is possible and are very fast.
GDI+ is not a speed demon by any means. Any serious image manipulation usually has to go into the native side of things (pInvoke calls and/or manipulation via a pointer obtained by calling LockBits.)
Have you looked into XNA/DirectX/OpenGL?. These are frameworks designed for game development and will be orders of magnitude more efficient and flexible than using a UI framework like WinForms or WPF. The libraries all offer C# bindings.
You could pInvoke into native code, using functions like BitBlt, but there is overhead associated with crossing the managed code boundary as well.
I'm working on a real-time WPF/Silverlight (and soon WP7) visualization component and I'm looking for the best solution to force a redraw of the entire component in a Game-loop style. Redraw should be on-demand, but I don't want to back up the message pump with re-draw calls. Most of the drawing in my component is done using non-WPF primitives (e.g. Bitmap Interop, Direct2D) so my code does not use InvalidateVisual, and as a result, currently looks like this
// Pseudocode, doesnt compile, just to convey the meaning
public void InvalidateElement()
{
if (CurrentlyDrawing)
return;
Dispatcher.BeginInvoke(() =>
{
CurrentlyDrawing = true;
DoDrawInternal();
CurrentlyDrawing = false;
}
}
Ok so this is great. If I call InvalidateElement lots of times I get good responsiveness. However, what I want to do is ensure I can push data to my visualization component as fast as possible but only draw when the component is able to draw, and not keep drawing to catch up with the data once the input stream completes.
No I can't override OnRender, I'm using non-WPF drawing inside WPF ;-)
Basically what I want is something like the old Invalidate() / OnPaint in WindowsForms, or better yet, a game loop in DirectX.
At the moment I get the situation where if I have an external thread that pushes data to the visualization component at a high rate then if I Stop pushing data I get another 20 seconds worth of refreshes to get through before the component stops drawing. I want to stop drawing as soon as data has gone in.
Another idea I had was to handle CompositionTarget.Rendering in the visualization component then implement some sort of rudimentary Queue to push data to and the Rendering event consumes this data as fast as it can.
In Summary
Given a WPF visualization component, V, and a datasource which pushes it data every 1ms, D, how can I ensure that no matter the datarate of D, V draws data at 30FPS (or whatever it can do) and updates itself in chunks, sort of how a game render loop does in DirectX?
When the data stops, V should redraw everything it has up to now in one go. When the data is too fast, V draws larger chunks at a time to compensate.
If you need more information I'd be happy to share it. Right now I've just posted a synopsis to gauge if there are any quick fixes but a fuller Q with code examples can be provided on request.
Best regards,
You might want to consider rendering on the CompositionTarget.Rendering event and throttling on the invalidated state.
Silverlight game loop example (F#):
/// Run game
let runGame () =
let state = gameState.GetEnumerator()
let rate = TimeSpan.FromSeconds(1.0/50.0)
let lastUpdate = ref DateTime.Now
let residual = ref (TimeSpan())
CompositionTarget.Rendering.Add (fun x ->
let now = DateTime.Now
residual := !residual + (now - !lastUpdate)
while !residual > rate do
state.MoveNext() |> ignore
residual := !residual - rate
lastUpdate := now
)
Play the game: http://trelford.com/blog/post/LightCycles.aspx
Read the source: https://bitbucket.org/ptrelford/lightcycles
You can listen to the CompositionTarget.Rendering event, which is triggered right before WPF renders the UI, and do your drawing in there.
Another tidbit.. InvalidateVisuals() is nothing like Form.Invalidate(), as it also causes re-layout which is expensive. If you want something like Form.Invalidate(), then create a DrawingGroup (or bitmap image) "backingStore", place it in the DrawingContext during OnRender(), and then update it whenever you want. WPF will automatically update and repaint the UI.
Have you thought of using a dispatch timer running at 30FPS, then take a snapshot of the current data and rendering it at each timer tick? If you want to avoid redrawing if nothing has changed, you can simply keep timestamps for LastChanged and LastRendered, only performing an actual redraw if LastChanged > LastRendered. Basically updating the data and rendering the data are decoupled from one-another; the main trick is making sure you can somehow get a coherent snapshot of the data when the rendering thread wants to render it (i.e. you'll need some sort of locking.)
I was recently working with a project that required a game loop like style. Although my example is purely in F#, you can figure it out how you can do that way in C# too, may be use some interop code to initialize the timer and hooking up events as given in this below link,
http://dl.dropbox.com/u/23500975/Demos/loopstate.zip
The sample doesn't show how to redraw, it just updates the underlying stock data for every 500ms, It should pretty much work for any kind of drawing mechanisms with WPF. The core idea is to use composable events, in F# an event is a first-class citizen + an IObservable (reactive extensions for C#), so we can easily compose functions that in-turn return a set of events or a single event. There is a function Observable.await, which takes in an Observable and also has a state to return.
eventSource
|> Observable.await(fun (t:State.t) e ->
// return the modified state back on every check or in the end
match e with
// start button click
| Choice1Of3(_) ->
{t with start=true}
// stop button click
| Choice2Of3(_) ->
{t with start=false}
// timer tick event,
| Choice3Of3(_) ->
if t.start = true then
handleStockUpdate(t)
else t
) (state)
I just used some of FP terms here, but it should work just fine with normal C# (OO) way of doing things here.
Hope this helps!
-Fahad
I'm not sure why you would use WPF for your front-end if you're drawing using non-WPF elements and require the Invalidate() method that was provided by WinForms? Can't you just switch the UI to use WinForms?