I am new to xamarin.ios wants to sort tableview in alphabetical order.ie every alphabet should display all the books starting with respective letter.I am able to list the details in tableview..need to sort and update cells
public class BooklistTableSourceClass : UITableViewSource
{
public List<Booklist> Bookdata { get; set; }
BooklistViewController parentcontroller;
NSString cellIdentifier = new NSString("BooklistCell");
public BooklistTableSourceClass(List<Booklist> listdata,BooklistViewController callingcontroller)
{
this.Bookdata = listdata;
this.parentcontroller = callingcontroller;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return Bookdata.Count;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = (BooklistCell)tableView.DequeueReusableCell("Bcell_id", indexPath);
var Bookitem = Bookdata[indexPath.Row];
cell.UpdateCell(Bookitem);
return cell;
}
}
Output:
A->section header
1)A1 //book name
2)A2
3)A3
Have you tried the Enumerable.OrderBy Method in LINQ?
Sort the Bookdata first and then pass it as source of tableView instead of UpdateCell in GetCell.
public partial class ViewController : UIViewController
{
public List<Booklist> Bookdata { get; set; }
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
Bookdata = new List<Booklist>();
Bookdata.Add(new Booklist("A2"));
Bookdata.Add(new Booklist("A3"));
Bookdata.Add(new Booklist("A1"));
Bookdata.Add(new Booklist("122"));
Bookdata.Add(new Booklist("C2"));
Bookdata.Add(new Booklist("b2"));
List<Booklist> sortedList = Bookdata.OrderBy(r => r.name).ToList();
//pass the sortedList as source of tableview
//...
}
}
public class Booklist
{
public Booklist(string n){
name = n;
}
public string name;
}
If you want the A->section header, I think there is another list that hold the header data. Or you can pick the first letter of data shows in each section as text shows in section header.
Related
I'm trying to convert a piece of Swift code to c#.
I know c# method extension but in this case the swift code inherit a class:
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return characters.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = characters[indexPath.row]
return cell
}
}
Is it possible to do this in c#?
EDIT: The code comes from here.
EDIT 2: Found the solution in the Xamarin docs.
Xamarin.iOS is based on Objective-C, which will have some difference with swift . In your case , it seems that you want to implement the DataSource of UITableView , right? If so , you could check the following code
1. Create a subclass of UITableViewSource
public class MyTableSource : UITableViewSource {
string[] characters;
string CellIdentifier = "TableCell";
public TableSource (string[] items)
{
characters = items;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return characters.Length;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
string item = characters[indexPath.Row];
//if there are no cells to reuse, create a new one
if (cell == null)
{
cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier);
}
cell.TextLabel.Text = item;
return cell;
}
}
2 . In ViewController
var TableView = new UITableView(View.Bounds);
string[] tableItems = new string[] {"111","222","333","444","555","666"};
table.Source = new MyTableSource(tableItems);
I am trying to implement instant search functionality using edittext.I have just binded the json array response to listview and added edittext at top of listview and trying to filter or search data in listview as user starts to type in edittext below code is used.Please help me. Any kind of suggestion,guidence and help is appreciated.
MainActivity.cs
SetContentView(Resource.Layout.HomeScreen);
tableItems = new List<TableItem>();
var client = new RestClient("http://azurewebsites.net/");
var request = new RestRequest("Service/regionSearch", Method.POST);
request.RequestFormat = DataFormat.Json;
tableItems = client.Execute<List<TableItem>>(request).Data;
listView.Adapter = new HomeScreenAdapter(this, tableItems);
region = FindViewById<TextView> (Resource.Id.viewtext);
area= FindViewById<TextView> (Resource.Id.viewtext2);
_filterText = FindViewById<EditText>(Resource.Id.search);
listView = FindViewById<ListView>(Resource.Id.listView);
_filterText.TextChanged += (object sender, Android.Text.TextChangedEventArgs e) => {
// filter on text changed
var searchTerm = _filterText.Text;
};
listView.ItemClick += OnListItemClick;
}
protected void OnListItemClick(object sender, Android.Widget.AdapterView.ItemClickEventArgs e)
{
var listView = sender as ListView;
var t = tableItems[e.Position];
// var clickedTableItem = listView.Adapter[e.Position];
Android.Widget.Toast.MakeText(this, clickedTableItem.DDLValue, Android.Widget.ToastLength.Short).Show();
}
HomeScreenAdapter.cs
public class HomeScreenAdapter : BaseAdapter<TableItem> {
List<TableItem> items;
Activity context;
public HomeScreenAdapter(Activity context, List<TableItem> items)
: base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override TableItem this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
// TableItem item = items[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.DDLValue;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.areaMsg;
return view;
}
}
It looks like you're pretty close. The last step is to use the searchTerm to filter out the results in tableItems. The easiest way to do this is to simply create a new HomeScreenAdapter with the filtered list, and set that as the ListView.Adapter. Check out this example code that implements: getting the search text, filtering all of your TableItem instances, and then giving the ListView a new Adapter.
_filterText.TextChanged += (object sender, Android.Text.TextChangedEventArgs e) => {
// filter on text changed
var searchTerm = _filterText.Text;
var updatedTableItems = tableItems.Where(
// TODO Fill in your search, for example:
tableItem => tableItem.Msg.Contains(searchTerm) ||
tableItem.DDLValue.Contains(searchTerm)
).ToList();
var filteredResultsAdapter = new HomeScreenAdapter(this, updatedTableItems);
listView.Adapter = filteredResultsAdapter;
};
Notice the TODO inside of the Where clause. I have no idea how you want to search on your TableItem but once you write your Where clause, this should do what you want.
It looks like your TableItem class is something like this (for reference):
public class TableItem {
public int Id {get; set;}
public string DDLValue {get; set;}
public string Msg {get; set;}
public int Status {get; set;}
}
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);
}
}
I have created a sample on github Github link
When clicking on a row or trying to scroll in the UIPickerView it will crash and i wonder why.
// create a ActionSheet
var actionPickerSheet = new UIActionSheet("Select a Category");
actionPickerSheet.Style = UIActionSheetStyle.BlackTranslucent;
// Create UIpickerView
var catPicker = new UIPickerView(){
Model = new catPickerModel(),
AutosizesSubviews = true,
Hidden = false,
ShowSelectionIndicator = true
};
// show inside view and add the catPicker as subView
actionPickerSheet.ShowInView(View);
actionPickerSheet.AddSubview(catPicker);
// resize both views so it fits smoothly
actionPickerSheet.Frame = new RectangleF(0,100,320,500);
catPicker.Frame = new RectangleF(actionPickerSheet.Frame.X,actionPickerSheet.Frame.Y-25,actionPickerSheet.Frame.Width, 216);
And the Model
private class catPickerModel : UIPickerViewModel
{
public string[] protocolNames = new string[]
{
"Web", "Phone Call", "Google Maps", "SMS", "Email"
};
public override int GetComponentCount(UIPickerView uipv)
{
return 1;
}
public override int GetRowsInComponent( UIPickerView uipv, int comp)
{
//each component has its own count.
int rows = protocolNames.Length;
return(rows);
}
public override string GetTitle(UIPickerView uipv, int row, int comp)
{
//each component would get its own title.
return protocolNames[row];
}
public override void Selected(UIPickerView uipv, int row, int comp)
{
Console.WriteLine("selected:" + row);
}
public override float GetComponentWidth(UIPickerView uipv, int comp)
{
return 300f;
}
}
I have no idea why it keeps crashing, is it some method i am missing in the Model or am i trying to do this in the wrong way ?
Thanks
Try to make your catPicker to a private variable of the class:
namespace TextfieldUIPickerView
{
public partial class TextfieldUIPickerViewViewController : UIViewController
{
private UIPickerView catPicker;
...
Looks like the GC has collected your catPicker and disposed it.
Okay, I've read a couple of questions regarding the use of the PropertyGrid and collections. But, I'm having a difficult time understanding how/if [TypeConverter] will work for me. I've read the little blurb-age that MSDN puts out there, and frankly, it's a bit lacking to this poor, self-taught programmer.
So, here is what I have. First a collection:
[Serializable]
public List<ModuleData> Modules
{ get { return modules; } }
private List<ModuleData> modules;
The object in the collection:
[Serializable]
internal class ModuleData : IEquatable<ModuleData>
{
// simple data class with public properties
// to display in the propgrid control
}
I have a ListView control that contains items describing both ModuleData objects and BatchData objects. When I select a BatchData item from the ListView, the PropertyGrid, as expected, displays the collection editor. Is there a way to limit the collection editor to any ModuleData items listed in the ListView control only? Ideally I would not want a BatchData item (from the ListView) to be added to a BatchData collection - especially since the collection is not 'typed' for BatchData object types.
If any further code samples are requested, I'll be more than happy to edit some snippets in.
For clarity, ModuleData is a custom class that holds data required to instance a class within a specified assembly. All it contains are fields and public/internal properties. What I would like to do is use the collection editor assembled with the property grid control to add ModuleData objects to the BatchData Module collection. The ModuleData objects that are qualified to be added are listed in the ListView control.
EDIT: Removed the : List<ModuleData> inheritance.
UPDATE: If I am going to create a custom collection editor, does that mean I am building my own custom form/dialog? Then basically providing the propertygrid the information to display my custom collection dialog through attributes and inheritance of an UITypeEditor?
First off I'm a little unsure about why this both inherits (: List<ModuleData>) and wraps (public List<ModuleData> Modules { get { return this; } }) a list - either individually should be fine.
However! To define the types of new objects you can create you need to derive from CollectionEditor and override the NewItemTypes property - and associate this editor with your type. I'm a little bit unclear on what objects you want to be addable, and whether this is the best design. If you want to add existing objects you may need a completely custom editor / uitypeeditor.
With the updated question, it definitely sounds like a job for a custom UITypeEditor; here's a version that uses a drop-down; you can do popups too (see methods on svc):
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Collections;
static class Program
{
static void Main()
{
MyWrapper wrapper = new MyWrapper();
wrapper.Modules.Add(new ModuleData { ModuleId = 123 });
wrapper.Modules.Add(new ModuleData { ModuleId = 456 });
wrapper.Modules.Add(new ModuleData { ModuleId = 789 });
wrapper.Batches.Add(new BatchData(wrapper) { BatchId = 666 });
wrapper.Batches.Add(new BatchData(wrapper) { BatchId = 777 });
PropertyGrid props = new PropertyGrid { Dock = DockStyle.Fill };
ListView view = new ListView { Dock = DockStyle.Left };
foreach (ModuleData mod in wrapper.Modules) {
view.Items.Add(mod.ToString()).Tag = mod;
}
foreach (BatchData bat in wrapper.Batches) {
view.Items.Add(bat.ToString()).Tag = bat;
}
view.SelectedIndexChanged += delegate {
var sel = view.SelectedIndices;
if(sel.Count > 0) {
props.SelectedObject = view.Items[sel[0]].Tag;
}
};
Application.Run(new Form { Controls = { props, view} });
}
}
class MyWrapper
{
private List<ModuleData> modules = new List<ModuleData>();
public List<ModuleData> Modules { get { return modules; } }
private List<BatchData> batches = new List<BatchData>();
public List<BatchData> Batches { get { return batches; } }
}
class ModuleListEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
{
IWindowsFormsEditorService svc;
IHasModules mods;
IList selectedModules;
if (context == null || (selectedModules = (IList)value) == null ||
(mods = context.Instance as IHasModules) == null
|| (svc = (IWindowsFormsEditorService)
provider.GetService(typeof(IWindowsFormsEditorService))) == null)
{
return value;
}
var available = mods.GetAvailableModules();
CheckedListBox chk = new CheckedListBox();
foreach(object item in available) {
bool selected = selectedModules.Contains(item);
chk.Items.Add(item, selected);
}
chk.ItemCheck += (s, a) =>
{
switch(a.NewValue) {
case CheckState.Checked:
selectedModules.Add(chk.Items[a.Index]);
break;
case CheckState.Unchecked:
selectedModules.Remove(chk.Items[a.Index]);
break;
}
};
svc.DropDownControl(chk);
return value;
}
public override bool IsDropDownResizable {
get {
return true;
}
}
}
interface IHasModules
{
ModuleData[] GetAvailableModules();
}
internal class BatchData : IHasModules {
private MyWrapper wrapper;
public BatchData(MyWrapper wrapper) {
this.wrapper = wrapper;
}
ModuleData[] IHasModules.GetAvailableModules() { return wrapper.Modules.ToArray(); }
[DisplayName("Batch ID")]
public int BatchId { get; set; }
private List<ModuleData> modules = new List<ModuleData>();
[Editor(typeof(ModuleListEditor), typeof(UITypeEditor))]
public List<ModuleData> Modules { get { return modules; } set { modules = value; } }
public override string ToString() {
return "Batch " + BatchId;
}
}
internal class ModuleData {
[DisplayName("Module ID")]
public int ModuleId { get; set; }
public override string ToString() {
return "Module " + ModuleId;
}
}