Paged data in a WPF Grid control - wpf

I have a Rest service that returns the data in pages. I know how many pages of data there is after getting the first result set. Now I want to consume this service in a WPF application, e.g. display the result in a Grid Control (or a list view).
The problem is that the whole paging mechanism should be transparent to the end user, so they shouldn't trigger data fetching by any means other than scrolling in the grid. Is this possible and how would you tackle this problem?

Here is another possible solution of your task: http://www.devzest.com/blog/post/wpf-data-virtualization.aspx
Main idea is to create your own implementation of IList which will encapsulate all async page loading features.
As a bonus that article contains full example code with a set of additional features:
Selection, sorting and filtering works well as if all data are stored locally;
Data loading as needed, in a separate thread, without blocking the UI;
Visual feedback when data is loading; if failed, user can retry the last failed attempt.

Put your grid or list into the ScrollViewer, subscribe to the ScrollChanged event, then use event args properties to determine if you are close enough to the end of scrollable area, and request next page from your service, and finally add received data to the end of list or grid.
That's in short. If you need more concrete example, let me know.
EDIT: Okay, assuming you're using the System.Windows.Controls.DataGrid control to display your data. I'm making this assumption because you've said your grid does have the scrolling capabilities built in, and no other control with name similar to grid does have it. And also because it makes sense to use DataGrid for data display. :)
You declare your DataGrid like this:
<DataGrid HorizontalAlignment="Left" Margin="20,10,0,0"
VerticalAlignment="Top" Height="301" Width="498"
ScrollViewer.ScrollChanged="DataGrid_ScrollChanged_1"
ItemsSource="{x:Static Fonts.SystemFontFamilies}">
</DataGrid>
Notice that I'm using ScrollViewer.ScrollChanged routed event. This is possible because DataGrid indeed has the ScrollViewer built in. This means it's possible to subscribe to that event and analyze it's arguments.
Here's how I handle this event for testing purposes:
private void DataGrid_ScrollChanged_1(object sender, ScrollChangedEventArgs e)
{
Debug.WriteLine("Extent height: " + e.ExtentHeight +
", vertical offset: " + e.VerticalOffset +
", viewport height: " + e.ViewportHeight);
}
When my datagrid is scrolled to the top, I see the following output:
Extent height: 267, vertical offset: 0, viewport height: 13
When it's scrolled to the bottom:
Extent height: 267, vertical offset: 254, viewport height: 13
So it's quite easy to determine when you're close to the bottom and act accordingly:
const int threshold = 20;
if (e.ExtentHeight <= e.VerticalOffset + e.ViewportHeight + threshold)
{
AskForNextPage();
}
Of course, there are some nuances here. You need to keep track if you've already downloading some page, and how many pages you've already loaded, to avoid data duplication and other inconsistencies. And, honestly speaking, this will be the hardest part to do, compared to what I've written here. :)

I thought the problem was interesting but the answer is too long for stackoverflow window so I built a simple app that uses a prefetching collection view.
It's sort of similar approach as the one posted by Woodman.
https://github.com/mrange/CodeStack/tree/master/q14793759/AutoFetching
The interesting code is in the class: PrefetchingCollectionView

Related

Automatically Scrolling Grid Inside ScrollViewer Results in Flickering/Pulsing Effect

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.

How to make UIScrollView load data as scrolling happens

