bit of a noob when it comes to async operations but I am having some trouble with a ObservableCollection and not sure if the problem is because it is in an async method or not. When it tries to add the delegate it crashes with a System.AccessViolationException error... Here's the code:
public partial class ContactsList : PhoneApplicationPage
{
static ObservableCollection<Contact> dataSource { get; set; }
public ContactsList()
{
InitializeComponent();
}
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
dataSource.CollectionChanged += this.dataSource_CollectionChanged;
var tasks = new List<Task>();
for (int i = 1; i < 6; i++)
{
tasks.Add(GetContacts(i.ToString()));
}
await Task.WhenAll(tasks);
}
private void dataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
List<AlphaKeyGroup<Contact>> DataSource = AlphaKeyGroup<Contact>.CreateGroups(dataSource, System.Threading.Thread.CurrentThread.CurrentUICulture, (Contact s) => { return s.Name; }, true);
ContactsLList.ItemsSource = DataSource;
}
public async Task GetContacts(string page)
{
try
{
string strCredidentials = Globals.APIKey;
string strAuthorization = Convert.ToBase64String(Encoding.UTF8.GetBytes(strCredidentials));
RestClient client = new RestClient(Globals.myURL);
RestRequest request = new RestRequest("/contacts.json?state=all&page=" + page);
request.RequestFormat = DataFormat.Json;
request.AddHeader("Authorization", "Basic " + strAuthorization);
request.Method = Method.GET;
var rslt = client.ExecuteAsync(request, (r) =>
{
if (r.ResponseStatus == ResponseStatus.Completed)
{
if (r.Content == "" || r.Content == " ")
{
MessageBox.Show("No Contacts Found");
}
else
{
dataSource = new ObservableCollection<Contact>();
var conts = JsonConvert.DeserializeObject<List<ContactWrapper>>(r.Content);
foreach (ContactWrapper cont in conts)
{
try
{
string name = cont.User.Name;
string email = cont.User.Email;
string mobile = cont.User.Mobile;
string phone = cont.User.Phone;
string jobtitle = cont.User.JobTitle;
dataSource.Add(new Contact("", "", "", "", "", email, "", jobtitle, mobile, name, phone, ""));
}
catch { }
}
}
}
});
} catch {}
}
}
}
In the GetContacts method the dataSource collection gets added to, so the idea is that GetContacts is called 6 times and each time the return data is added to the dataSource.
When that happens I want to call dataSource_CollectionChanged to update the bound longlistselector on the XAMl page.
Can someone tell me where I am going wrong?
Thanks
I've build a simple example basing on your code and it seems that your code is all right.
I think the problem may be in your Task GetContacts(string page) in // Do stuff here - check if you are not trying to change something within UI if so, do it by using Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//do UI stuff here;
});
EDIT - it turned out in the discussion that the Collection hadn't beed initialized on start.
Related
Why textBox3.text do not shows value _TextBoxRequestMsg. MessageBox opens and shows _TextBoxRequestMsg value OK, console prints too.
public partial class F_Main : Form
{
private string _TextBoxRequestMsg;
public string TextBoxRequestMsg
{
get { return textBox3.Text; }
set
{
_TextBoxRequestMsg = value;
MessageBox.Show(_TextBoxRequestMsg);
Console.WriteLine(_TextBoxRequestMsg);
textBox3.Text = _TextBoxRequestMsg;
}
}
public F_Main()
{
InitializeComponent();
}
}
public class CdataController : ApiController
{
F_Main mainForm = new F_Main();
public async Task<HttpResponseMessage> PostPayloadEventsOp(string SN, string table, string OpStamp)
{
using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
{
contentStream.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(contentStream))
{
string results = sr.ReadToEnd();
mainForm.TextBoxRequestMsg = results;
}
}
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent("OK", System.Text.Encoding.UTF8);
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(2)
};
return response;
}
}
Your question states that your goal is to change textBox.Text control value in Winform and your code indicates that you want to do this by processing an HttpResponseMessage. Consider that the Form that owns the textBox3 control could await the response so that it can meaningfully process its content and assign the value to the text box.
For a minimal example, mock the API request:
public class MockCdataController : ApiController
{
public async Task<HttpResponseMessage> MockPostPayloadEventsOp(string SN, string table, string OpStamp)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync("https://stackoverflow.com/q/75310027/5438626");
response.Content = new StringContent("OK", System.Text.Encoding.UTF8);
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(2)
};
return response;
}
}
}
The Form that is in possession of textBox3 could invoke something like this:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
buttonPost.Click += onPost;
}
private async void onPost(object? sender, EventArgs e)
{
try
{
UseWaitCursor = true;
buttonPost.BackColor = Color.LightGreen;
var response = await _controller.MockPostPayloadEventsOp("38D6FF5-F89C", "records", "Asgard");
if((response.Headers != null) && (response.Headers.CacheControl != null))
{
textBox3.Text = $"{response.Headers.CacheControl.MaxAge}";
}
}
finally
{
UseWaitCursor = false;
Cursor.Position = new Point(Cursor.Position.X + 1, Cursor.Position.Y);
}
}
MockCdataController _controller = new MockCdataController();
}
I've implemented what I think is a pretty vanilla usage of Realm:
public class MyObj : RealmObject
{
[PrimaryKey]
public string Key { get; set; }
public bool Value { get; set; }
}
then, in my app:
using(Realm r = Realm.GetInstance()) {
var c1 = r.All<MyObj>().Count();
}
which returns zero, as expected.
Then I add an object:
using(Realm r = Realm.GetInstance()) {
r.Write(() =>
{
var obj = new MyObj() { Key = "test", Value = true };
r.Add(obj);
});
}
then reopen it and get the count:
using(r = Realm.GetInstance()) {
var c2 = r.All<MyObj>().Count();
}
and c2 is one, as expected. So far, so good.
But when I close my app, and restart, c1 (the initial count) is zero, not one.
Any idea why?
Sorry,we couldn't see the other code of your app.
But you can refer to an article by entering key words Xamarin.Forms - Working With Realm Database in your browser. It works properly even after restarting the app.
You can also check the full sample here.
The main code is:
public partial class MainPage : ContentPage
{
List<OptionItems> optionItems = new List<OptionItems>();
Student editStudent;
public MainPage()
{
InitializeComponent();
imgBanner.Source = ImageSource.FromResource("XamarinFormsRelam.images.banner.png");
var realmDB = Realm.GetInstance();
List<Student> studentList = realmDB.All<Student>().ToList();
listStudent.ItemsSource=studentList;
}
private void btnAdd_Clicked(object sender, EventArgs e)
{
var realmDB = Realm.GetInstance();
var students= realmDB.All<Student>().ToList();
var maxStudentId = 0;
if (students.Count!=0)
maxStudentId = students.Max(s=>s.StudentID);
Student student = new Student()
{
StudentID = maxStudentId + 1,
StudentName = txtStudentName.Text
};
realmDB.Write(() =>
{
realmDB.Add(student);
});
txtStudentName.Text = string.Empty;
List<Student> studentList = realmDB.All<Student>().ToList();
listStudent.ItemsSource = studentList;
}
private async void listOptions_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var realmDB = Realm.GetInstance();
OptionItems selectedItem = optionList.SelectedItem as OptionItems;
if (selectedItem != null)
{
switch (selectedItem.OptionText)
{
case "Edit":
popupOptionView.IsVisible = false;
popupEditView.IsVisible = true;
editStudent = realmDB.All<Student>().First(b => b.StudentID == selectedItem.StudentId);
txtEditStudentName.Text = editStudent.StudentName;
break;
case "Delete":
var removeStudent = realmDB.All<Student>().First(b => b.StudentID == selectedItem.StudentId);
using (var db = realmDB.BeginWrite())
{
realmDB.Remove(removeStudent);
db.Commit();
}
await DisplayAlert("Success", "Student Deleted", "OK");
popupOptionView.IsVisible = false;
List<Student> studentList = realmDB.All<Student>().ToList();
listStudent.ItemsSource = studentList;
break;
default:
popupOptionView.IsVisible = false;
break;
}
optionList.SelectedItem = null;
}
}
protected override void OnAppearing()
{
base.OnAppearing();
var realmDb = Realm.GetInstance();
}
private void listStudent_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
Student selectedStudent = listStudent.SelectedItem as Student;
if(selectedStudent!=null)
{
optionItems.Add(new OptionItems { OptionText = "Edit",StudentId=selectedStudent.StudentID});
optionItems.Add(new OptionItems { OptionText = "Delete", StudentId = selectedStudent.StudentID });
optionItems.Add(new OptionItems { OptionText = "Cancel"});
optionList.ItemsSource = optionItems;
popupOptionView.IsVisible = true;
}
}
private void Button_Clicked(object sender, EventArgs e)
{
popupEditView.IsVisible = false;
}
private async void Button_Clicked_1(object sender, EventArgs e)
{
var realmDB = Realm.GetInstance();
var selectedStudent = realmDB.All<Student>().First(b => b.StudentID == editStudent.StudentID);
using (var db = realmDB.BeginWrite())
{
editStudent.StudentName = txtEditStudentName.Text;
db.Commit();
}
await DisplayAlert("Success", "Student Updated","OK");
txtEditStudentName.Text = string.Empty;
popupEditView.IsVisible = false;
}
}
Are you deploying on a physical device? If that's the case, it's likely due to the automatic backup and restore functionality built into Android - when you deploy the app, it treats it as an install and restores the last known state.
You can read about it here: An Android app remembers its data after uninstall and reinstall but the tl;dr is - go to settings and turn off "Automatic Restore" or update your manifest to instruct Android not to backup the data.
I've got a problem with my Xamarin App. The App uses a custom API to my website. I have not much experience in async/await methods.
The following code shows the App.xaml.cs:
public partial class App : Application
{
public static bool IsUserLoggedIn { get; set; }
public static UserModel currentLoggedInUser { get; set; }
public static List<ActionTypeModel> listTypes;
public static List<ActionSubTypeModel> listSubtypes;
public static List<UserModel> listFriends;
public static List<List<ActionModel>> listActiveActions;
public static List<ActionModel> listLastAction;
public App()
{
this.InitializeComponent();
APIHelper.InitializeClient();
StartApp().Wait();
}
private async Task StartApp()
{
//// DEBUG
//MyAccountStorage.Logout();
string username = await MyAccountStorage.GetUsername().ConfigureAwait(false);
string password = await MyAccountStorage.GetPassword().ConfigureAwait(false);
string user_id = await MyAccountStorage.GetId().ConfigureAwait(false);
if (username != null && password != null)
{
currentLoggedInUser = new UserModel();
if (user_id != null)
currentLoggedInUser.user_id = Convert.ToInt32(user_id);
currentLoggedInUser.username = username;
currentLoggedInUser.password = password;
bool isValid = false;
isValid = await AreCredentialsCorrect(0, currentLoggedInUser.username, currentLoggedInUser.password).ConfigureAwait(false);
if (isValid)
{
IsUserLoggedIn = true;
await FillLists().ConfigureAwait(false);
MainPage = new NavigationPage(await MyPage.BuildMyPage().ConfigureAwait(false));
}
else
{
IsUserLoggedIn = false;
MainPage = new NavigationPage(await LoginPage.BuildLoginPage().ConfigureAwait(false));
}
}
else
{
IsUserLoggedIn = false;
MainPage = new NavigationPage(await LoginPage.BuildLoginPage().ConfigureAwait(false));
}
}
private async Task FillLists()
{
listFriends = await DataControl.GetFriends(App.currentLoggedInUser.user_id, App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
if (listFriends == null)
listFriends = new List<UserModel>();
listTypes = await DataControl.GetTypes(App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
if (listTypes == null)
listTypes = new List<ActionTypeModel>();
listActiveActions = new List<List<ActionModel>>();
for (int i = 0; i < listTypes.Count; i++)
listActiveActions.Add(await DataControl.GetActiveActions(listTypes[i].action_type_id, currentLoggedInUser.user_id, currentLoggedInUser.username, currentLoggedInUser.password).ConfigureAwait(false));
listSubtypes = await DataControl.GetSubtypes(App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
if (listSubtypes == null)
listSubtypes = new List<ActionSubTypeModel>();
listLastAction = await DataControl.GetLastAction(App.currentLoggedInUser.user_id, App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
if (listLastAction == null)
listLastAction = new List<ActionModel>();
}
public static async Task<bool> AreCredentialsCorrect(int type, string user, string pass, string nick = "", string email = "")
{
List<UserModel> listUsers;
if (type == 1)
listUsers = await DataControl.CheckCredentials(1, user, pass, nick, email).ConfigureAwait(false);
else
listUsers = await DataControl.CheckCredentials(0, user, pass).ConfigureAwait(false);
if (listUsers != null)
if (listUsers.Any())
{
currentLoggedInUser = listUsers.First();
currentLoggedInUser.password = pass;
return true;
}
return false;
}
}
I have the API in DataControl.cs:
public static async Task<List<UserModel>> CheckCredentials(int type, string username, string pass, string email = "", string nickname = "")
{
string password = APIHelper.GetHashSha256(pass);
string url = string.Empty;
if (type == 0)
url = APIHelper.ApiClient.BaseAddress + "/account/login.php?username=" + username + "&password=" + password;
if (type == 1)
{
string nick = string.Empty;
if (string.IsNullOrEmpty(nickname) == false)
nick = "&nickname=" + nickname;
url = APIHelper.ApiClient.BaseAddress + "/account/signup.php?username=" + username + "&password=" + password + "&email=" + email + nick;
}
if (string.IsNullOrEmpty(url))
return null;
using (HttpResponseMessage response = await APIHelper.ApiClient.GetAsync(url).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
List<UserModel> listUsers = JsonConvert.DeserializeObject<List<UserModel>>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
return listUsers;
}
else
return null;
}
}
That's one of the different async methods. When I leave out the ConfigureAwait(false), I run into a deadlock. When I add it to the code I run into an error.
Could you please help me.
As #GSerg already wrote, you have to restructure the code. The App constructor must set the MainPage to some page. That one can be empty saying something like "Loading data".
Then you can start a background Task which retrieves the data you need.
When the data has been loaded, then you can update your page with the newly loaded data. But UI updates always have to happen on the UI thread. This is the same on all platforms. So you have to switch back with Device.BeginInvokeOnMainThread(...).
public App()
{
InitializeComponent();
APIHelper.InitializeClient();
MainPage = new LoadingPage();
// start a background thread
Task.Run(async () =>
{
try
{
await StartApp(); // load the data
// go back to the main thread
Device.BeginInvokeOnMainThread(() =>
{
// replace the MainPage with one which shows the loaded data
MainPage = new DataPage();
});
}
catch (Exception ex)
{
// handle the exception
}
});
}
You can use Task.Run(
//call your async method here
);
So in your case:
Task.Run(Startup);
I created a ContentDialog that contains a TextBox where the user enter a SMS code. When this code is right, I need the user navigate to a Home Page.
The problem is Navigate.Frame does not exist in this context.
Please, What Am I doing wrong?
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
String _confirmaSms = "https://example.com";
RestClient client = new RestClient();
string msisdn = PrimaryButtonCommandParameter.ToString();
string codigoConfirmacao = txtCodigoConfirmacao.Text;
Usuario usuario = new Usuario()
{
msisdn = msisdn,
codesms = codigoConfirmacao
};
string output = JsonConvert.SerializeObject(usuario);
//Debug.WriteLine(output);
string response = await client.RestConnection(_confirmaSms, "POST", output);
JObject responseObj = JObject.Parse(response);
JObject resultObj = (JObject)responseObj["result"];
string result = resultObj["codesms"].ToString();
if (usuario.codesms.ToString() == result && result != null)
{
//code to navigate
}
else
{
//code to navigate to other page
}
Debug.WriteLine(resultObj["ltoken"]);
}
Finally, after some research, I found an answer.
Have to instantiate the RootFrame at the beginning of class.
public sealed partial class ConfirmaSMS : ContentDialog
{
Frame rootFrame = Window.Current.Content as Frame;
public ConfirmaSMS()
{
this.InitializeComponent();
}
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
String _confirmaSms = "https://www.Example.com";
RestClient client = new RestClient();
string msisdn = PrimaryButtonCommandParameter.ToString();
string codigoConfirmacao = txtCodigoConfirmacao.Text;
string output = JsonConvert.SerializeObject(usuario);
//Debug.WriteLine(output);
string response = await client.RestConnection(_confirmaSms, "POST", output);
JObject responseObj = JObject.Parse(response);
JObject resultObj = (JObject)responseObj["result"];
string result = resultObj["codesms"].ToString();
if (usuario.codesms.ToString() == result && result != null)
{
Debug.WriteLine(result.ToString());
rootFrame.Navigate(typeof(HomePage));
}
else
{
confirmaSMS.SecondaryButtonCommandParameter = false;
confirmaSMS.Hide();
}
Debug.WriteLine(resultObj["ltoken"]);
}
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
}
i'm working on a windows phone application using pivot view to preview data for the user, the data comes from a web service, then i put it in List then i add the item to the pivot view
but when i call the web service the view doesn't wait till i get the data from the server to add to the view and the view adds nothing , here is my code
public class downloads : List<Downloaded>
{
List<string> downoladedList = new List<string>();
public downloads()
{
BuildCollection();
}
//private const string IMG_PATH = "../Images/";
public ObservableCollection<Downloaded> DataCollection { get; set; }
public ObservableCollection<Downloaded> BuildCollection()
{
// int x=0;
Downloaded downObject = new Downloaded();
ServiceReference1.Service1Client service = new ServiceReference1.Service1Client();
service.GetDownloadsCompleted += new EventHandler<ServiceReference1.GetDownloadsCompletedEventArgs>(GetDownLoads);
System.Threading.Thread.Sleep(100000);
service.GetDownloadsAsync(20019);
DataCollection = new ObservableCollection<Downloaded>();
foreach (var elem in downoladedList)
{
string[] elemProp = new string[8];
elemProp = elem.Split('=');
if (elemProp[3] == "1")
elemProp[3] = "downloaded";
else
elemProp[3] = "in progress";
DataCollection.Add(new Downloaded(elemProp[1], elemProp[3], "test.png"));
}
return DataCollection;
}
public void GetDownLoads(object sender, ServiceReference1.GetDownloadsCompletedEventArgs e)
{
try
{
downoladedList = e.Result.ToList<string>();
}
catch (Exception ee)
{
}
}
}
You cannot call thread.sleep. This will block entire UI thread.
Declare DataCollection = new ObservableCollection();
outside scope.
You should put all your code on completed like this :
public void GetDownLoads(object sender, ServiceReference1.GetDownloadsCompletedEventArgs e)
{
try
{
downoladedList = e.Result.ToList<string>();
foreach (var elem in downoladedList)
{
string[] elemProp = new string[8];
elemProp = elem.Split('=');
if (elemProp[3] == "1")
elemProp[3] = "downloaded";
else
elemProp[3] = "in progress";
DataCollection.Add(new Downloaded(elemProp[1], elemProp[3], "test.png"));
}
}
catch (Exception ee)
{
}
}