Selector in UITableViewCell in Xamarin - c#

I need the UIMenuItem to call a method inside the UITableViewCell. Something similar is possible in swift as explained here: https://stackoverflow.com/a/31358474/202179 .
At the moment I am only able to call the method inside the UIViewController from the UIMenuItem if I add an Export attribute to the method.
There is some trick in the export of this method in the cell, but I have failed to find it till now. I would expect this code to work, but it doesn't:
[Register("CustomCell")]
public class CustomCell : UITableViewCell
{
public CustomCell(IntPtr intPtr) : base(intPtr)
{
var menuItem = new UIMenuItem("Menu Item", new ObjCRuntime.Selector("CustomCell.SeekSelectedText"));
UIMenuController.SharedMenuController.MenuItems = new UIMenuItem[] { menuItem };
UIMenuController.SharedMenuController.Update();
}
[Export("SeekSelectedText")]
void SeekSelectedText()
{
//do something
}
}
As said just quoting a different selector inside the UIViewController works, so the problem is that this selector doesn't appear to be recognized whatever I try.

Here is my test and the custom UIMenuItem works fine.
First, define a custom cell class which inherits UITableViewCell
class MyCell: UITableViewCell
{
public MyCell(UITableViewCellStyle style, string reuseIdentifier):base(style, reuseIdentifier)
{
}
[Export("custom")]
void Custom()
{
//do something
Console.WriteLine("custom pop up");
}
public override bool CanPerform(Selector action, NSObject withSender)
{
if (action == new Selector("custom"))
return true;
else
return false;
}
}
Second, set the source of your UITableView
class MyTableViewSource : UITableViewSource
{
int i = 0;
// create custom cell
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
string cellStr = "cell";
MyCell cell = (MyCell)tableView.DequeueReusableCell(cellStr);
if (cell == null)
{
cell = new MyCell(UITableViewCellStyle.Subtitle, cellStr);
cell.TextLabel.Text = $"User Name {i}";
cell.DetailTextLabel.Text = $"details here... {i++}";
}
return cell;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return 10;
}
}
Then override the realted methods(e.g., ShouldShowMenu, CanPerformAction).
class MyTableViewDelegate : UITableViewDelegate
{
public override bool ShouldShowMenu(UITableView tableView, NSIndexPath rowAtindexPath)
{
return true;
}
public override bool CanPerformAction(UITableView tableView, Selector action, NSIndexPath indexPath, NSObject sender)
{
return true;
}
public override void PerformAction(UITableView tableView, Selector action, NSIndexPath indexPath, NSObject sender)
{
}
public override bool ShouldHighlightRow(UITableView tableView, NSIndexPath rowIndexPath)
{
return true;
}
}
Last, don't foget to add custom MenuItem to your UITableViewCell.
UIMenuController.SharedMenuController.MenuItems = new UIMenuItem[] {
new UIMenuItem ("Custom", new Selector ("custom"))
};
Since UICollectionView has similar functions to UITableVIew, you can directly refer to the sample SimpleCollectionView.

Related

UITableViewCell not being rendered correctly

We've created a TableViewController so that a user can enable/disable different notification types. After the initial load, everything is fine until the user scrolls to the bottom then back to the top and the first 1-2 table view cells aren't displaying correctly.
Example:
https://imgur.com/7n2VpTo
Typically deleting and recreating the view controller and xib/cs files fixes this, but was wondering if any one knows the cause of this.
Controller Code:
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return 305;
}
public override nint NumberOfSections(UITableView tableView)
{
return 1;
}
public override nint RowsInSection(UITableView tableView, nint section)
{
return _numRows;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(Setting.Key) as Setting;
if (cell == null)
{
var values = _settings.Where(s => s.Index == indexPath.Row).FirstOrDefault();
cell = new Setting(values);
var views = NSBundle.MainBundle.LoadNib(Setting.Key, cell, null);
cell = Runtime.GetNSObject(views.ValueAt(0)) as Setting;
}
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
cell?.PopulateData(_settings.Where(s => s.Index == indexPath.Row).FirstOrDefault(), this);
return cell;
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
}
Cell Code:
public partial class Setting : UITableViewCell
{
public static readonly NSString Key = new NSString("Setting");
public static readonly UINib Nib;
public SettingModel Values { get; set; }
static Setting()
{ 
Nib = UINib.FromName("Setting", NSBundle.MainBundle);
} 
 
protected Setting(IntPtr handle) : base(handle) { }

public Setting() : base() {

 }

public override void LayoutSubviews()
{ 
base.LayoutSubviews();
}

public void PopulateData(SettingModel values) 
{
//logic...
}
}
Upgrading to Visual Studio for Mac 8.6.5 resolved the issue.

