Automatically Scrolling Grid Inside ScrollViewer Results in Flickering/Pulsing Effect - wpf

I'm developing a constantly scrolling display of information related to parts coming off a paint line at a manufacturing facility. The information gets regularly refreshed and is displayed to employees on a horizontally oriented 70" LCD monitor (I'm using a standard Vizio 70" 4K LCD TV for my testing). Here is a quick screenshot for reference...
I'm using a WPF form inside VB.net and creating a series of grids with embedded text boxes to make up all various cells you see in the screenshot. This is done at runtime since it needs to be dynamic. In order to get the whole thing to "scroll" automatically the main grid is embedded in a scrollviewer control and I use a timer to increment the the VerticalOffset property of the scrollviewer every X number of milliseconds to scroll through all the information until it reaches the end.
I should mentioned that the entire grid is usually comprised of less than 100 total rows of information.
Everything works fine with this solution except that when the grid is autoscrolling through the rows I end up will this pulsing/flickering effect, it is especially prominent when running the program on the large display. I've read about double buffering but this is already a WPF form so I'm not sure it applies here or if can even be applied to a scrollviewer or grid control. Here is a short video that shows what I'm talking about (note: it pausing the scrolling after every couple of rows is intentional)...
Youtube Video Example
Any ideas on what I can do to minimize this effect when scrolling?
Right now I'm setting up a timer to run every 30 milliseconds...
Dim scrollTimer As DispatcherTimer = New DispatcherTimer()
AddHandler scrollTimer.Tick, AddressOf scrollTimer_Tick
scrollTimer.Interval = New TimeSpan(0, 0, 0, 0, 30)
scrollTimer.Start()
And then scrolling the vertical offset by 1...
scrlPaintLineStatus.ScrollToVerticalOffset(scrlPaintLineStatus.VerticalOffset + 1)
Thanks in advance.

Related

How can I show a wait cursor during whole WPF render cycle?

I wrote a project that's creating a hierarchy diagram using nested controls.
When run on a complex data source, having WPF paint the diagram comprising approx. 2,000 nested controls takes quite a while.
A property change, like border stroke width, may trigger the whole render cycle, incl. Measure, Arrange etc.
I'd like to show a wait cursor for the whole render cycle, including Measure, Arrange and everything else happening before WPF presents the final result on screen. Is there a set of events I can register with to set the mouse cursor appropriately?

How to Set the Background of WPF Control and Ensure Rendering Immediately When Visible?

