Table Section URL Calling in Xamarin - c#

I would like to display two section tableviewcontroller. I have two urls to call to get json object and tabulate them in tableviewcontroller. I could not able to figure out how to Create a single TableSource that can handle two different instances of List.
I am posting here full source code here. I will be glad if any one able to help me out in this problem, either share a link that would be useful, or share the code.
public override nint RowsInSection(UITableView tableview, nint section)
returns me an "object reference not set" error. It seems to me that once I fecth data from the first url and it tries to tabulate tableview, however, the other URL data at that time may not be ready.
namespace TPM
{
partial class IViewController : UIViewController
{
public List<HumanTask> cTasks;
public List<HumanTask> aTasks;
public InboxViewController (IntPtr handle) : base (handle)
{
this.Title = "Inside";
}
public override void ViewDidLoad()
{
base.ViewDidLoad ();
GInbox ();
CInbox ();
}
public void CInbox()
{
var client = new RestClient ("URL");
client.Authenticator = new HttpBasicAuthenticator ("admin", "admin");
var request = new RestRequest ("other part URL");
request.AddHeader ("Accept", "application/json");
request.AddHeader ("Content-Type", "application/json");
client.ExecuteAsync (request, response => {
cTasks = Newtonsoft.Json.JsonConvert.DeserializeObject<List<HTask>> (response.Content);
InvokeOnMainThread (() => {
TableView.Source= new TableSource(cTasks,this,0);
TableView.ReloadData();
});
});
}
public void GInbox()
{
var client = new RestClient ("URL");
client.Authenticator = new HttpBasicAuthenticator ("admin", "admin");
var request = new RestRequest ("the rest URL");
request.AddHeader ("Accept", "application/json");
request.AddHeader ("Content-Type", "application/json");
client.ExecuteAsync (request, response => {
aTasks = Newtonsoft.Json.JsonConvert.DeserializeObject<List<HTask>> (response.Content);
InvokeOnMainThread (() => {
TableView.Source= new TableSource(aTasks,this,1);
TableView.ReloadData();
});
});
}
public class TableSource:UITableViewSource{
List<HTask>cTableItems;
List<HTask>aTableItems;
int defi;
string cellIdentifier="TableCell";
private IViewController iv;
public TableSource (List<HTask>items, IViewController vc, int def)
{
if(def==0)
{
cTableItems=items;
}
else if(def==1)
{
aTableItems=items;
}
iv=vc;
defi=def;
}
public override nint NumberOfSections (UITableView tableView)
{
return 2;
}
public override nfloat GetHeightForHeader (UITableView tableView, nint section)
{
if(section==0){
return 40;
}
if(section == 1) {
return 40;
}
return 40;
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
UIView headerView = new UIView(new RectangleF (0, 0, (float)UIScreen.MainScreen.Bounds.Width, (float)tableView.SectionHeaderHeight));
headerView.BackgroundColor = UIColor.Black;
UILabel sectionTitle = new UILabel( new RectangleF(10, (float)((headerView.Frame.Height - 22) / 2), 200, 24));
sectionTitle.Font = UIFont.BoldSystemFontOfSize(22);
sectionTitle.TextColor = UIColor.White;
sectionTitle.TextAlignment = UITextAlignment.Right;
if (section == 0) {
sectionTitle.Text = "Cmed";
}
else if (section == 1) {
sectionTitle.Text = "Asy";
}
headerView.AddSubview(sectionTitle);
return headerView;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
if (section == 0)
return cTableItems.Count;
else
return aTableItems.Count;
}
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(cellIdentifier);
if (cell == null)
cell = new UITableViewCell(UITableViewCellStyle.Subtitle, cellIdentifier);
if (indexPath.Section == 0) {
cell.TextLabel.Text = cTableItems [indexPath.Row].displayName;
cell.DetailTextLabel.Lines = 3;
cell.DetailTextLabel.Text = "Process ID:" + cTableItems [indexPath.Row].processInstanceId + "\n" + DateTime.Parse (Convert.ToDateTime (cTableItems [indexPath.Row].createdOn).ToShortTimeString ());
if (cTableItems [indexPath.Row].priority == 0) {
cell.ImageView.Image = UIImage.FromFile ("Images/green.png");
}
else if (cTableItems [indexPath.Row].priority == 1) {
cell.ImageView.Image = UIImage.FromFile ("Images/yellow.png");
}
else if (cTableItems [indexPath.Row].priority == 2) {
cell.ImageView.Image = UIImage.FromFile ("Images/red.png");
}
}
else if (indexPath.Section == 1) {
cell.TextLabel.Text = assignTableItems [indexPath.Row].displayName;
cell.DetailTextLabel.Lines = 3;
cell.DetailTextLabel.Text = "Process ID:" + aTableItems [indexPath.Row].processInstanceId + "\n" + DateTime.Parse (Convert.ToDateTime (aTableItems [indexPath.Row].createdOn).ToShortTimeString ());
if (aTableItems [indexPath.Row].priority == 0) {
cell.ImageView.Image = UIImage.FromFile ("Images/green.png");
}
else if (aTableItems [indexPath.Row].priority == 1) {
cell.ImageView.Image = UIImage.FromFile ("Images/yellow.png");
}
else if (aTableItems [indexPath.Row].priority == 2) {
cell.ImageView.Image = UIImage.FromFile ("Images/red.png");
}
}
cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
return cell;
}
public override nfloat GetHeightForRow (UITableView tableView, Foundation.NSIndexPath indexPath)
{
return 60;
}
}
}
}
Based on Jason answer: I am getting the following error:
Second update based on Jason answer:
Third update based on Jason answer:

Instead of passing the two sets of data in the constructor, pass them as properties - this allows you to set them after the Source has been created, which is useful since you are getting the data asynchronously.
public class TableSource:UITableViewSource{
public List<HTask> cTableItems;
public List<HTask> aTableItems;
string cellIdentifier="TableCell";
private IViewController iv;
public TableSource (IViewController vc)
{
aTableItems = new List<HTask>();
cTableItems = new List<HTask>();
iv=vc;
}
Then create your Source once when you create the VC
public override void ViewDidLoad()
{
base.ViewDidLoad ();
TableView.Source = new TableSource(this);
GInbox ();
CInbox ();
}
Finally, when you get the data don't recreate your Source, just update it with the data: (repeat the same thing for your other dataset)
public void CInbox()
{
var client = new RestClient ("URL");
client.Authenticator = new HttpBasicAuthenticator ("admin", "admin");
var request = new RestRequest ("other part URL");
request.AddHeader ("Accept", "application/json");
request.AddHeader ("Content-Type", "application/json");
//request.Method = (string)"GET";
client.ExecuteAsync (request, response => {
cTasks = Newtonsoft.Json.JsonConvert.DeserializeObject<List<HTask>> (response.Content);
InvokeOnMainThread (() => {
((TableSource)this.TableView.Source).cTableItems = cTasks;
TableView.ReloadData();
});
});

Related

getter setter can't change textBox.Text control value in Winform c#

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();
}

Is there a way to pass list from android project to MainPage in xamarin.forms project

I have CustomMapRenderer class in my android project in which when you press a marker on the map the list is filling from database with this code:
using System;
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Widget;
using MaritsaTundzhaForecast;
using MaritsaTundzhaForecast.Models;
using MaritsaTundzhaForecast.Droid;
using MySqlConnector;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MaritsaTundzhaForecast.Droid
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
public CustomMapRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
}
}
protected override void OnMapReady(GoogleMap map)
{
base.OnMapReady(map);
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
}
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
//marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.green));
var custom = customPins.Where(x => x.Label == pin.Label && x.Address == pin.Address).FirstOrDefault();
if (custom != null)
{
if (custom.AlertLevel == 1)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.green));
}
if (custom.AlertLevel == 2)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.yellow));
}
if (custom.AlertLevel == 3)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.orange));
}
if (custom.AlertLevel == 4)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.red));
}
}
return marker;
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Name.Equals("Xamarin"))
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
CustomPin pin = GetCustomPin(marker);
int CodeNum = pin.CodeNum;
int AlertLevel = pin.AlertLevel;
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
var infoSubtitle2 = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle2);
var infoSubtitle3 = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle3);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
if (infoSubtitle2 != null)
{
infoSubtitle2.Text = "Тревога: (1-4): " + AlertLevel;
}
if (infoSubtitle3 != null)
{
infoSubtitle3.Text = "Код на станция: " + CodeNum;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
public IEnumerable<AlertLevel> DataBaseConnection(int mapCode)
{
string ConnectionString = "server=192.168.0.1;uid=username;port=3389;pwd=password;database=dbName;";
MySqlConnection Conn = new MySqlConnection(ConnectionString);
var listAlert = new List<AlertLevel>();
try
{
Conn.Open();
//replace(2) with mapCode
string query = "CALL Get_Alert_levels_Station(" + mapCode + ");";
MySqlCommand myCommand = new MySqlCommand(query, Conn);
MySqlDataReader myReader;
myReader = myCommand.ExecuteReader();
try
{
while (myReader.Read())
{
var currentData = new AlertLevel()
{
dateForecast = myReader.GetDateTime(0),
levelForecast = myReader.GetInt32(1)
};
listAlert.Add(currentData);
}
}
finally
{
myReader.Close();
Conn.Close();
}
}
catch (Exception ex)
{
Console.WriteLine("Database Connection", "Not Connected ..." + ex.ToString(), "OK");
}
return listAlert;
}
CustomPin GetCustomPin(Marker annotation)
{
string id = annotation.Id.Substring(1);
int mapCode = int.Parse(id);
var result = DataBaseConnection(mapCode);
MessagingCenter.Send(this, "PinSelected", result);
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
}
In the GetCustomPin method I want to pass to MainPage.cs this line of code:
var result = DataBaseConnection(mapCode);
In the same method I try to pass with this line of code:
MessagingCenter.Send(this, "PinSelected", result);
So I delete AlertLevel.cs object in the android project and create a new object AlertLevel.cs in xamarin.forms project. In the MainPage.cs I set using MaritsaTundzhaForecast.Models; to use AlertLevel.cs and with this code I try to receive the method from the android project:
MessagingCenter.Subscribe<CustomMapRenderer, IEnumerable<AlertLevel>>(this, "PinSelected", async (sender, arg) =>
{
// do something here with arg, which is IEnumerable<AlertLevel>
});
But I receive error:
The type or namespace name 'CustomMapRenderer' could not be found (are you missing a using directive or an assembly reference?)
Is there a way to fix that and how can I fill a ListView from MainPage with this result from CustomMapRenderer ?
in MainPage use MessagingCenter to listen for messages from the map control
MessagingCenter.Subscribe<object, IEnumerable<AlertLevel>>(this, "PinSelected", async (sender, arg) =>
{
// do something here with arg, which is IEnumerable<AlertLevel>
});
in your map renderer, send the message with result as a parameter
MessagingCenter.Send<object, IEnumerable<AlertLevel>>(this, "PinSelected", result);

How to send a broadcast or notification from a .NET Standard 2.0 view model?

I have a view model where multiple APIs that support pagination are called as separate Tasks. The view model reside in a .NET Standard 2.0 module which is shared by Xamarin.Android and Xamarin.iOS projects. Whenever I receive a response from any API, I need to send a broadcast if it is Xamarin.Android or notification if it is being called within the iOS project. Corresponding screens will have registered for notification/broadcast so that on receiving them updated data will be fetched from DB and UI will be updated/appended with new data.
public class SyncViewModel : BaseViewModel
{
private int _totalProductPages = 1;
private int _totalCategoryPages = 1;
private int _totalInstProductPages = 1;
private int _totalUserAssignmentPages = 1;
private readonly int _pageSize = 25;
private SyncCommand _command;
private JsonSerializerSettings _settings;
public override void Execute(ICommands commands)
{
_command = (SyncCommand)commands;
_settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
FetchCategories();
FetchProducts();
FetchInstitutionSubscriptionProducts();
}
private void FetchProducts()
{
Task.Run(async () =>
{
ResponseType responseType = ResponseType.PRODUCTS;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while (pageNumber <= _totalProductPages)
{
Response response = await CloudService.GetCloud().FetchAllProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
private void FetchCategories()
{
Task.Run(async () =>
{
ResponseType responseType = ResponseType.CATEGORIES;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while(pageNumber <= _totalCategoryPages)
{
Response response = await CloudService.GetCloud().FetchAllCategories(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
private void FetchInstitutionSubscriptionProducts()
{
if (!App.isLoggedIn)
return;
Task.Run(async () =>
{
ResponseType responseType = ResponseType.INSTITUTION_PRODUCTS;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while (pageNumber <= _totalInstProductPages)
{
Response response = await CloudService.GetCloud().FetchInstitutionSubscriptionProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
[MethodImpl(MethodImplOptions.Synchronized)]
public APIResponseStatus ProcessResponse(Response response, ResponseType type)
{
string data = "";
if (response.status == "error")
{
Error error = JsonConvert.DeserializeObject<Error>(response.data);
data = error.Message;
return APIResponseStatus.ERROR;
}
else if (response.status == "internalError")
{
data = response.data;
return APIResponseStatus.INTERNAL_ERROR;
}
else
{
data = response.data;
Pagination paginationDetails = JsonConvert.DeserializeObject<Pagination>(JObject.Parse(data)["_pagination"].ToString(), _settings);
Console.WriteLine("\n");
Console.WriteLine("SYNC_RESPONSE_LOG");
Console.WriteLine("Response Type: " + type.ToString());
Console.WriteLine("Pagination Details: " + paginationDetails);
Console.WriteLine("\n");
switch (type)
{
case ResponseType.PRODUCTS:
List<Product> products = JsonConvert.DeserializeObject<List<Product>>(JObject.Parse(data)["products"].ToString(), _settings);
DBService.GetDB().InsertOrUpdateProducts(products);
if(paginationDetails != null)
_totalProductPages = paginationDetails.TotalPages;
break;
case ResponseType.CATEGORIES:
SubCategoryList subCategoryList = JsonConvert.DeserializeObject<SubCategoryList>(data, _settings);
List<Category> category = subCategoryList.Categories.ToList();
DBService.GetDB().InsertOrUpdateCategories(category);
if (paginationDetails != null)
_totalCategoryPages = paginationDetails.TotalPages;
break;
case ResponseType.INSTITUTION_PRODUCTS:
List<Product> instProducts = JsonConvert.DeserializeObject<List<Product>>(JObject.Parse(data)["products"].ToString(), _settings);
DBService.GetDB().InsertOrUpdateProducts(instProducts);
if (paginationDetails != null)
_totalInstProductPages = paginationDetails.TotalPages;
break;
}
return APIResponseStatus.SUCCESS;
}
}
}
How to send notification in native iOS and Android platform
You can implement it by using DependencyService
in Forms , Creating the Interface
public interface ISendNotifi
{
void SendNotifi(string content); //you can set the params as you want
}
iOS Implementation
[assembly: Dependency(typeof(SendNotifiImplementation))]
namespace xxx.iOS
{
public class SendNotifiImplementation: ISendNotifi
{
public SendNotifiImplementation() { }
void SendNotifi(string content)
{
// send notification
}
}
}
Android Implementation
[assembly: Dependency(typeof(SendNotifiImplementation))]
namespace xxx.Droid
{
public class SendNotifiImplementation: ISendNotifi
{
public SendNotifiImplementation(Context context):base(context) { }
void SendNotifi(string content)
{
// send notification
}
}
}
Don't forgrt to register with the Apple and Android Push Notification Service first. And you can call the method in viewmodel and implement it in iOS and Android platform.Such as
...
while (pageNumber <= _totalInstProductPages)
{
Response response = await CloudService.GetCloud().FetchInstitutionSubscriptionProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
DependencyService.Get<ISendNotifi>().SendNotifi("pageNumber++");
}
...

How to make a request asynchronously in c# (xamarin)?

I used to work with browser-based applications. for example Angular simple repository.
function getSomeData(params) {
...
return $http({
url: conf.urlDev + 'some/rest-url',
method: "GET",
params: params,
cache: true
}).then(getDataComplete);
function getDataComplete(response) {
return response.data;
}
}
How it will look the same in c# (XAMARIN for example)?
i try :
public class BaseClient
{
protected Http _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient ()
{
_client = new Http(new HttpClientHandler());
}
public string Path
{
set
{
_urlObj = value;
}
}
public async Task<Result<IList<T>>>getList<T>(Dictionary<string,object> parametrs = null)
{
if (parametrs != null)
{
foreach(KeyValuePair<string, object> keyValue in parametrs)
{
_urlObj = _urlObj.SetQueryParam(keyValue.Key, keyValue.Value);
}
}
var response = await _client.GetAsync(_urlObj.ToString());
if (response.IsSuccessStatusCode)
{
return new Result<IList<T>>()
{
Success = true,
Value = JsonConvert.DeserializeObject<IList<T>>(await response.Content.ReadAsStringAsync())
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.ReasonPhrase,
Success = false
};
return error;
}
}
in my service:
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
_client.Path = _config.NewsPath;
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.getList<News>(dict);
return res.Value;
}
and im ny viewmodel
foreach (var item in await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id))
{
NewsList.Add(item);
}
Is his query executed synchronously ?
How do I make it an asynchronous?
First of all I would really encourage you to use RestSharp, it really simplifies making HTTP requests and deserialise them. Add a RestSharp nuget package to your project. Here is how your code will look like using RestSharp.
public class BaseClient
{
protected IRestClient _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient()
{
_client = new RestClient();
}
public async Task<Result<IList<T>>> GetList<T>(string path, Dictionary<string, object> parametrs = null)
{
var request = new RestRequest(path, Method.GET);
if (parametrs != null)
{
foreach (var keyValue in parametrs)
{
request.AddQueryParameter(keyValue.Key, keyValue.Value);
}
}
var response = await _client.Execute<List<T>>(request);
if (response.IsSuccess)
{
return new Result<IList<T>>()
{
Success = true,
Value = response.Data
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.StatusDescription,
Success = false
};
return error;
}
}
}
In your service
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.GetList<News>(_config.NewsPath, dict);
return res.Value;
}
And in your viewmodel
var news = await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id);
foreach (var item in news)
{
NewsList.Add(item);
}
This will be 100% asynchronous.

Xamarin C# Two UITableViewCells in one UITableView

I am trying to put two different UITableViewCell's into one UITableView however when I try to load data into a TableSource It only allows me to send one List<> at a time. However I need two List<>'s for both my Cells to display. The two Lists in my TableSource.cs class are public variables called instaData and faceData. When I run Individual requests for getting instaData and faceData it works flawlessly. Better shown then explained:
Request for InstaData
var client = new RestClient ("https://api.instagram.com/v1");
client.ExecuteAsync (request, response => {
var rootObject = JsonConvert.DeserializeObject<RootObject> (response.Content);
InstagramObject.next_url = rootObject.pagination.next_url.ToString();
FLDTRequest.instagramDataCopy = rootObject.data;
table.InvokeOnMainThread (() => {
table.Source = new TableSource(stream);
((TableSource)table.Source).instaData = rootObject.data;
table.ReloadData ();
});
});
Request for FaceData
var client = new RestClient ("https://graph.facebook.com/");
client.ExecuteAsync (request, response => {
var rootObject = JsonConvert.DeserializeObject<Floadt.Core.Facebook.RootObject> (response.Content);
FLDTRequest.facebookDataCopy = rootObject.data;
table.InvokeOnMainThread (() => {
table.Source = new TableSource(stream);
((TableSource)table.Source).faceData = rootObject.data;
table.ReloadData ();
});
});
Basically I call both methods when I want to get both data but I usually get a error from the Method I call last saying that that Object is not a reference. For Example: If i call faceData request last it would say Object is not a reference. Here is my TableSource Class:
public class TableSource : UITableViewSource
{
StreamViewController controller;
public List<Datum> instaData { get; set; }
public List<string> twitData { get; set; }
public List<Floadt.Core.Facebook.Datum> faceData { get; set; }
public TableSource(StreamViewController stream)
{
controller = stream;
}
public override int RowsInSection (UITableView tableview, int section)
{
return faceData.Count;
}
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
if (indexPath.Row % 2 == 0) {
return 340;
} else {
return 436;
//return 210;
}
}
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
if (indexPath.Row % 2 == 0) {
NetworkCheck netCheck = new NetworkCheck ();
netCheck.runCheck ();
// bool log = netCheck.anyLoggedIn ();
if (tableView.ContentSize.Height - UIScreen.MainScreen.Bounds.Height <= tableView.ContentOffset.Y) {
BTProgressHUD.Show ("Getting more...");
FLDTRequest.getInstagramNextPage (tableView);
BTProgressHUD.Dismiss ();
}
var cell = tableView.DequeueReusableCell (InstagramCell.Key) as InstagramCell;
if (cell == null) {
cell = new InstagramCell ();
var views = NSBundle.MainBundle.LoadNib ("InstagramCell", cell, null);
cell = Runtime.GetNSObject (views.ValueAt (0)) as InstagramCell;
}
//Datum h = instaData [indexPath.Row/2];
//cell.BindData (h);
return cell;
} else {
NetworkCheck netCheck = new NetworkCheck ();
netCheck.runCheck ();
// bool log = netCheck.anyLoggedIn ();
if (tableView.ContentSize.Height - UIScreen.MainScreen.Bounds.Height <= tableView.ContentOffset.Y) {
BTProgressHUD.Show ("Getting more...");
FLDTRequest.getInstagramNextPage (tableView);
BTProgressHUD.Dismiss ();
}
var cell = tableView.DequeueReusableCell (FacebookCell.Key) as FacebookCell;
if (cell == null) {
cell = new FacebookCell ();
var views = NSBundle.MainBundle.LoadNib ("FacebookCell", cell, null);
cell = Runtime.GetNSObject (views.ValueAt (0)) as FacebookCell;
}
var fbPhotoCell = tableView.DequeueReusableCell (FacebookPhotoCell.Key) as FacebookPhotoCell;
if (fbPhotoCell == null) {
fbPhotoCell = new FacebookPhotoCell ();
var views = NSBundle.MainBundle.LoadNib ("FacebookPhotoCell", cell, null);
fbPhotoCell = Runtime.GetNSObject (views.ValueAt (0)) as FacebookPhotoCell;
}
Floadt.Core.Facebook.Datum f = faceData [indexPath.Row/2];
fbPhotoCell.BindData (f);
return fbPhotoCell;
}
}
}
It looks like you are trying to set table.Source twice, once for each List? You need to merge your lists into a single data source, and create a UITableViewCell that can visualize both data types.
1)Drag and Drop UITableView to ViewController and add constraints.
2)Add two Prototype cells and Associate with Two UITableViewCells.
3) Set up UITableViewSource(Combines UITableVIewDataSource and UITableViewDelegate).
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
this.sampleTableView.Source = new SampleTableViewSource ();
}
Add Source class to ViewController.
//Table Source
public class SampleTableViewSource : UITableViewSource
{
string CellIdentifier = "sampleTableViewCellID";
string CellIdentifier2 = "sampleTableViewCell2ID";
public override nint RowsInSection(UITableView tableview, nint section)
{
return 2;
}
public override nint NumberOfSections (UITableView tableView)
{
return 1;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = new UITableViewCell ();
//---- if there are no cells to reuse, create a new one
if (indexPath.Row == 0)
{
cell = tableView.DequeueReusableCell (CellIdentifier) as SampleTableViewCell;
}
else if(indexPath.Row == 1 ) {
cell = tableView.DequeueReusableCell (CellIdentifier2) as sampleTableViewCell2;
}
return cell;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow (indexPath, true);
}
}

Categories

Resources