Bind second UITableView on RowClick of first UITableView in the same UIViewController - ios Xamarin

I have two UITableView in the same UIViewController.
tblView1 & tblView2.
Both TableViews are bringing data dynamically.
Now my problem is: I want to load data in tblView2 on the basis of SelectedRow from tblView1.
For Example, when I select a company name from tblView1, then tblView2 needs to populate the Cars made by that company.
I have rowIndex of my first table (tblView1), but i can't bind my tblView2 from the RowSelected Method of tblView1.
I need help in binding my tblView2. How can i do this?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using CoreGraphics;
using Foundation;
using UIKit;
namespace Tables
{
public partial class ViewController : UIViewController
{
protected ViewController(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public ViewController()
{
//tblView2.Source = new CompanyModelsSource();
// Note: this .ctor should not contain any initialization logic.
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (tblView1 != null)
{
tblView1.Source = new CarModelsSource();
}
// Perform any additional setup after loading the view, typically from a nib.
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
}
class CompanyModelsSource : UITableViewSource
{
private List<Companies> _Companies;
NSString _cellID = new NSString("TableCell");
public CompanyModelsSource()
{
_Companies = CompanyTableSource.AllCompany();
}
public override nint RowsInSection(UITableView tableview, nint section)
{
// tell the TableView how many rows to create
return _Companies.Count;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
//I can't access my tblView2 in this method. So where i can bind my other UITableView?
UILabel lblTotalFacilities = new UILabel();
lblTotalFacilities.Layer.BackgroundColor = UIColor.Blue.CGColor;
lblTotalFacilities.Frame = new CGRect(305, 200, 1024, 400);
ViewController vc = new ViewController();
vc.View.AddSubview(lblTotalFacilities);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
Company currentCompany = _Companies[indexPath.Row];
var cell = tableView.DequeueReusableCell(_cellID) as CompanyTableCell;
if (cell == null) { cell = new CompanyTableCell(_cellID); }
cell.UpdateCellControlsWithAuditData(currentCompany.Name);
return cell;
}
}
public class CompanyTableSource
{
public CompanyTableSource()
{
}
public static List<Companies> AllCompany()
{
CompanyCollection CC = null;
string path1 = path + "Companies.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CompanyCollection));
StreamReader reader = new StreamReader(path1);
CC = (CompanyCollection)serializer.Deserialize(reader);
reader.Close();
List<Companies> comp = CC.CompanyCollection.Cast<Companies>().ToList();
return comp;
}
}
public class CompanyTableCell : UITableViewCell
{
UILabel nameLabel;
public CompanyTableCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(54, 194, 36);
var newFont = UIFont.SystemFontOfSize(12);
nameLabel = new UILabel();
nameLabel.Font = newFont;
ContentView.AddSubviews(new UIView[] { nameLabel });
}
public void UpdateCellControlsWithAuditData(string Name)
{
nameLabel.Text = Name;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
nameLabel.Frame = new CGRect(10, 0, 350, 33);
}
}
}
Hope this helps. Created everything in a single file for simplicity.
public partial class ViewController : UIViewController
{
public ViewController() : base("ViewController", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
var companySource = new CompanyTableSource();
tblView1.Source = companySource;
companySource.ItemSelected += OnCompanyItemSelected;
}
//this will handle the ItemSelected event
//here you will retrieve the data for the second UITableView
void OnCompanyItemSelected(object sender, Company e)
{
//you can do it this way
tblView2.Source = new CarTableSource(e.Id);
List<Car> cars = GetCarsByCompanyId(e.Id);
//or this whay
tblView2.Source = new CarTableSource(cars);
}
List<Car> GetCarsByCompanyId(int id)
{
//Your logic to get the data goes here
throw new NotImplementedException();
}
}
public class CompanyTableSource : UITableViewSource
{
// You can use the whole object or just an integer (companyId)
public event EventHandler<Company> ItemSelected;
public IList<Company> Items { get; private set; } = new List<Company>();
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var item = Items[indexPath.Row];
ItemSelected?.Invoke(this, item);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
//Do your implementation
throw new NotImplementedException();
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return Items.Count;
}
}
public class CarTableSource : UITableViewSource
{
public IList<Car> Items { get; private set; } = new List<Car>();
public CarTableSource(int companyId)
{
//Get your data based on the companyId
}
//Or pass in the data already retreived. (I preffer this method)
public CarTableSource(IList<Car> cars)
{
Items = cars;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
//Do your implementation
throw new NotImplementedException();
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return Items.Count;
}
}