I have a WPF application that takes a screenshot of the contents of a WindowsFormsHost control and applies it to the background of a Grid control. The Grid acts as a transition screen from the WindowsFormsHost to other WPF controls. This is done to make a smooth transition effect and to avoid air space issues. I first capture the WinFormsHost control image as a bitmap and apply it to the Grid background. Then programmatically change the visibility of the transition Grid to visible. Then do an opacity animation to smoothly show another control. It works perfectly about 70 to 90 percent of the time depending on what computer I test the application on. The problem is that the transition Grid background is not being rendered fast enough or at the correct time. So that occasionally I am seeing a screenshot from a previous transition which does not match the current screen image at transition time.
If I could somehow ensure that the transition image was drawn before the Grid is made visible, it would work everytime. I just cannot see how to do this. The application seems to always wait until the last minute to do any rendering.
I have tried to force rendering with Dispatcher.Invoke() and Dispatcher.BeginInvoke() methods. I have also tried to delay the time when the Grid is made visible with a Dispatcher Timer, but no matter how much time there is between the call setting the background image and setting the visibility, the Grid does not always update. I have also tried things like InvalidateVisual() with no luck.
After looking at many examples into problems involving WPF's rending and UI threads, I made several attempts to correct the problem...
Attempt 1 - Works poorly, but attempt works better with a line of code that first sets background to nothing or black:
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
State = "WPFControlMode"
SwitchState()
Attempt 2 - Best results so far. Works fair to well depending on computer. Increasing the counter value does not improve results. The appearance of the TransitionScreen will be delayed with a larger counter value, but will have the same chance of showing a previous screenshot (about 1 in 5 times on my laptop):
Case "Transition"
'Grab Screen Shot and apply to transition screen...'
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
End Select
'The following code is inside a loop...'
If State = "Transition" Then
TransitionCounter += 1
If TransitionCounter = 25 Then
TransitionCounter = 0
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
State = "WPFControlMode"
SwitchState()
End If
End If
Attempt 3, 4, 5, ... - Attempts try to force rendering of control by working through Dispatcher. These attempts do not appear to help and often make the problem worse. They have been used in combination with both attempts 1 and 2.
Case "Transition"
'Grab Screen Shot and apply to transition screen...'
TransitionScreen.Visibility = Visibility.Visible
TransitionScreen.Background = Nothing
TransitionScreen.Background = New ImageBrush(BitmapToImageSource(GrabScreenshot(ScreenWidth, ScreenHeight)))
FlushWindowsMessageQueue()
End Select
'Tried many variations of the following procedure. Every dispatcher priority with both BeginInvoke and Invoke methods, as well as calling the methods through Application and TransitionScreen objects.'
Private Sub FlushWindowsMessageQueue()
Application.Current.Dispatcher.Invoke( _
New Action(AddressOf DummySub), _
DispatcherPriority.Background, _
New Object() {})
End Sub
Private Sub DummySub()
End Sub
There were more attempts also, but this should give you an idea of what I have tried. This is a real puzzle. This is the last major problem with a fairly involved project, and I am pretty much invested in the transition screen at this point. If you can think of anything, I will greatly appreciate it. Thanks.
I did come up with a work around for this problem. I do a transition animation before I take the screenshot. The transition animation results in a lower resolution image than the control would normally show. Then I take the screenshot of the lower resolution image which can be applied to the transition grid control, after it is made visible, fast enough that there is never a flicker. Then when I want to return to the WinFormsHost control, I run through this process in reverse. My application now works without flicker and run smoothly on most computers I have tried. On some computers with large monitors, there is sometimes a slight delay when rendering the transition screen, but still no flicker. The delay is about half a second, and I assume it has to do with the larger area that transition image must be rendered to.
Also, the above was done in combination with method/attempt #2. The flicker still occurs when in combination with attempt #1.

Jerky animation when scrolling image in WPF using SharpDX