I have an IOS application designed for ipad. In one page, I display a report to the user and the report requires 5000 UILabels to be rendered on the screen inside a UIScrollView. This causes application to crash due to being out of memory. I know that UITableView has the functionality for loading content on demand. How can I make scroll view render only certain content and as the scrolling happens remove the content that got invisible and add the content that should be visible?
Is there any way you can reconfigure it to use a table view with custom rows, perhaps rows that consist of several labels? That model supports load on demand and resource reuse in a very natural way...
Perhaps you can be more specific on your screen layout?
You should re-cycle (i.e., reuse) the labels just like a table view recycles its table cells. This is sometimes referred to as "tiling" subviews. Tiling allows you to display more than will fit into memory.
I recommend watching WWDC 2012 Session 104. This session's tutorial creates a photo app that tiles image views in a UIScrollView. Although the photo app scrolls pages of content, rather than a grid of items, I think the video could be relevant to your app.
Here's a very brief overview of the tutorial applied to your specific case:
(1.) declare iVars that keep track of your labels:
NSMutableSet *recycledLabels;
NSMutableSet *visibleLabels;
(2.) implement a method that fetches a re-usable label:
- (UILabel *)dequeueRecycledLabel;
(3.) implement a method that does the tiling:
- (void)tileLabels; // this will add/remove labels from the scroll view
(4.) set your scroll view's delegate and call tileLabels in the scrollViewDidScroll: delegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self tileLabels];
}
The WWDC video will help you fill in the details, which I think are applicable to your case. However, you will have to do plenty of improvising in order to make it work for your specific case. This may not be easy; but its doable.
At the end of the video, tiling with CATileLayer is discussed. I'll be honest, I didn't understand that part. But I don't think it's applicable in your case because you're not displaying large images.
I finally implemented dynamic content loading. The method I implemented is scrollViewDidScroll.
Inside this method I determined the direction of scroll by the following
bool isUp = (currentOffset.y > lastScrollOffset);
Then I determined the visible content rectangle by the following.
CGRect visibleContentRect;
visibleContentRect.origin = scrollView.contentOffset;
visibleContentRect.size = scrollView.bounds.size;
Then I had an array of View Elements and each knew its place in the scroll view because of their frame being set. Long story short, each time scroll happened, I determined the views whose frame either intersected or contained by visible content frame. I added those views to the scroll view. I also determined the ones that disappeared and removed them from scroll view and I also set those views to nil and recreated them. Once [scrollView addSubview:view] method is called, then view gets more space in the memory because it gets visible. [view removeFromSuperView] method doesn't deallocate that space. That's why setting the view to nil and recreating it is necessary.

Silverlight (wp7)

Being new to silverlight I'm struggling to 'get going' with the following.
Basically I wish to create some form of grid like control (custom or user?).
The idea is similar to that of a planner. Along the top are times (set intervals). Downwards are subjects. Then over the grid like background rectangles (or something) indicate when the subject is planned for.
The actual design of the above is not the issue. i.e. a grid with ractangles overlaid. But my issue is I wish this grid to be scrolled up and down (with bounds fixing the top and bottom when the subject lines start and end). And also the grid to be scrolled left and right (with bounds fixing how far left and right it can scroll, current time & 3 days into future).
Based on the above needs, I don't wish to create a control which is very large, and just dragged into view (unless this is the only way?) but instead show the grid at a current time and when dragged dynamically load the next few hours worth of content, possibly with a few hours buffer.
The appearance I am seeking is it looking like it is one massive control, but truely its not, its dynamic.
Does this make sense? Am I worrying about nothing? Should I create a massive grid well into the future and then just handle the load of data dynamically over the top? Its just my concern if I want a grid 3 month into the future this would be massive and a waste of memory.
I'm struggling to find examples on the net, but feel this maybe to do with me not knowing what to search for. This isn't about getting a detailed answer and someone doing it for me, but instead about guidance pointing me in the right direction.
Many thanks
About the up-down scroll: you can simply put a grid containing your data in a ScrollViewer control - this will handle all the scrolling for you. Another solution would be using a listbox control - this is better if you use MVVM. You can bind it to a data source and set as data template a custom control.
For the left-right scroll. I'm thinking you could use gestures for this. Like - catch left-to-right and right-to-left flicks and change the data in your grid / listbox according to the gesture's direction. You could also place two buttons at the top of the grid to handle scrolling from one day to the other (just like in the calendar controls: gestures + buttons).

Winforms Usercontrol: Perfomance issues when creating and adding to parent