iOS TableView button is being called multiple times

I have a TableView with a custom UITableViewCell. In each cell I have multiple buttons, when any of the buttons is click after scrolling down and up it calls itself the many times I scroll down and up.
I have read and research for a solution and I haven't found a solution.
I know the problem is the cell are being reuse so that's why the buttons are being call multiple times but I can't find a way to prevent it.
I added console write line statements through the code and the else part in MoveToWindow is never call. Could that be the reason why?
Research material for solution:
my code is calling twice the btndelete method in uitableview
UIButton click event getting called multiple times inside custom UITableViewCell
My Code:
namespace Class.iOS
{
public partial class CustomCell : UITableViewCell
{
public static readonly NSString Key = new NSString ("CustomCell");
public static readonly UINib Nib;
public int Row { get; set; }
public event EventHandler LikeButtonPressed;
private void OnLikeButtonPressed()
{
if (LikeButtonPressed != null)
{
LikeButtonPressed(this, EventArgs.Empty);
}
}
public override void MovedToWindow()
{
if (Window != null)
{
btnAdd.TouchUpInside += HandleLikeButtonPressed;
}
else
{
btnAdd.TouchUpInside -= HandleLikeButtonPressed;
}
}
private void HandleLikeButtonPressed(object sender, EventArgs e)
{
OnLikeButtonPressed();
}
static CustomCell ()
{
Nib = UINib.FromName ("CustomCell", NSBundle.MainBundle);
}
public CustomCell ()
{
}
public CustomCell (IntPtr handle) : base (handle)
{
}
public void UpdateCell (string Name, int number)
{
// some code
}
public class TableSource : UITableViewSource
{
public override nint RowsInSection (UITableView tableview, nint section)
{
return 8;
}
private void HandleLikeButtonPressed(object sender, EventArgs e)
{
var cell = (CustomCell)sender;
var row = cell.Row;
switch (row)
{
case 0:
cell.label.Text = ""
break;
case 1:
cell.label.Text = ""
break;
}
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (CustomCell.Key) as CustomCell;
cell.Row = indexPath.Row;
if (cell == null)
{
cell = new CustomCell ();
var views = NSBundle.MainBundle.LoadNib("CustomCell", cell, null);
cell.LikeButtonPressed += HandleLikeButtonPressed;
cell = Runtime.GetNSObject( views.ValueAt(0) ) as CustomCell;
}
cell.UpdateCell
(
// some code
);
return cell;
}
}
}
}
The cells are reused in iOS, so you need to make sure you properly unhook handlers and reset state when a cell is reused. You can do something like this:
public partial class CustomCell : UITableViewCell {
EventHandler _likeButtonHandler;
public static readonly NSString Key = new NSString (typeof(CustomCell).Name);
public static readonly UINib Nib = UINib.FromName (typeof(CustomCell).Name, NSBundle.MainBundle);
public CustomCell ()
{
}
public CustomCell (IntPtr handle) : base (handle)
{
}
public override void PrepareForReuse ()
{
LikeButton.TouchUpInside -= _likeButtonHandler;
_likeButtonHandler = null;
base.PrepareForReuse ();
}
public void SetupCell (int row, string Name, EventHandler likeBtnHandler)
{
_likeButtonHandler = likeBtnHandler;
LikeButton.TouchUpInside += _likeButtonHandler;
LikeButton.Tag = row;
NameLabel.Text = Name;
RowLabel.Text = row.ToString ();
}
Notice that I unhook the event handler in the PrepareForReuse override. This is the correct place to do clean up and reset a cell for reuse. You should NOT be using MovedToWindow().
Then your GetCell method will look like this:
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (CustomCell.Key) as CustomCell ?? new CustomCell ();
cell.SetupCell (indexPath.Row, _fruits [indexPath.Row], _likeButtonHandler);
return cell;
}
The _likeButtonHandler is a simple EventHandler.

