I'm trying to learn to write Universal apps, and I'm starting out by trying to recreate another project I wrote in WinForms. I need to be able to read from log files in an arbitrary folder, and as I understand it I need to get the users permissions to access the folder. I should then store an access token so I can re-read that folder in future. By reading around I've managed to cobble together the following code:
Friend Async Function GetLogFolder() As Task(Of StorageFolder)
Dim myLogFolder As StorageFolder
If ApplicationData.Current.LocalSettings.Values.ContainsKey("LogFolder") Then
Dim sToken As String = ApplicationData.Current.LocalSettings.Values("LogFolder")
myLogFolder = StorageApplicationPermissions.FutureAccessList.GetFileAsync(sToken)
Else
Dim myFolderPicker As FolderPicker = New FolderPicker
myFolderPicker.FileTypeFilter.Add("*")
myLogFolder = Await myFolderPicker.PickSingleFolderAsync
Dim sToken As String = StorageApplicationPermissions.FutureAccessList.Add(myLogFolder)
ApplicationData.Current.LocalSettings.Values.Add("LogFolder", sToken)
End If
Return myLogFolder
End Function
But it doesn't seem to work. At this stage I have a form containing just a TextBlock and Button. Clicking the Button calls a method that will parse all the *.log files in a given folder. The first thing it does is:
Dim myFolder As StorageFolder = GetLogFolder.Result
When the code runs, and I click the button, I get a folder browser dialog shown, but then everything freezes and I have to switch to Visual Studio and hit stop. I've probably made some silly error, but I can't figure out what it is.
Any help would be much appreciated.
The problem is very likely not caused by the file access code itself, but by the way you use the asynchronous API.
Because the GetLogFolder method returns a Task of StorageFolder, you will need to await the result instead of getting it using the Result property. The reason is that the async/await pattern allows you to do I/O work on a separate thread but return control back to the UI thread when finished. What you are doing here is to call the GetLogFolder method, in which you let the user choose a folder using FolderPicker. Here is the problem - the user is presented with a folder picker while the control returns to your code and you query the Result property of the Task returned by the GetLogFolder method. Querying the Result property causes the UI thread to stop and wait until the Task is finished to get the result. Unfortunately, when the user picks the folder, the control wants to return to the UI thread to continue executing the rest of the GetLogFolder method and we have a deadlock. Result property stopped the UI thread to wait for the Task result and the Task waits for the UI thread to become available. Neither can continue so the app completely freezes.
The solution is quite simple - using the async / await keywords. You can read up more about them in VB.NET here with a clear example.
In your case the first step would be to make the button's Click handler method async and then replace the code inside by the following:
Dim myFolder As StorageFolder = Await GetLogFolder
Related
The post has been rewritten to better fit the current problem.
I have a button x:Name="selectVesselButton". On button click, it tries to establish a connection to a server, which takes a sec or two for to do. Originally, I wanted the button to be grayed out while it was downloading and deserializing the json file from the connection.
My old code (before async, and trying to update the button):
// disabling the button to prevent spam clicking.
string buttonText = selectVesselButton.Text;
selectVesselButton.IsEnabled = false;
selectVesselButton.Text = "loading...";
// retrieve data for speed page.
RetrieveData();
// redirect to next info block if build was successfull.
FocusSpeedblock();
// enabling the button again.
selectVesselButton.Text = buttonText;
selectVesselButton.IsEnabled = true;
The issue with this code was that the button visuals did not update until the RetrieveData() was finished, defeating the purpose of doing that at all. This was because the code for updating the interface and the code for downloading and deserializing the object were both on the same thread.
However, following Ivan's advice, I made the downloading and deserializing Async, which fixed this issue (more like moved it).
This works fairly well, but I am still having some trouble updating the interface automatically. I have some labels that need to be updated based on the json file output. The value of the labels update on the background, but only update visually once I interact with the labels (I.E. scrolling the scrollview they are on). Check edit 3 for more detail on that.
EDIT 3:
When the second thread is finished, it should call the UpdateSpeedLabels() and update some labels. However, they update in codebehind, without instantly updating the interface. They only update if I interact with those labels.
The preferred way of doing this on Xamarin is with data binding. As you opted out of this it is still possible.
What you need is to ensure that your long task is not running in the UI thread as it blocks it and prevent its updates. You do this by using Task.Run(() => { your task code }); . However you can't update your user interface inside the Task.Run as it is not running on the UI thread and it would crash the app, so you need to use Device.BeginInvokeOnMainThread(() => { your UI code }); inside Task.Run for that part.
My app displays an error dialog whenever a JavaScript error occurs. This is always a bad sign, so I want to set up my tests so that, if the error dialog appears, it causes the test to fail there and then.
So I'd like to do something like (very much pseudocode!);
// start a new 'guard' thread;
start {
found = this.driver.wait(untilVisible(By.css('.myErrorDialog')), VERY_LONG_TIMEOUT);
if (found) {
// the error dialog appeared! That's bad!
throw();
}
}
// now run the test
login();
clickButton();
testBannerContains();
But I'm having trouble and I think it has to do with the way Selenium schedules actions.
What I've found is that for a single driver, I can only schedule one thing at a time, so the guard I set up early in the test blocks the body of the test from starting.
Is there a better way to handle conditions like 'this should never happen', or a way to create two independent threads in the same test?
So the problem with the code you have is that it immediately runs it and waits for a VERY_LONG_TIMEOUT amount of time for that error dialog to appear. Since it never does, it continues to wait. You have already discovered that is not what you want... ;)
I haven't done anything like this but I think you want a JS event handler that watches for the event that is triggered when the error dialog appears. See the link below for some guidance there.
Can my WebDriver script catch a event from the webpage?
One option would be to watch for that event to fire and then store true (or whatever) in some JS variable. Before leaving a page, check to see if the variable is set to true and if so, fail the test. You can set and get JS variables using JavascriptExecutor. Some google searches should get you all you need to use it.
I'm working on a program that should find some image's urls from a website and should download them, i already wrote the parsing code and the downloading code, and it work, but since i noticed it's really slow i thought that it would be better if i make it work async from the form, so i created 2 background worker:
1) The parser
2) The downloader
When the parser starts the downloader starts too, the parser should add urls to a listbox, and the downloader should download them and delete from the list, i don't think it will be a problem to manage that, my real problem is... that i never used background worker...
The parser should load the page in a Webbrowser, than parse the images, but when i call the function navigate on the webbrowser... it stop giving me a TargetInvocationException.
I searched online, and from what i have seen it seems that backgrond workers cannot directly access to proprieties and methods of the GUI controls, from what i've understand it shold use Invoke, so i created a function that do all the work, and it check if the browser is in another thread or is in this thread by doing this:
Sub parse(ByVal url As String)
If wb.InvokeRequired Then 'wb is the Webbrowser
wb.Invoke(New Action(AddressOf prova))
Return
End If
'Navigate to the url, wait for browser to complete loading then do the parsing
End Sub
Now my problems are two:
1)The invokerequired propriety value is false even if i call the method from the worker, so the invoke is not called and it still give me the same exception
2)If i call invoke i should force the method to run in the GUI thread, right?
If so... shouldn't it slow my program as before?
I did it alone, the problem was that i created the components in runtime, so I should have created the handles for the components, it was enough to do something like this after creating the components:
If Not wb.IsHandleCreated Then
Dim handle As IntPtr = wb.Handle
End If
The variable handle is useless, but calling the handle propriety of the component it force the component to create the handle, that's why I made that assignment.
I'm coming from VB 6 and am semi-new to VB.NET.
I'm writing a Windows Phone 8 application. There's a grid in which I have several textBlocks that I want to dynamically display data from a file stream (that contains scanned data).
When the WP8 page opens, it automatically loads the data into the textBlocks. This works. Before I load the data from the file, I want to "reset" all textBlocks and hide them. For this I wanted to use a procedure which essentially does the following for every textBlock:
tbl1.Text = ""
tbl1.Visibility = System.Windows.Visibility.Collapsed
This works exactly one time: when the page loads. The procedure does not produce an error.
Now when I call that method again later, when I want to refresh the data shown on the page, I get the following error on the first line of code shown above:
An exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll but was not handled in user code
Additional information: Invalid cross-thread access.
I'm a bit lost now. In VB6 I was able to do whatever I wanted with my UI elements. I presume I'm making some kind of newbie mistake here?
I read somewhere about some Dispatcher thing. But that seems overly complicated to simply change a value in a textBlock to me. Is there no simple way?
The solution is indeed to use the Dispatcher.BeginInvoke method.
Dispatcher.BeginInvoke(() =>
{
tbl1.Text = ""
tbl1.Visibility = System.Windows.Visibility.Collapsed
});
The reason why you need this is because you try to access an object that depends on the UI thread from a non-UI thread.
It is probably because you are not calling the method from UI thread. Try using Dispatcher.BeginInvoke:
Dispatcher.BeginInvoke(()=>
{
tbl1.Text = "";
tbl1.Visibility = System.Windows.Visibility.Collapsed;
});
My WinForm apps needs to execute complex query with significant execution time, I have no influence (about 10mins)
When query is executing user sees 'application not responding' in task manager, which is really confusing to user, also not very professional...
I believe that query shall be executed in different thread or so. Have tried some approaches but have difficulties to make it really working (execute query, force main application wait result, return back to main app, possibility to cancel execution etc)
I wonder if you have own / good working solution for that. Code samples would be also very welcome :)
Also I believe there might exist some ready to use utilities / frameworks allowing simple execution of that.
The simplest approach here would be to do that work from a BackgroundWorker. MSDN has examples for this. This then executes on a worker thread, with events for completion/error/etc. It can also support cancel, but your operation needs to be coded to be interruptable.
Another approach is the Task API in 4.0; but if you use this you'll need to get back to the UI thread (afterwards) yourself. With BackgroundWorker this is automatic (the events are raised on the UI thread).
If ExecuteQuery if the method you want to execute, you can do:
void SomeMethod() {
var thread = new Thread(ExecuteQuery);
thread.Start();
}
void ExecuteQuery() {
//Build your query here and execute it.
}
If ExecuteQuery receives some parameters, like:
void ExecuteQuery(string query) {
//...
}
You can do:
var threadStarter = () => { ExecuteQuery("SELECT * FROM [Table]"); };
var thread = new Thread(ThreadStarter);
thread.Start();
If you want to stop the execution of the background thread, avoid calling thread.Abort() method. That will kill the thread, and you do not want this, because some incosistency could appear in your database.
Instead, you can have a bool variable visible from ExecuteQuery and from outside you can set it to True when you want to stop it. Then all you have to do is check in some parts of the code inside ExecuteQuery if that variable is still True. Otherwise, do some rollback to maintain the database stable.
Be sure you set that bool variable volatile
Edit:
If you want the UI to wait from the background thread, I usually do:
Start the background thread
Start some progress bar in the UI
Disable some controls (like buttons, etc) to avoid the user to click them while the background thread is working (eg: If you're executing the thread when a user clicks a button, then you should disable that button, otherwise multiple queries will be ocurring at the same time).
After finished the thread, you can stop progress bar and enable controls again.
How to know when the thread finished? You can use Events for that. Just create an event and fire it when it finishes, and do whatever you want inside the event handler...
Be sure you're accessing correctly to UI controls from the background thread, otherwise it will give you an error.
If you are new to threads and task is simple (as it seems), you should try to use standard background worker component.