I have built a UserControl for display a list of other UserControls which themselves are bound to individual data objects. The link below shows an example implementation of this control.
Each of the individual user rows is its own UserControl/XtraUserControl, laid out in a FlowLayoutPanel.
The problem I have is with perfomance, to populate the list above takes around 500ms (excluding any data loading) - this is a combination of creating each control and then adding them to the FlowLayoutPanel using the AddRange(controls[]) method.
Does anyone know any way I can improve perfomance here? Do I have to manually paint the items instead of using User Controls?
Thanks in advance.
EDIT: I've added my own response below showing the solution I have stuck with for now.
Whether manually painting would help is a guess. Even if it were right (which I doubt) it's better not to guess.
I've seen this kind of issue before, and chances are there's a lot of stuff that goes on in the binding.
The way I've solved the problem is with this approach, but it's definitely "out there" in terms of programmer acceptance.
I gues you are using devexpress controls because you mention XtraUserControl. If so, why don't use an XtraGrid?You can add images column and button columns, and I think you'll get better performance and simpler/less code
First of all, try use pair SuspendLayout()/ResumeLayout(), then it has sense to stop painting at all by hiding the container control until all child usercontrols added.
Anyway, placing lots of child controls to a container is not a good idea.
You can have the same result by using highly customized grid or by custom painting (which is preferable).
Good luck!
I had a brainwave for another solution which I'm not quite sure is appropriate. I would really appreciate any feedback on this.
Two rationales led to this solution:
Firstly I wanted the flexibility of creating rows like any other control.
Secondly the lists that would use this approach only intend to display brief chunks of data, never more than say 20 items - for anything larger, ListViews are used.
So anyway, what I decided to do was cache a set number of the Panels (I've referred to the custom controls or rows as Panels throughout the code) and to build up this cache as the control is created. When populating the control with BusinessObjects, the existing cached Panels are displayed with their bound BusinessObject. You can see how this works exactly from the code below, so there is no need for a in-depth description.
The fact of the matter is that I've managed to reduce the data population time (after the initial cache setup of around 180ms for 10 Panels) from 500ms to 150ms for the list shown in the image above.
private int cacheSize = 10;
private List<P> cachedPanels = new List<P>(10);
private void InitItems()
{
this.contentPanel.SuspendLayout();
// Create the cached panels from the default cache value.
for (int i = 0; i < cacheSize; i++)
cachedPanels.Add(new P() { Margin = new Padding(0), Visible = false });
this.contentPanel.Controls.AddRange(cachedPanels.ToArray());
this.contentPanel.ResumeLayout(true);
}
private void PopulateListFromCache()
{
this.contentPanel.SuspendLayout();
// Iterate against both BusinessObjects and Panels to ensure that nothing is missed, for
// instance, where there are too many panels, the rest are hidden, and too many Business
// Objects, then more Panels are created.
for (int i = 0; i < this.businessObjects.Count || i < this.cachedPanels.Count; i++)
{
if (i >= this.cachedPanels.Count)
{
// Here, we have more BusinessObjects than Panels, thus we must create
// and assign a new panel.
this.cachedPanels.Add(new P() { Margin = new Padding(0) });
this.cachedPanels[i].Item = this.businessObjects[i];
this.contentPanel.Controls.Add(this.cachedPanels[i]);
}
else if (i >= this.businessObjects.Count)
{
// Here, we still have Panels cached but have run out of BusinessObjects,
// let's just hide them and clear their bindings.
this.cachedPanels[i].Item = default(T);
this.cachedPanels[i].Visible = false;
}
else
{
// Here, we have both BusinessObjects and Panels to put them in, so just
// update the binding and ensure the Panel is visible.
this.cachedPanels[i].Item = this.businessObjects[i];
this.cachedPanels[i].Visible = true;
}
}
this.contentPanel.ResumeLayout(true);
}
Obviously, more optimizations can be made, such as un-caching Panels after a certain amount of time of not being used etc. Also, I'm not entirely sure if keeping these controls - which are rather simple - in a cache will affect memory usage much.
If anyone can think of any other pointers then please, be my guest. Oh, and if you got this far, then thank you for reading this.

Get airport display type transition when data changes

A client has asked for a display to flick over like an airport display screen, ie each row flicks over when information changes.
I am not sure which is the best control to use, or the method of getting each row to transform one after the other.
any suggestions woul b gratfully accepted
John
Here's what I would do in general concept..
Make a regular panel of, say 50px high. (This is arbitrary but this panel just holds the size in place so the control doesn't shrink with its contents.)
Create a panel inside that one that will be the 'animated' panel.
When it's time for information to animate, create a storyboard that uses a transformation to "stretch" the height down to 0, change the content to the updated information, then tranform stretch the height back to 50px. This will create the illusion that the panel is flipping over.
If you make this a user control, then you could simply add however many "rows" you needed of this control to a StackPanel to make your screen.
The best way of representing this effect easily is to randomize the text during the change.
Patrick Long implemented this effect as a custom animation here

Resources