Change View Controller on UITableViewCell Tap

I'm trying to change the View Controller when someone Taps on a Table View Cell. However Im not doing it the conventional way. I am submethoding the method that actually detects the tap so I could use it from any other class. Here is how I am doing it:
TableSource.cs
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
if (OnRowSelected != null) {
OnRowSelected (this, new RowSelectedEventArgs (tableView, indexPath));
}
}
public class RowSelectedEventArgs : EventArgs
{
public UITableView tableView { get; set; }
public NSIndexPath indexPath { get; set; }
public RowSelectedEventArgs(UITableView tableView, NSIndexPath indexPath) : base()
{
this.tableView = tableView;
this.indexPath = indexPath;
}
}
public event EventHandler<RowSelectedEventArgs> OnRowSelected;
Request.cs
tableSourceTable.OnRowSelected += (object sender, TableSource<Datum>.RowSelectedEventArgs e) => {
vc = new InstagramModalController (){instaData = tableSourceTable.instaData[e.indexPath.Row]};
this.navControl.PushViewController(this.vc,true);
this.getMediaID(tableSourceTable.instaData[e.indexPath.Row].link);
new UIAlertView(tableSourceTable.instaData[e.indexPath.Row].user.full_name.ToString(),
tableSourceTable.instaData[e.indexPath.Row].caption.text.ToString(), null, "OK", null).Show();
e.tableView.DeselectRow (e.indexPath, true);
};
That is all the code from the TableSource class and I would be okay with any change.

How do you handle Touch Events in a UITableView

I'm trying to apply an event to a cell in order to specify the TouchesBegan method.
cell.TouchesBegan += OnTouchesBegan;
But TouchesBegan is a method group and not an event.
How do I add custom behavior to a Cells TouchesBegan() method in monotouch?
[Register("MenuView")]
public class MenuView : ViewControllerBase
{
private new MenuViewModel ViewModel { get { return (MenuViewModel)base.ViewModel; } }
public override void ViewDidLoad()
{
base.ViewDidLoad();
// this ensures the sliding panel doesn't fill the entire view.
var frame = View.Frame;
frame.Width = 300;
View.Frame = frame;
var chapters = ViewModel.Chapters;
//var listHeight = (chapters.Count*40);
var navigationList = new UITableView(new RectangleF(0, 50, 300, 300))
{
Source = new NavigationTableSource(chapters)
};
Add(navigationList);
}
}
public class NavigationTableSource : UITableViewSource
{
private readonly IList<ChapterViewModel> _chapters;
private const string CellIdentifier = "NavigationCell";
public NavigationTableSource(IList<ChapterViewModel> chapters)
{
_chapters = chapters;
}
public override int RowsInSection(UITableView tableview, int section)
{
return _chapters.Count;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(CellIdentifier) ??
new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier);
// if there are no cells to reuse, create a new one
cell.TextLabel.Text = _chapters[indexPath.Row].Title;
cell.TouchesBegan += OnTouchesBegan;
return cell;
}
}
TouchesBegan is a method that you can override by subclassing UITableViewCell:
public class CustomCell : UITableViewCell
{
public CustomCell(UITableViewCellStyle style, NSString reuseIdentifier) : base(style, reuseIdentifier)
{
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
// do something
base.TouchesBegan(touches, evt);
}
}
Then use your subclass when creating a cell:
var cell = tableView.DequeueReusableCell(CellIdentifier) ??
new CustomCell(UITableViewCellStyle.Default, CellIdentifier);
So the way I was able to manage this was to SubClass my UITableViewSource. In there I overrode RowSelected(UITableView tableView, NSIndexPath indexPath)
public class NavigationTableSource : UITableViewSource
{
private readonly IList<ChapterViewModel> _chapters;
private const string CellIdentifier = "NavigationCell";
public NavigationTableSource(IList<ChapterViewModel> chapters)
{
_chapters = chapters;
}
public override int RowsInSection(UITableView tableview, int section)
{
return _chapters.Count;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
// todo: This is where we're going to load up the appropriate chapter.
new UIAlertView("Row Selected", _chapters[indexPath.Row].Title, null, "OK", null).Show();
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(CellIdentifier) ??
new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier);
// if there are no cells to reuse, create a new one
cell.TextLabel.Text = _chapters[indexPath.Row].Title;
return cell;
}
}

Categories

Resources