I am trying to do the standard - bind a list of data (including images) received from REST API calls in a very quick and smooth manner - a paradox in itself. I have 2 service calls that each take about 2 seconds to complete so I can async/await those, but based on the data returned, I then build other lists (observableCollection) in memory and bind them back to ListBox's in the page.
Problems:
This actual binding seems to lock up the UI thread, how can I asynchronously load my page - listBox by listBox (or even item by item) in a lazy fashion? I'd like to put a placeholder image in place and when finally bound, the placeholder is replaced by the bound image. Any ideas? Frameworks? Tools?
When binding the actual images, the other data in my DataTemplate, actually jumps around the screen while the Image is rendered. It looks terrible... I'd like to be able to, at the very least, bind the image first and then the other controls in the dataTemplate after? Anything that would make it appear a bit smoother would help.
Thanks in advance.
I suspect your problem in (2) will be solved with the placeholder image (assuming that it is the same size as the downloaded images).
I suspect that your "lock up" problem in (1) is that you are calling Wait or Result on a Task returned by an async method. In many cases, this results in a deadlock, as I explain in a recent MSDN article and on my blog.
I think what you really want is a way to start a Task and get a data-binding notification when it completes. I've developed a set of types (TaskCompletionNotifier) that helps out in this situation. Check out the end of my blog post on async properties for a sample. You may also be interested in my blog post on async constructors.
(1) If the list of items is large, binding them all at once will cause some stalling on the UI thread. One fix is to add the items a few at a time and pause so that the UI thread can get a new frame to the compositor before continuing.
public async void AddObjects(List<object> objects)
{
for(int i = 0; i < objects.Count; i++)
{
_myObservableCollection.Add(objects[i]);
if(i % 10 == 0) await Task.Delay(100);
}
}
(2) You should set a fixed width and height on the images in the DataTemplate, so that it does not change as the image is actually downloaded. Alternately, if you can fetch the width and height from your service in the API calls, bind the image width/height to those values before it gets downloaded.
Related
I'm trying to make an editable control using the HTMLEditor ( I'd like a rich-text pane like Swing's JEditorPane or JTextPane but it seems I need to wait a couple of years for that ). I want to have the user type in text and the control grows to suit. I have tried catching an event when the scroll-bar appears and increasing the size until it disappears but I can't work out how to wait until the JavaFX thread has actually re-sized the control on its parent.
There's probably a better way to do it than that... any ideas? Any ideas how to reduce it if text is removed?
TIA Mike Watts
For the edit control, use a WebView with contenteditable set to true like this example or a customized HTMLEditor.
Interact with it using the Java/JavaScript bridge similar to this editor. You can script the WebView using JQuery. Run a Timeline which polls the edit control's text dimensions using a JQuery dimension query script and adjust the control size appropriately.
[side note: I've added this as an answer even though it is just the details from jewelsea's - I couldn't format the code when replying as a comment].
This is what has worked to a certain extent:
in the html of the WebView component, added a tag <div id='measured'> around all of the text blocks
added a handler to the WebView using setOnKeyPressed and that calls checkHeight()
private int lastOffsetHeight;
private void checkHeight() {
int newHeight = (Integer)webview.getEngine().executeScript(
"document.getElementById(\"measured\").offsetHeight;") + 14;
if (newHeight != lastOffsetHeight) {
lastOffsetHeight = newHeight;
webview.setPrefHeight(newHeight);
}
}
This is not too bad, main problem is that if all of the text is deleted then the WebView component deletes the div. As jewelsea mentioned, JQuery might be a better solution but I'll update this if I ever fix the problem of including the library ;-)
I am new the WP7.1. I am developing chat app, in that there is screen like friends, in that screen it like 1000s of friends are there, We are binding to longlistselector the using sqlite.
Main problem is like, we have one API for calling friend's update like (displayname, profile picture etc) and I am processing the API data and binding the data to longlistselector, at the time of binding, I am not able to scroll the longlistselecor and any other buttons in the screen.
I am binding the data using Deployment.Current.Dispatcher.BeginInvoke(() => { Binding to }); and Dispatcher.BeginInvoke(() => { Binding to }) and BackgroundWorker (I tried those many ways).
Please suggest any solutions binding the without disturbing UI.
Thanks in advance.
-- Chandra
Whenever you push data on the screen (be it through XAML {Binding} or through directly accessing the page's UI elements) it needs to be done on the UI thread. While the UI thread is busy with processing your code or with redrawing the UI because of your code, the UI freezes.
This means that you need to make everything inside BeginInvoke() as short and efficient as possible. However you also need to make as few calls to BeginInvoke() as possible. Finding the balance between these will determine the experienced speed of your UI.
In the case of LongListSelector I found that it's efficient to add 50 items at a time, wait half a second and add the next 50 items. Your mileage may vary.
I am trying to show the progress of my windows form application in a panel within the form. However, all the messages show together at the end of when the application completes executing. Is there a way to display the messages as the code - execution "progress" through these messages - just the way it would in an interpreted language?
PS: I am adding the message as a label control to the panel at different points within the code.
Thank you.
You have to invalidate the label every time you change the message:
label1.Text = "Initializing...";
label1.Refresh();
// Do Stuff
label1.Text = "Working...";
label1.Refresh();
// Do Stuff
label1.Text = "Completed.";
label1.Refresh();
Or, it may be worth using a backgroundWorker (or another thread). Whats happening now is that the main thread (the one that also draws the UI) is too busy doing the processing to update the UI.
I'm not sure what language you're using, but Ill assume C#. In that case, create a BackgroundWorker that reports progress. Call the Background worker asynchronously so that it does the processing and use the Reports_Progress event to set the label. You cant set the label in the main Backgroundworker do_work procedure since the label was created by another thread. See this example, it may help (admittedly he sets a progress-bar value but you can just as easily set label text - http://www.dotnetperls.com/progressbar)
If you dont have the Backgroundworker class, you can implement the same logic, just using a different thread.
If you need some more info, let me know.
I'm finding a common pattern during my WP7 development.
Something takes a long time to display and I want to break down the display into 2 parts - an initial display so I can show a Loading message and start the progress bar then a secondary display where I can load the data.
At the moment I'm trying to do this in a custom control but it could equally apply to user control or a page.
I can't find a way of doing this. Way back in WinForm days there were events I could call before the form was shown and others for after. I guess I'm looking for something similar.
I have also tried to see if I can display a stack panel first with the Loading message then capture an event on that to fire the data loading but nothing so far.
Any ideas?
I'm using Caliburn Micro BTW.
You can use the page's Loaded event or an OnNavigatedTo override to show the Loading message, and then you can use the BackgroundWorker class to run your long-running process on a background thread so that the UI thread remains responsive, and then in the handler for the RunWorkerCompletedEvent handler, which is marshalled onto the UI thread for you, you can hide the loading message and perform your second stage display.
I want to persist the user's location in the document he or she is browsing, then bring them back to that spot when they return from tombstoning or between sessions.
My first approach was to wrap the browser component in a scrollviewer, but it turns out it handles its own scrolling and the scrollviewer never changes its verticaloffset.
My guess is that the browser component must have a scrollviewer or something like it embedded in it. I need to get the verticaloffset and scroll to an offset.
Any guesses how to get there?
My next approach would be a painful mish-mash of javascript and c# to figure out where they are...
Because of the way the WebBrowser control is built you'll need to track scrolling in Javascript then pass the location to managed code to handle storage of that value.
On resuming you'll need to have the managed code pass the scroll position to a Javascript function to reset the scroll position.
That's the theory but I haven't looked at the funcitonality around javascript scrolling events in the WebBrowser yet. That's the only place I can see possible problems.
Would be good to hear how you get on.
I've accepted Matt's answer, but I want to put in some details here. I'm also going to blog about how I did it once I'm completely done.
Since the WebBrowser component is essentially a black-box, you don't have as much control as I would like. Having said that, it is possible to get and set the vertical offset.
Javascript lets you ask for the value, but different browsers use different variations on HOW to ask. For THIS case I only have one browser to worry about.
First I make a couple of simple javascript functions:
function getVerticalOffset() {
return document.body.scrollTop;
}
function setVerticalOffset(offset) {
document.body.scrollTop = offset;
}
Next I call into the WebBrowser using the InvokeScript method on the browser object.
I'll post an update here with a link to my blog when I get the full write-up done.
I have been writing an eBook reader and had a similar question. Code for setting a scroll position has been easy enough to find.
Code for setting vertical scroll position:
string script = string.Format("window.scrollBy(0,{0});", "put your numeric value here");
wb_view.InvokeScript("eval", script);
Google didn't help much in finding solution for getting the value of current scroll position. Lacking any knowledge in javascript it took me almost two hours to get it right.
Code for getting the vertical scroll position:
var vScroll = wb_view.InvokeScript("eval",
"var vscroll = window.pageYOffset; vscroll.toString();");