I am trying to smoothly scroll some images across a window using DirectX11 via SharpDX in a WPF application.
A bit of background:
The images are signal returns, varying by time, that have been loaded from a file and loaded into D3D as a texture array, and the image itself is rendered as a series of textured quads (a series of adjoining rectangles in a very long horizontal line).
The viewport is set up so that the left and right edges represent time offsets, and the signal is displayed for the time period that falls between these times.
The DirectX implementation is very simple; the quad vertices are generated once, and a very small per-frame buffer contains a simple 2D world-view transform that updates scale and translation according to the current visible range/zoom etc.
As far as I can tell, the D3D implementation is not part of the problem I am having - it really is a very simple set up, and seems to be rendering extremely quickly (as it should), and although I do have some fancier stuff (streaming of textures from disk as required), I have disabled all of these and am running with very simple (small) textures whilst I try and resolve the issue I am having..
The problem:
Scrolling of the image is not smooth when the visible time range is animated. The image "jitters" much of the time, and frankly looks awful (when it is not jittering, it looks great).
The setup:
DirectX is rendered to a D3DImage, with a bit of work going on behind the scenes to make DX11 work - this code is taken from https://sharpdxwpf.codeplex.com/
There are multiple D3DImages (up to a total of 4), arranged in a grid (I have been testing with two, both of which contain signals for the same time period and are animated together).
These D3DImages are drawn by a DrawingVisual (which is hosted by a custom FrameworkElement) where they are used as the source of an ImageBrush. The frameworkelement object triggers a render as required, and the drawing visual handles the D3D render call and draws a rectangle to fill the control using the D3DImage brush.
The time range value is animated using a WPF DoubleAnimation. The visuals that are currently displayed are bound to this value via INotifyPropertyChanged and trigger a render (via InvalidateVisual) on each change.
DrawingVisual render code, triggered by change of "Position" value (start of visible time range):
// update scene per-frame buffer first, with world-view transform
using (DrawingContext dc = RenderOpen()
{
_scene.Renderer.Render(_draw_args);
_image.Invalidate();
dc.DrawRectangle(_brush, null, new Rect(viewer.RenderSize));
}
What I have tried:
I have tried a number of things (and searched a lot) to try and determine whether the issue is due to jittery render request timing, or if the issue is further into the render process.
Hooking into CompositionTarget.Rendering
Firstly driving the update of the position value via CompositionTarget.Rendering, and leaving the rest of the process as-is (i.e. elements react to the change of this value):
(needless to say, this is very much "test" code):
Stopwatch rsw;
long last_time = 0;
void play()
{
rsw = new Stopwatch();
last_time = 0;
rsw.Start();
CompositionTarget.Rendering += rendering;
}
void rendering(object sender, EventArgs e)
{
double update_distance = rsw.ElapsedMilliseconds - last_time;
Position += 10 * update_distance;
last_time = rsw.ElapsedMilliseconds;
}
Results - worse than the simple double animation.
Using a DispatcherTimer. Similar to above, using a timer in conjunction with a stopwatch to judge the elapsed time a little better. Worse results again, with the interesting side note that CPU usage dropped from about 7% (half a core) to 0.7%.
The original attempt, along with variants 1 & 2, all try to update a single value that then triggers the render of interested parties. Out of interest, I logged the time differences between render requests as they reached the Drawing visual - although the DisptacherTimer actually gave the most consistent results there (both WPF animation and CompositionTarget dropped the odd frame), the DisptacherTimer also gave the most jittery animation.
Updating the image, but not invalidating the visual. Updating the source of an ImageBrush updates the displayed image, without having to re-render the DrawingVisual. This method produced very jittery results.
CompositionTarget.Rendering in the framework element itself. This produced the best results of the lot, but still not perfect. Jitter would happen, then dissipate, only to return again. In this approach, the framework element that holds the DV hooks up to CompositionTarget.Rendering, and the visual queries the current position, which is being animated independently. I could almost live with this approach.
Attempting the wait until the D3D scene is rendered, before invalidating the image (no discernible improvement):
_scene.Renderer.Render(_draw_args);
_scene.Renderer.Device.ImmediateContext.End(q);
while (!(_scene.Renderer.Device.ImmediateContext.IsDataAvailable(q)))
Thread.Yield();
_image.Invalidate();
Observations:
I really don't think this is a performance issue as such. My dev machine has a good graphics card, 8 i7 cores etc, and this is a simple rendering operation.
This really seems like some sort of synchronisation issue between D3D and the WPF rendering, but I have no idea how to begin looking into this.
If I have two images animating in parallel, the jitter is much more pronounced on the first of the two (usually).
If I actively resize the window whilst animating, animation is perfectly smooth (despite the fact that a lot of extra work is being done, as the D3D context is being resized constantly).
EDIT
I've taken things back as far as possible to try and isolate the problem, and the issue seems to be fundamental to the way in which D3DImage is updated & rendered by WPF.
I have modified the WPFHost example in the SharpDX Samples solution so that the simple trigangle that is displayed is animated across the screen. This example is hosted in a DX10ImageSource that is rendered by a DPFCanvas on CompositionTarget.Rendering.
This example couldn't be more simple, and is about as "close to the metal" as you can get whilst rendering a D3DImage in WPF. A single triangle, translated across the screen by a value calculated from the time difference between renders. The stutter remains, coming and going, as if some sort of synchronisation issue. It baffles me, but essentially makes SharpDX unusable within WPF for any sort of smooth animations, which is extremely disappointing.
If anyone is interested in reproducing this problem, the SharpDX samples are available here: https://github.com/sharpdx/SharpDX-Samples
I made the following simple changes to the WPFHost example:
void IScene.Render()
{
...
EffectMatrixVariable wv = this.SimpleEffect.GetVariableBySemantic("WorldView").AsMatrix();
wv.SetMatrix(this.WorldViewMatrix);
...
}
void IScene.Update(TimeSpan sceneTime)
{
float x = (float)sceneTime.Milliseconds * 0.001f - 0.5f;
WorldViewMatrix = Matrix.Translation(x, 0, 0);
}
and in shader Simple.fx:
float4x4 WorldViewTransform : WorldView;
PS_IN VS( VS_IN input )
{
PS_IN output = (PS_IN)0;
output.pos = mul(input.pos, WorldViewTransform);
output.col = input.col * Overlay;
return output;
}
D3DImage is fundamentally broken.
If you don't need to overlay XAML elements on top of D3D or you do not need to resize too frequently the D3D surface, prefer to host a HWND/WinForm into your WPF apps and render to it directly using regular rendering loop. If you want a bit more details about the reasons, you can check this issue.

WPF DataGrid : CanContentScroll property causing odd behavior

I have a solution where I generate a DataGrid (or multiple instances) based on user criteria. Each grid keeps receiving data as it comes in via an ObservableCollection.
The problem I had was that the scroll acted weird. It was choppy, and scrollbar would resize itself while scrolling.
Then I found the CanContentScroll property! It completely fixes the weird scrolling behavior bringing me temporary bliss and happiness.
However, it causes two unfortunate side effects:
Whenever I re-create DataGrid instances and bind them to my ObservableCollection, it freezes my entire window for 5 seconds. When my DataGrid grows to a big size, this delay can last for 30 seconds.
When I call TradeGrid.ScrollIntoView(TradeGrid.Items(TradeGrid.Items.Count - 1)) to scroll to the bottom, it jumps to bottom and then back to the top.
Is there another way to achieve smooth scrolling perhaps?
You are encountering the differences between physical scrolling and logical scrolling.
As you have discovered, each has its tradeoffs.
Physical scrolling
Physical scrolling (CanContentScroll=false) just goes by pixels, so:
The viewport always represents exactly the same portion of your scroll extent, giving you a smooth scrolling experience, and
but
The entire contents of the DataGrid must have all templates fully applied and be measured and arranged to determine the size of the scrollbar, leading to long delays during loading and high RAM usage, and
It doesn't really scroll items so it doesn't understand ScrollIntoView very well
Logical scrolling
Logical scrolling (CanContentScroll=true) calculates its scroll viewport and extent by items instead of pixels, so:
The viewport may show a different number of items at different times, meaning the number of items in the viewport as compared to the number of items in the extent varies, causing the scrollbar length to change, and
Scrolling moves from one item to the next and never in between, leading to "jerky" scrolling
but
As long as you're using VirtualizingStackPanel under the hood, it only needs to apply templates and measure and arrange the items that are actually visible at the moment, and
ScrollIntoView is much simpler since it just needs to get the right item index into view
Choosing between them
These are the only two kinds of scrolling provided by WPF. You must choose between them based on the above tradeoffs. Generally logical scrolling is best for medium to large datasets, and physical scrolling is best for small ones.
A trick to speed loading during physical scrolling is to make the physical scrolling better is to wrap your items in a custom Decorator that has a fixed size and sets its child's Visibility to Hidden when it is not visible. This prevents the ApplyTemplate, Measure and Arrange from occuring on the descendant controls of that item until you're ready for it to happen.
A trick to make physical scrolling's ScrollIntoView more reliable is to call it twice: Once immediately and once in a dispatcher callback of DispatcherPriority.ApplicationIdle.
Making logical scroll scrollbar more stable
If all your items are the same height, the number of items visible in the viewport at any time will stay the same, causing the scroll thumb size to stay the same (because the ratio with total number if items doesn't change).
It is also possible to modify the behavior of the ScrollBar itself so the thumb is always calculated to be a fixed size. To do this without any hacky code-behind:
Subclass Track to replace the calculation of Thumb position and size in MeasureOverride with your own
Change the ScrollBar template used for the logical-scrolling ScrollBar to use your subclassed Track instead of the regular one
Change the ScrollViewer template to explicitly set your custom ScrollBar template on the logical-scrolling ScrollBar (instead of using the default template)
Change the ListBox template to use explicitly set your custom ScrollViewer template on the ScrollViewer it creates
This means copying a lot of template code fom the built-in WPF templates, so it is not a very elegant solution. But the alternative to this is to use hacky code-behind to wait until all the templates are expanded, then find the ScrollBar and just replace the ScrollBar template with the one that uses your custom Track. This code saves two large templates (ListBox, ScrollViewer) at the cost of some very tricky code.
Using a different Panel would be a much larger amount of work: VirtualizingStackPanel is the only Panel that virtualizes, and only it and StackPanel to logical scrolling. Since you are taking advantage of VirtualizingStackPanel's virtualization abilities you would have to re-implement all of these plus all IScrollInfo info function plus your regular Panel functions. I could do something like that but I would allocate several, perhaps many, days to get it right. I recommend you not try it.
I also have same issue with my DataGrid and finally I did:
ScrollViewer.CanContentScroll="True"
EnableRowVirtualization="True"
VirtualizingPanel.VirtualizationMode="Standard"
Now everything is working fine in my DataGrid.

WPF Custom Draw Multiple Progress Bar

In processing a group of items, I wanted to display a unified image of the status of the group, so I essentially made a Grid of a number of progressbars with transparent backgrounds and various colored foregrounds all at the same cell.
I'm running into some transparency artifacts (purple bar is actually purple under the green, and sometimes it draws over the top, etc) and it just seems a bit wasteful. So, I decided to make my own, but now I've got a bit of paralysis on how to do it. Do I use the DrawingContext in FrameworkElement's OnRender, or is there something simpler? Is there a set of general rules when it comes to making your own control?
I pondered switching to a pie chart since those are easy to come by, but its high time I did something not off-the-shelf.
Thanks!
I'm not quite sure how you intend the progressbar to combine different progresses, but if say the furthest along progress is at the bottom of the z-index and the least along progress is at the top, then I'd do something on the lines of this:
1) I would probably create a user control for this new progresbar.
2) It would have a property called NumberOfProgresses, that is tied with an array containing status of said progresses.
3) Each progress would be represented by a Border item (or perhaps something more suitable up the visual tree), because it's a simple wpf control with a background property. The background property would be set to nice a looking progress style and the progress color can be bound in the style to say the border's borderbrush property. Making it easy to set the color of the progress.
4) The user control would have a method UpdateProgress which takes the percentage value and the index of the progress in the array as parameters.
5) As progresses are updated you can either, just calculate the appropriate width (user control actual width * percentage) for the border and play around with the Z index to get it displayed at the top/bottom, or stack the borders horizontaly, set the least along progress as first, then for the rest of the progresses you'd have to substract previous progresses lengths to get the same effect.
This way there would be no transparency induced artifacts and no OnRender()...
Mind you, in WPF there should be no reason to mess with OnRender this and OnRender that, like it was required in WinForms with OnPaint.
Just set up the elements via code to get the look you want, and let WPF do it's rendering ;)
I can imagine one problem with this user control though. You'd have to provide feedback to the user as to which color belongs to which progress. But that would probably take you back to square one, meaning it's better/simpler to just display multiple progressbars.

Resources