I created for Windows Phone 8.0 Silverlight App an async method GetStreetName
string streetname;
private async Task<string> GetStreetName(int i)
{
MapAddress address;
ReverseGeocodeQuery query = new ReverseGeocodeQuery();
query.GeoCoordinate = Route[i].Item1;
query.QueryCompleted += (s, e) =>
{
if (e.Error != null)
return;
address = e.Result[0].Information.Address;
streetname = address.Street;
};
query.QueryAsync();
return streetname;
}
and I call it using the await operator inside of a for loop:
for (int i = 0; i < Route.Count; i++)
{
ListBox.Items.Add(await GetStreetName(i));
}
but I always get only the street name of the first loaded geoposition back and I have no idea why (I thought the await operator is waiting until the async method is finished).
Additional info: i just saw that this is not 100% clear at this short snippet, streetname and Route are global "variables", Route is a tuple list where the first item is a geocoordinate.
How can I fix this issue?
You are returning from GetStreetName before the results are ready becayse query.QueryAsync(); just starts the query and doesn't wait for it to be complete.
On top of that, you're writing all results to the same global streetname.
You need to use a TaskCompletionSource.
Try something like this:
private async Task<string> GetStreetNameAsync(int i)
{
var tcs = new TaskCompletionSource<IEnumerable<string>>();
EventHandler<QueryCompletedEventArgs<IList<MapLocation>>> handler = (s, e) =>
{
if (e.Error != null)
{
tcs.TrySetException(e.Error);
return;
}
if (e.Cancelled)
{
tcs.TrySetCanceled();
return;
}
tcs.TrySetResult(e.Result[0].Information.Address.Street);
};
var query = new ReverseGeocodeQuery();
query.GeoCoordinate = Route[i].Item1;
try
{
query.QueryCompleted += handler;
query.QueryAsync();
return await tcs.Task;
}
finally
{
query.QueryCompleted -= handler;
}
}
Related
I have tried the code mentioned in this answer: https://stackoverflow.com/a/27089652/
It works fine and I want to use it for running a PowerShell script in for loop. GUI was freezing initially then I tried the code mentioned in this answer: https://stackoverflow.com/a/35735760/
Now GUI does not freeze while the PowerShell script is running in the background although nothing is updated in the textbox until for loop is complete. I want to see the results updating in real time. Here is the code I am running:
private async void run_click(object sender, RoutedEventArgs e)
{
Text1.Text = "";
await Task.Run(() => PS_Execution(Text1));
}
internal async Task PS_Execution(TextBox text)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(script.ToString());
{
Collection<PSObject> results = ps.Invoke();
foreach (PSObject r in results)
{
text.Dispatcher.Invoke(() =>
{
text.Text += r.ToString();
});
await Task.Delay(100);
}
}
}
Maybe I am missing something important. Please help me understand how to solve this problem.
Instead of using ps.Invoke() which is synchronous call and will wait for all results to return use ps.BeginInvoke() instead. Then subscribe to the DataAdded event of the output PSDataCollection and use the action to update your ui.
private async void run_click(object sender, RoutedEventArgs e)
{
Text1.Text = "";
await Task.Run(() => PS_Execution(Text1));
}
internal async Task PS_Execution(TextBox text)
{
using PowerShell ps = PowerShell.Create();
ps.AddScript(script.ToString());
PSDataCollection<string> input = null;
PSDataCollection<string> output = new();
IAsyncResult asyncResult = ps.BeginInvoke(input, output);
output.DataAdded += (sender, args) =>
{
var data = sender as PSDataCollection<string>;
text.Dispatcher.Invoke(() =>
{
text.Text += data[args.Index];
});
};
}
I need to capture an image from my camera using xamarin.forms portable, and obtain access to the image byte[] data for image processing purposes.
How can this be done?
I have the working code that captures the image and simply shows it, using xlabs
public async Task<MediaFile> TakePicture()
{
Setup ();
ImageSource = null;
return await _Mediapicker.TakePhotoAsync (new CameraMediaStorageOptions {
DefaultCamera = CameraDevice.Front, MaxPixelDimension = 400
}).ContinueWith (t => {
if (t.IsFaulted)
{
Status = t.Exception.InnerException.ToString();
}
else if (t.IsCanceled)
{
Status = "Canceled";
}
else
{
MediaFile mediaFile = t.Result;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
return mediaFile;
}
return null;
}, _scheduler);
}
and
private async void buttonTakePicture_Clicked() {
await cameraViewModel.TakePicture();
imageView.Source = cameraViewModel.ImageSource;
}
clicking the button launches cameraViewModel.TakePicture() which in turn uses xlabs to actually take the picture on the device.
How can I alter the code to also give me the image raw data (or use a different code altogether)?
Thanks
You can take mediaFile.Path and pass it as a parameter for a DependencyService:
//For Android
public byte[] ByteArrayFromStream(string path)
{
var bitmap = BitmapFactory.DecodeFile(path);
byte[] bitmapData;
using(stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
//For iOS
public byte[] ByteArrayFromStream(string path)
{
UIImage originalImage = UIImage.FromFile(path);
return originalImage.AsPNG().ToArray();
}
To learn more about DependencyService you can access the documentation at:
https://developer.xamarin.com/guides/xamarin-forms/dependency-service/
This will help you to get byte[ ] in PCL :
public async Task<MediaFile> TakePicture()
{
Setup ();
ImageSource = null;
return await _Mediapicker.TakePhotoAsync (new CameraMediaStorageOptions {
DefaultCamera = CameraDevice.Front, MaxPixelDimension = 400
}).ContinueWith (t => {
if (t.IsFaulted)
{
Status = t.Exception.InnerException.ToString();
}
else if (t.IsCanceled)
{
Status = "Canceled";
}
else
{
MediaFile mediaFile = t.Result;
using (MemoryStream ms = new MemoryStream())
{
mediaFile.Source.CopyTo(ms);
byte[] attchmentbytes = ms.ToArray();
}
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
return mediaFile;
}
return null;
}, _scheduler);
}
In my programme I've a large array of strings(say 1600) which I want to show as a CheckBox list. The array is actually the location of all the songs in one's PC, and thus can gradually be bigger. I don't wanna use ListView<String> as the CheckBox list is more efficient and above all visually better for my purpose. I'm currently doing the below :
private void listAll() {
songs = MediaManager.getAllSongs();
VBox vb = new VBox();
vb.setSpacing(5);
vb.getStyleClass().add("background");
if (songs != null) {
Service s = new Service() {
#Override
protected Task createTask() {
Task t = new Task() {
#Override
protected Object call() throws Exception {
for (String song : songs) {
addSong(song, vb);
c++;
updateMessage(c+" songs");
}
return null;
}
};
t.messageProperty().addListener((obs,o,n)->{
count.setText(n);
});
return t;
}
};
s.start();
ScrollPane sp = new ScrollPane(vb);
getChildren().add(sp);
}
}
private void addSong(String n, Pane p) {
String toAdd = "";
Media m = new Media(Paths.get(n).toUri().toString());
if (m.getMetadata().get("title") == null || !(m.getMetadata().get("title").equals(""))) {
toAdd = m.getSource().split("/")[m.getSource().split("/").length - 1].replace("%20", " ").replace(".mp3", "");
} else {
toAdd = ((String) m.getMetadata().get("title"));
}
SongBox s = new SongBox(toAdd);
s.setUserData(n);
p.getChildren().add(s);
}
class SongBox extends CheckBox {
public SongBox(String t) {
this();
setText(t);
}
public SongBox() {
super();
setOnAction((ActionEvent evt) -> {
if (isSelected()) {
if (!playNow.isVisible()) {
playNow.setVisible(true);
}
path = (String) getUserData();
selected.add((String) getUserData());
} else {
selected.remove((String) getUserData());
if (selected.size() == 0) {
playNow.setVisible(false);
}
}
});
}
}
First of all, that is not showing the complete array. Whenever I'm going back and returning to it, the number of songs get changed. Secondly, the whole UI is getting sluggish(sometimes also hanging my PC). Moreover, I can't cancel the Service when I've gone to the previous window, as it's always returning false. Anyone have a better approach?
In my Mainpage.xaml.cs file I have a function that creates an instance of another class and tries to download a webpage using a HttpWebRequest from that instance. The problem is, once I've managed to download the webpage I can't send it back to the main UI thread. I've tried using Deployment.Current.Dispatcher.BeginInvoke to send the webpage back to a TextBlock I have waiting, but when I try I get an error telling me that I can't access the TextBlock from the other class. Is there any way to pass data between two threads without using LocalStorage?
EDIT: code below:
MainPage:
private void button1_Click(object sender, RoutedEventArgs e)
{
Member m = new Member(name, id);
}
Member class:
public Member(String Member, String API)
{
APIKey = API;
MemberName = Member;
this.super = super;
DoSend(method, string, "", null);
}
public void DoSend(string method, string url, string body, string mimetype)
{
if (WebRequest.RegisterPrefix("https://",System.Net.Browser.WebRequestCreator.ClientHttp)) {
HttpWebRequest request = WebRequest.Create(makeURI(url)) as HttpWebRequest;
request.Method = method;
request.Headers["X-NFSN-Authentication"] = MakeAuthHeader(url,body);
if (body != "")
{
byte[] bodyData = Encoding.UTF8.GetBytes(body);
request.ContentType = mimetype;
//Stuff Should Happen Here
}
else
doStuff(request);
}
public void doStuff(HttpWebRequest httpReq)
{
httpReq.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
ResponseBlock.Text = response; //Invalid cross-thread reference
}
}, httpReq);
}
MainPage:
customClass.DownloadPage((result) =>
{
textBlock.Text = result;
},
(exception) =>
{
MessageBox.Show(exception.Message);
});
CustomClass:
public void DownloadPage(Action<string> callback, Action<Exception> exception)
{
WebClient webClient = new WebClient();
webClient.DonwloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(e.Result);
});
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
exception(e.Error);
});
}
};
webClient.DonwloadStringAsync();
}
I am working on a 'proof of concept' Silverlight 4 project and am learning the way of THE ASYNC. I have stopped fighting the urge to implement some pseudo-synchronous smoke and mirrors technique. I am going to learn to stop worrying and love THE ASYNC.
Most of the time I just use a BusyIndicator while async methods are running and all is good but I have run into a few situations where I need to call methods sequentially. I put together this example and it works. But in my experience... if it works... there is something wrong with it.
When is this going to blow up in my face or steal my wife or date one of my daughters?
Is there a better way to do this?
The Code:
public class CustomPage : Page
{
static readonly object _AsyncMethodChain_Lock = new object();
private Dictionary<Action<object>, string> _AsyncMethodChain = new Dictionary<Action<object>, string>();
public Dictionary<Action<object>, string> AsyncMethodChain
{
get { lock (_AsyncMethodChain_Lock) { return this._AsyncMethodChain; } }
set { lock (_AsyncMethodChain_Lock) { this._AsyncMethodChain = value; } }
}
private void CustomPage_Loaded(object sender, RoutedEventArgs e)
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var user = this.SecurityProvider.UserObject as TimeKeeper.UserServiceReference.User;
if (user == null)
return;
this.AsyncMethodChain.Add(
data =>
{
var userServiceClient = new UserServiceClient();
userServiceClient.GetCompleted +=
(send, arg) =>
{
var userViewSource = this.Resources["userViewSource"] as CollectionViewSource;
userViewSource.Source = new List<UserServiceReference.User>(new UserServiceReference.User[1] { arg.Result });
userViewSource.View.MoveCurrentToPosition(0);
this.AsyncMethodChain.ExecuteNext(arg.Result.UserID, this.BusyIndicator);
};
userServiceClient.GetAsync(user.UserID);
},
"Loading user..."
);
this.AsyncMethodChain.Add(
data =>
{
var userID = (int)data;
var timeLogServiceClient = new TimeLogServiceClient();
timeLogServiceClient.FindByUserIDCompleted +=
(send, arg) =>
{
var timeLogViewSource = this.Resources["timeLogViewSource"] as CollectionViewSource;
timeLogViewSource.Source = arg.Result;
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
};
timeLogServiceClient.FindByUserIDAsync(userID);
},
"Loading time logs..."
);
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
}
}
}
public static class Extensions
{
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data, BusyIndicator busyIndicator)
{
if (methods.Count <= 0)
{
busyIndicator.BusyContent = "";
busyIndicator.IsBusy = false;
return;
}
else
{
var method = methods.Keys.ToList<Action<object>>()[0];
busyIndicator.BusyContent = methods[method];
busyIndicator.IsBusy = true;
}
methods.ExecuteNext(data);
}
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data)
{
var method = methods.Keys.ToList<Action<object>>()[0];
methods.Remove(method);
method(data);
}
}
What you have sine looks pretty good, but if you are still worried about the callsequence i would sugest that you create a new method in your webservice which will call the other four in whatever sequence you need them to and call this new method from your silverlight application.
I dont see the need for you to do that as your current implemenation is pretty good as well.