btnSave Event: in this event i wana update datagridview but it's not working :(
private async void btnSave_Click(object sender, EventArgs e)
{
if (this.ValidateChildren(ValidationConstraints.Enabled))
{
Remember remember = new Remember();
remember.RememberTitle = txtTitle.Text;
remember.RememberDate = dateTimePickerRememberDate.Value.Date;
remember.RememberTime = dateTimePickerRememberTime.Value.TimeOfDay;
remember.RememberContent = txtDescription.Text;
remember.RememberTransaction = false;
remember.RememberExpire = false;
var res = await Rep_App.NewRemember(remember).ConfigureAwait(false);
if (res)
{
MessageBox.Show("Success");
//this line did not executed
await GetRemembers();
}
else
{
MessageBox.Show("...");
}
}
}
GetRemember method :
private async Task GetRemembers()
{
try
{
// Instantiate a new DBContext
ParkingManager.Context.DataBaseContext dbContext = new ParkingManager.Context.DataBaseContext(PublicVariables.ConnectionString);
// Call the LoadAsync method to asynchronously get the data for the given DbSet from the database.
await dbContext.Remembers.LoadAsync().ContinueWith(loadTask =>
{
// Bind data to control when loading complete
remembersBindingSource.DataSource = dbContext.Remembers.ToList();
}, System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
}
catch (InvalidOperationException)
{
return;
}
catch (Exception)
{
MessageBox.Show(MessagesStruct.Exception);
}
}
GetRemember() did not execute in Save Event but when the form loaded GetRemember() executed completely, what's wrong?
The problem is caused by ConfigureAwait(false) and ContinueWith. ConfigureAwait(false) will cause execution to continue on a background thread instead of getting back to the UI. LoadAsync isn't needed either, the data will be loaded by ToList or ToListAsync().
EF Core and async/await don't need so much code. All of this can be replaced with
var res = await Rep_App.NewRemember(remember);
if (res)
{
MessageBox.Show("Success");
using(var dbContext = new DataBaseContext(PublicVariables.ConnectionString))
{
remembersBindingSource.DataSource = await dbContext.Remembers.ToListAsync();
}
}
else
{
MessageBox.Show("...");
}
Related
I'm using Two await function in my program, and first one is working flawlessly but on encountering second await, my program comes out of function without executing that awaitable function and any line after that.
Not giving error and not even crashing.
Tried "wait()", "Running on main thread", "Task Run", "Threading Sleep"..
here a snippet of the code along
private async void Btn_Clicked(object sender, EventArgs e)
{
//this statement works perfectly fine
var status = await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>();
//on debugging i found out it return out of function at this point without executing
var check = await CrossPermissions.Current.RequestPermissionAsync<CameraPermission>();
//None of the code is executed
Console.WriteLine("Granted");
Console.WriteLine("Granted");
}
You try to use the below method :
private async void Btn_Clicked(object sender, EventArgs e)
{
await GetPermissions();
}
public static async Task<bool> GetPermissions()
{
bool permissionsGranted = true;
var permissionsStartList = new List<Permission>()
{
Permission.Location,
Permission.Camera,
};
var permissionsNeededList = new List<Permission>();
try
{
foreach (var permission in permissionsStartList)
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
if (status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
{
permissionsNeededList.Add(permission);
}
}
}
catch (Exception ex)
{
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(permissionsNeededList.ToArray());
try
{
foreach (var permission in permissionsNeededList)
{
var status = Plugin.Permissions.Abstractions.PermissionStatus.Unknown;
//Best practice to always check that the key exists
if (results.ContainsKey(permission))
status = results[permission];
if (status == Plugin.Permissions.Abstractions.PermissionStatus.Granted || status == Plugin.Permissions.Abstractions.PermissionStatus.Unknown)
{
permissionsGranted = true;
}
else
{
permissionsGranted = false;
break;
}
}
}
catch (Exception ex)
{
}
return permissionsGranted;
}
I have been attempting to have a re-usable modal progress window (I.e. progressForm.ShowDialog()) to show progress from a running async task, including enabling cancellation.
I have seen some implementations that launch start the async task by hooking the Activated event handler on the form, but I need to start the task first, then show the modal dialog that will show it's progress, and then have the modal dialog close when completed or cancellation is completed (note - I want the form closed when cancellation is completed - signalled to close from the task continuation).
I currently have the following - and although this working - are there issues with this - or could this be done in a better way?
I did read that I need to run this CTRL-F5, without debugging (to avoid the AggregateException stopping the debugger in the continuation - and let it be caught in the try catch as in production code)
ProgressForm.cs
- Form with ProgressBar (progressBar1) and Button (btnCancel)
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
}
public event Action Cancelled;
private void btnCancel_Click(object sender, EventArgs e)
{
if (Cancelled != null) Cancelled();
}
public void UpdateProgress(int progressInfo)
{
this.progressBar1.Value = progressInfo;
}
}
Services.cs
- Class file containing logic consumed by WinForms app (as well as console app)
public class MyService
{
public async Task<bool> DoSomethingWithResult(
int arg, CancellationToken token, IProgress<int> progress)
{
// Note: arg value would normally be an
// object with meaningful input args (Request)
// un-quote this to test exception occuring.
//throw new Exception("Something bad happened.");
// Procressing would normally be several Async calls, such as ...
// reading a file (e.g. await ReadAsync)
// Then processing it (CPU instensive, await Task.Run),
// and then updating a database (await UpdateAsync)
// Just using Delay here to provide sample,
// using arg as delay, doing that 100 times.
for (int i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(arg);
progress.Report(i + 1);
}
// return value would be an object with meaningful results (Response)
return true;
}
}
MainForm.cs
- Form with Button (btnDo).
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private async void btnDo_Click(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// Create the ProgressForm, and hook up the cancellation to it.
ProgressForm progressForm = new ProgressForm();
progressForm.Cancelled += () => cts.Cancel();
// Create the progress reporter - and have it update
// the form directly (if form is valid (not disposed))
Action<int> progressHandlerAction = (progressInfo) =>
{
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.UpdateProgress(progressInfo);
};
Progress<int> progress = new Progress<int>(progressHandlerAction);
// start the task, and continue back on UI thread to close ProgressForm
Task<bool> responseTask
= MyService.DoSomethingWithResultAsync(100, token, progress)
.ContinueWith(p =>
{
if (!progressForm.IsDisposed) // don't attempt to close disposed form
progressForm.Close();
return p.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Debug.WriteLine("Before ShowDialog");
// only show progressForm if
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.ShowDialog();
Debug.WriteLine("After ShowDialog");
bool response = false;
// await for the task to complete, get the response,
// and check for cancellation and exceptions
try
{
response = await responseTask;
MessageBox.Show("Result = " + response.ToString());
}
catch (AggregateException ae)
{
if (ae.InnerException is OperationCanceledException)
Debug.WriteLine("Cancelled");
else
{
StringBuilder sb = new StringBuilder();
foreach (var ie in ae.InnerExceptions)
{
sb.AppendLine(ie.Message);
}
MessageBox.Show(sb.ToString());
}
}
finally
{
// Do I need to double check the form is closed?
if (!progressForm.IsDisposed)
progressForm.Close();
}
}
}
Modified code - using TaskCompletionSource as recommended...
private async void btnDo_Click(object sender, EventArgs e)
{
bool? response = null;
string errorMessage = null;
using (CancellationTokenSource cts = new CancellationTokenSource())
{
using (ProgressForm2 progressForm = new ProgressForm2())
{
progressForm.Cancelled +=
() => cts.Cancel();
var dialogReadyTcs = new TaskCompletionSource<object>();
progressForm.Shown +=
(sX, eX) => dialogReadyTcs.TrySetResult(null);
var dialogTask = Task.Factory.StartNew(
() =>progressForm.ShowDialog(this),
cts.Token,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
await dialogReadyTcs.Task;
Progress<int> progress = new Progress<int>(
(progressInfo) => progressForm.UpdateProgress(progressInfo));
try
{
response = await MyService.DoSomethingWithResultAsync(50, cts.Token, progress);
}
catch (OperationCanceledException) { } // Cancelled
catch (Exception ex)
{
errorMessage = ex.Message;
}
finally
{
progressForm.Close();
}
await dialogTask;
}
}
if (response != null) // Success - have valid response
MessageBox.Show("MainForm: Result = " + response.ToString());
else // Faulted
if (errorMessage != null) MessageBox.Show(errorMessage);
}
I think the biggest issue I have, is that using await (instead of
ContinueWith) means I can't use ShowDialog because both are blocking
calls. If I call ShowDialog first the code is blocked at that point,
and the progress form needs to actually start the async method (which
is what I want to avoid). If I call await
MyService.DoSomethingWithResultAsync first, then this blocks and I
can't then show my progress form.
The ShowDialog is indeed a blocking API in the sense it doesn't return until the dialog has been closed. But it is non-blocking in the sense it continues to pump messages, albeit on a new nested message loop. We can utilize this behavior with async/await and TaskCompletionSource:
private async void btnDo_Click(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// Create the ProgressForm, and hook up the cancellation to it.
ProgressForm progressForm = new ProgressForm();
progressForm.Cancelled += () => cts.Cancel();
var dialogReadyTcs = new TaskCompletionSource<object>();
progressForm.Load += (sX, eX) => dialogReadyTcs.TrySetResult(true);
// show the dialog asynchronousy
var dialogTask = Task.Factory.StartNew(
() => progressForm.ShowDialog(),
token,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
// await to make sure the dialog is ready
await dialogReadyTcs.Task;
// continue on a new nested message loop,
// which has been started by progressForm.ShowDialog()
// Create the progress reporter - and have it update
// the form directly (if form is valid (not disposed))
Action<int> progressHandlerAction = (progressInfo) =>
{
if (!progressForm.IsDisposed) // don't attempt to use disposed form
progressForm.UpdateProgress(progressInfo);
};
Progress<int> progress = new Progress<int>(progressHandlerAction);
try
{
// await the worker task
var taskResult = await MyService.DoSomethingWithResultAsync(100, token, progress);
}
catch (Exception ex)
{
while (ex is AggregateException)
ex = ex.InnerException;
if (!(ex is OperationCanceledException))
MessageBox.Show(ex.Message); // report the error
}
if (!progressForm.IsDisposed && progressForm.Visible)
progressForm.Close();
// this make sure showDialog returns and the nested message loop is over
await dialogTask;
}
I'm using monogame as a framework for games and I'm trying to implement a in-app-purchase-functionality in an UWP-App which throws an exception when I'm calling RequestProductPurchaseAsync. It states:
Cannot change thread mode after it is set. (Exception from HRESULT:
0x80010106 (RPC_E_CHANGED_MODE)) at
Windows.ApplicationModel.Store.CurrentAppSimulator.RequestProductPurchaseAsync(String
productId) at
Crocs_World__Xbox_Edition_.App.d__7.MoveNext()
That's what I'm doing in code:
public async Task LoadInAppPurchaseProxyFileAsync()
{
StorageFolder proxyDataFolder = await Package.Current.InstalledLocation.GetFolderAsync("data");
StorageFile proxyFile = await proxyDataFolder.GetFileAsync("in-app-purchase.xml");
licenseChangeHandler = new LicenseChangedEventHandler(InAppPurchaseRefreshScenario);
CurrentAppSimulator.LicenseInformation.LicenseChanged += licenseChangeHandler;
await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
// setup application upsell message
try
{
ListingInformation listing = await CurrentAppSimulator.LoadListingInformationAsync();
var product1 = listing.ProductListings["product1"];
var product2 = listing.ProductListings["product2"];
}
catch (Exception e)
{
Debug.WriteLine("LoadListingInformationAsync API call failed:" + e);
}
}
private async void InAppPurchaseRefreshScenario()
{
Debug.WriteLine("InAppPurchaseRefreshScenario");
}
public async Task BuyFeature()
{
LicenseInformation licenseInformation = CurrentAppSimulator.LicenseInformation;
if (!licenseInformation.ProductLicenses["product2"].IsActive)
{
Debug.WriteLine("Buying Product 2...");
try
{
await CurrentAppSimulator.RequestProductPurchaseAsync("product2");
if (licenseInformation.ProductLicenses["product2"].IsActive)
{
Debug.WriteLine("You bought Product 2.");
}
else
{
Debug.WriteLine("Product 2 was not purchased.");
}
}
catch (Exception e)
{
Debug.WriteLine("Unable to buy Product 2." + e);
}
}
else
{
Debug.WriteLine("You already own Product 2.");
}
}
Whenever I call BuyFeature it throws the exception. Except if I call it right in LoadInAppPurchaseProxyFileAsync. Then it seems to be in the same thread I guess.
If I replace Task with void in both methods it doesn't work either. It also doesn't matter if I call it from app.xaml.cs or the game.cs.
Does anybody have an idea what I'm doing wrong?
Thank you,
Harry
You have to call await CurrentAppSimulator.RequestProductPurchaseAsync(...) in the UI thread, because if you run it in release mode with await CurrentApp.RequestProductPurchaseAsync(...) it shows a "Buy item" content dialog on the screen which is only possible in the UI thread.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await CurrentAppSimulator.RequestProductPurchaseAsync("product2");
};
This question already has answers here:
Stop SQL query execution from .net Code
(3 answers)
Closed 8 years ago.
If you recommend use SqlCommand.Cancel(), please give a working example!
I need to show a wait form with a cancel button(if the user clicks this button, the query must stop running) during query execution.
My solution: (.net 4)
I pass to form constructor two parameters:
query execution task
cancelation action
Below is code of my loading form:
public partial class LoadingFrm : Form
{
//task for execute query
private Task execute;
//action for cancelling task
private Action cancel;
public LoadingFrm(Task e, Action c)
{
execute = e;
cancel = c;
InitializeComponent();
this.cancelBtn.Click += this.cancelBtn_Click;
this.FormBorderStyle = FormBorderStyle.None;
this.Load += (s, ea) =>
{
//start task
this.execute.Start();
//close form after execution of task
this.execute.ContinueWith((t) =>
{
if (this.InvokeRequired)
{
Invoke((MethodInvoker)this.Close);
}
else this.Close();
});
};
}
//event handler of cancel button
private void cancelBtn_Click(object sender, EventArgs e)
{
cancel();
}
}
Below code of my ExecuteHelper class:
public class ExecuteHelper
{
public static SqlDataReader Execute(SqlCommand command)
{
var cts = new CancellationTokenSource();
var cToken = cts.Token;
cToken.Register(() => { command.Cancel(); });
Task<SqlDataReader> executeQuery = new Task<SqlDataReader>(command.ExecuteReader, cToken);
//create a form with execute task and action for cancel task if user click on button
LoadingFrm _lFrm = new LoadingFrm(executeQuery, () => { cts.Cancel(); });
_lFrm.ShowDialog();
SqlDataReader r = null;
try
{
//(1) here
r = executeQuery.Result;
}
catch (AggregateException ae)
{
}
catch (Exception ex)
{
}
return r;
}
}
But I can't stop execution of SqlCommand. After some time method that call ExecuteHelper.Execute(command) get a result (SqlDataReader) from sql server with data? Why? Anybody can help? How i can cancel execution of sqlcommand?
And i have another question. Why if i click a cancel button of my form and cts.Cancel() was been called //(1) here i get executeQuery.IsCanceled = false although executeQuery.Status = Faulted.
Instead of calling ExecuteReader call ExecuteReaderAsync and pass in the CancellationToken.
If you are on .net 4.0 you can write your own ExecuteReaderAsync using a TaskCompletionSource. I haven't tested this code, but it should roughly be this:
public static class Extensions
{
public static Task<SqlDataReader> ExecuteReaderAsync(this SqlCommand command, CancellationToken token)
{
var tcs = new TaskCompletionSource<SqlDataReader>();
// whne the token is cancelled, cancel the command
token.Register( () =>
{
command.Cancel();
tcs.SetCanceled();
});
command.BeginExecuteReader( (r) =>
{
try
{
tcs.SetResult(command.EndExecuteReader(r));
}
catch(Exception ex)
{
tcs.SetException(ex);
}
}, null);
return tcs.Task;
}
}
You are using the SqlCommand.Cancel method to cancel any async operation in progress. Then you can use it like this:
public static SqlDataReader Execute(SqlCommand command)
{
SqlDataReader r = null;
var cts = new CancellationTokenSource();
var cToken = cts.Token;
var executeQuery = command.ExecuteReaderAsync(cToken).
.ContinueWith( t =>
{
if(t.IsCompleted)
{
r = t.Result;
}
}, TaskScheduler.Default);
//create a form with execute task and action for cancel task if user click on button
LoadingFrm _lFrm = new LoadingFrm(executeQuery, () => { cts.Cancel(); });
// Assuming this is blocking and that the executeQuery will have finished by then, otheriwse
// may need to call executeQuery.Wait().
_lFrm.ShowDialog();
return r;
}
I modified the Execute method to use ContunueWith rather than r.Result because Result is a blocking property and you'll not show the dialog until the query is complete.
As mentioned it is untested, but should be pretty close to what you need.
I'm developing a WinPhone 8 App.
On this App there is a Button 'Send SMS'.
When the user clicks on this button two things should happen:
(Method A) Get the geo-coordinate of the current Location (using Geolocator and GetGeopositionAsync).
(Method B) Compose and send an SMS with the geo-coordinate as part of the body.
The Problem: GetGeopositionAsync is an asynchronous method. Before the coordinate is detected (which takes a few seconds) the SMS is sent (of course with no coordinates).
How can I tell Method 2 to wait until the coordinates are available?
OK, here is my code:
When the user presses the button, the coordinates are determined by the first method and the second method sends the SMS which includes the coordinates in its body:
private void btnSendSms_Click(object sender, RoutedEventArgs e)
{
GetCurrentCoordinate(); // Method 1
// -> Gets the coordinates
SendSms(); // Method 2
// Sends the coordinates within the body text
}
The first method GetCurrentCoordinate() looks as follows:
...
private GeoCoordinate MyCoordinate = null;
private ReverseGeocodeQuery MyReverseGeocodeQuery = null;
private double _accuracy = 0.0;
...
private async void GetCurrentCoordinate()
{
Geolocator geolocator = new Geolocator();
geolocator.DesiredAccuracy = PositionAccuracy.High;
try
{
Geoposition currentPosition = await geolocator.GetGeopositionAsync(
TimeSpan.FromMinutes(1),
TimeSpan.FromSeconds(10));
lblLatitude.Text = currentPosition.Coordinate.Latitude.ToString("0.000");
lblLongitude.Text = currentPosition.Coordinate.Longitude.ToString("0.000");
_accuracy = currentPosition.Coordinate.Accuracy;
MyCoordinate = new GeoCoordinate(
currentPosition.Coordinate.Latitude,
currentPosition.Coordinate.Longitude);
if (MyReverseGeocodeQuery == null || !MyReverseGeocodeQuery.IsBusy)
{
MyReverseGeocodeQuery = new ReverseGeocodeQuery();
MyReverseGeocodeQuery.GeoCoordinate = new GeoCoordinate(
MyCoordinate.Latitude,
MyCoordinate.Longitude);
MyReverseGeocodeQuery.QueryCompleted += ReverseGeocodeQuery_QueryCompleted;
MyReverseGeocodeQuery.QueryAsync();
}
}
catch (Exception)
{ // Do something }
}
private void ReverseGeocodeQuery_QueryCompleted(object sender,
QueryCompletedEventArgs<IList<MapLocation>> e)
{
if (e.Error == null)
{
if (e.Result.Count > 0)
{
MapAddress address = e.Result[0].Information.Address;
lblCurrAddress.Text = address.Street + " " + address.HouseNumber + ",\r" +
address.PostalCode + " " + address.City + ",\r" +
address.Country + " (" + address.CountryCode + ")";
}
}
}
}
And the Methode 'SendSms()':
private void SendSms()
{
SmsComposeTask smsComposeTask = new SmsComposeTask();
smsComposeTask.To = "0123456";
smsComposeTask.Body = "Current position: \rLat = " + lblLatitude.Text +
", Long = " + lblLongitude.Text +
"\r" + lblCurrAddress.Text;
// -> The TextBoxes are still empty!
smsComposeTask.Show();
}
The problem is, that all these TextBoxes (lblLatitude, lblLongitude, lblCurrAddress) are still empty when the method SendSms() sets the SmsComposeTask object.
I have to ensure that the TextBoxes are already set BEFORE the method SendSms() starts.
You should almost never mark a method async void unless it's a UI event handler. You're calling an asynchronous method without waiting for it to end. You are basically calling those 2 methods in parallel, so it's clear why the coordinates aren't available.
You need to make GetCurrentCoordinate return an awaitable task and await it, like this:
private async Task GetCurrentCoordinateAsync()
{
//....
}
private async void btnSendSms_Click(object sender, RoutedEventArgs e)
{
await GetCurrentCoordinateAsync();
// You'll get here only after the first method finished asynchronously.
SendSms();
}
This is one of the primary reasons you should avoid async void. void is a very unnatural return type for async methods.
First, make your GetCurrentCoordinate an async Task method instead of async void. Then, you can change your click handler to look like this:
private async void btnSendSms_Click(object sender, RoutedEventArgs e)
{
await GetCurrentCoordinate();
SendSms();
}
Your click handler is async void only because event handlers have to return void. But you should really strive to avoid async void in all other code.
There two things you're doing wrong here:
Using void returning async methods when you need to await on them. This is bad because you can't await on execution of these methods and should only be used when you can't make the method return Task or Task<T>. That's why you're not seeing anything on the text boxes when SendSmsis called.
Mixing UI and non-UI code. You should transfer data between UI and non-UI code to avoid tight coupling between code with different responsibilities. IT also makes it easy to read and debug the code.
ReverseGeocodeQuery does not have an awaitable async API but you can easily make your own:
private async Task<IList<MapLocation>> ReverseGeocodeQueryAsync(GeoCoordinate geoCoordinate)
{
var tcs = new TaskCompletionSource<IList<MapLocation>>();
EventHandler<QueryCompletedEventArgs<IList<MapLocation>>> handler =
(s, e) =>
{
if (e.Cacelled)
{
tcs.TrySetCancelled();
}
else if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else
{
tcs.TrySetResult(e.Result);
}
};
var query = new ReverseGeocodeQuery{ GeoCoordinate = geoCoordinate };
try
{
query.QueryCompleted += handler;
query.QueryAsync();
return await tcs.Task;
}
finally
{
query.QueryCompleted -= handler;
}
}
This way you'll get full cancellation and error support.
Now let's make the retrieval of the geo coordinate information all in one chunk:
private async Task<Tuple<Geocoordinate, MapLocation>> GetCurrentCoordinateAsync()
{
try
{
var geolocator = new Geolocator
{
DesiredAccuracy = PositionAccuracy.High
};
var currentPosition = await geolocator.GetGeopositionAsync(
TimeSpan.FromMinutes(1),
TimeSpan.FromSeconds(10))
.ConfigureAwait(continueOnCapturedContext: false);
var currentCoordinate = currentPosition.Coordinate;
var mapLocation = await this.ReverseGeocodeQueryAsync(
new GeoCoordinate(
currentCoordinate.Latitude,
currentCoordinate.Longitude));
return Tuple.Create(
currentCoordinate,
mapLocation.FirstOrDefault());
}
catch (Exception)
{
// Do something...
return Tuple.Create(null, null);
}
}
Now the button eventnt handler becomes much more readable:
private void btnSendSms_Click(object sender, RoutedEventArgs e)
{
var info = await GetCurrentCoordinate();
if (info.Item1 != nuil)
{
lblLatitude.Text = info.Item1.Latitude.ToString("0.000");
lblLongitude.Text = info.Item1.Longitude.ToString("0.000");
}
if (info.Item2 != null)
{
var address = info.Item2.Information.Address;
lblCurrAddress.Text = string.Format(
"{0} {1},\n{2} {3},\n{4} ({5})",
address.Street,
address.HouseNumber,
address.PostalCode,
address.City,
address.Country,
address.CountryCode);
}
SendSms(info.Item1, info.Item2);
}
Does this make sense?