I am writing a Xamarin.iOS application with MvvmCross. I am trying to make a table, I can see the items being binded into the source, but I never see any cells being created. The function GetOrCreateCellFor never gets called. Here is my code:
public class ContactsManager
{
ContactsView _contactsView;
public ContactsManager()
{
_contactsView = new ContactsView();
Source = new FriendTableViewSource(_contactsView.FriendsTableView);
_contactsView.FriendsTableView.Source = Source;
}
public FriendTableViewSource Source { get; set; }
}
public class FriendTableViewSource : MvxTableViewSource
{
private readonly List<SeeMyFriendsItemViewModel> _content = new List<SeeMyFriendsItemViewModel>();
private readonly UITableView _tableView;
public FriendTableViewSource(UITableView t) : base(t)
{
_tableView = t;
t.RegisterNibForCellReuse(UINib.FromName(FriendCell.Key, NSBundle.MainBundle), FriendCell.Key);
}
private void Init(IEnumerable<SeeMyFriendsItemViewModel> items)
{
_content.Clear();
_content.AddRange(items);
}
public override System.Collections.IEnumerable ItemsSource
{
get
{
return base.ItemsSource;
}
set
{
// I put a break point here to check if I'm getting the items, and it is, so the binding is fine...
if (value != null)
Init(value.Cast<SeeMyFriendsItemViewModel>());
base.ItemsSource = value;
_tableView.ReloadData();
}
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return 60;
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
// This function never gets called!
return TableView.DequeueReusableCell(FriendCell.Key, indexPath);
}
}
[Register("FriendCell")]
public class FriendCell : MvxTableViewCell
{
public static readonly NSString Key = new NSString("FriendCell");
public static readonly UINib Nib;
static FriendCell()
{
Nib = UINib.FromName("FriendCell", NSBundle.MainBundle);
}
protected FriendCell(IntPtr handle) : base(handle)
{
BackgroundColor = UIColor.Red;
}
}
EDIT
This is what a working version of your source should look like. What's also interesting is that GetOrCreateCellFor won't get called if the table is not added to your view.
public class FriendTableViewSource : MvxTableViewSource
{
private readonly List<SeeMyFriendsItemViewModel> _content = new List<SeeMyFriendsItemViewModel>();
private MvxNotifyCollectionChangedEventSubscription _subscription;
public FriendTableViewSource(UITableView t) : base(t)
{
t.RegisterClassForCellReuse(typeof(FriendCell), FriendCell.Key);
}
private void Init(IEnumerable<SeeMyFriendsItemViewModel> items)
{
_content.Clear();
_content.AddRange(items);
}
public override System.Collections.IEnumerable ItemsSource
{
get
{
return base.ItemsSource;
}
set
{
if (value != null)
{
Init(value.Cast<SeeMyFriendsItemViewModel>());
var collectionChanged = value as System.Collections.Specialized.INotifyCollectionChanged;
if (collectionChanged != null)
{
_subscription = collectionChanged.WeakSubscribe(CollectionChangedOnCollectionChanged);
}
}
base.ItemsSource = value;
ReloadTableData();
}
}
protected override void CollectionChangedOnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args)
{
if (args.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (var item in args.NewItems)
{
var chatItem = item as SeeMyFriendsItemViewModel;
_content.Add(chatItem);
}
}
Init(ItemsSource.Cast<SeeMyFriendsItemViewModel>());
base.CollectionChangedOnCollectionChanged(sender, args);
InvokeOnMainThread(() => {
ReloadTableData();
TableView.SetContentOffset(new CGPoint(0, TableView.ContentSize.Height - TableView.Frame.Size.Height), true);
});
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return 60;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return _content.Count();
}
public override nint NumberOfSections(UITableView tableView)
{
return 1;
}
protected override object GetItemAt(NSIndexPath indexPath)
{
if (indexPath.Row < _content.Count)
return _content[indexPath.Row];
return null;
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
return TableView.DequeueReusableCell(FriendCell.Key, indexPath);
}
}
Override RowsInSection in FriendTableViewSource .
Since tableview needs row count and row height to decide its frame, if height = 0 or count = 0, GetOrCreateCellFor will never be called.
Related
I have a problem with refreshing a UITableView correctly, at least the first time of ViewWillAppear and sometimes (rarely) might even hang the app. When I navigate away from the UIViewController with changing tab item and navigate back, everything starts working perfectly for the entire lifecycle..
Having a ReactiveTableViewSource like below.
public class ATableViewSource : ReactiveTableViewSourceBase<IAViewModel>
{
WeakReference<AListViewController> _weakContainer;
Lazy<AListViewController> _lazyContainerViewController;
AListViewController Container => _lazyContainerViewController.Value;
public ATableViewSource(AListViewController container,
UITableView tableView,
IReactiveNotifyCollectionChanged<IAViewModel> collection)
: base(tableView, collection,
ATableViewCell.Key,
ATableViewCell.Height,
ATableViewCell.Height)
{
_weakContainer = new WeakReference<AListViewController>(container);
tableView.RegisterNibForCellReuse(ATableViewCell.Nib, ATableViewCell.Key);
_lazyContainerViewController = new Lazy<AListViewController>(() =>
{
AListViewController _container;
_weakContainer.TryGetTarget(out _container);
return _container;
});
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
base.RowSelected(tableView, indexPath);
tableView.DeselectRow(indexPath, false);
var item = ItemAt(indexPath) as IAViewModel;
if (item.IsNotNull())
{
AViewController viewController = new AViewController(item);
Container.NavigationController.PushViewController(viewController, true);
}
}
}
In AListViewController.ViewDidLoad I have this setup.
ATableViewSource _viewSource;
public override void ViewDidLoad()
{
base.ViewDidLoad();
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 350.0f;
TableView.ContentInset = new UIEdgeInsets(8.0f, 0.0f, 8.0f, 0.0f);
_viewSource = new ATableViewSource(this, TableView, ViewModel.TheReactiveList);
TableView.Source = _viewSource;
}
And in ViewWillAppear I always refresh the data (ViewModel.TheReactiveList).
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
ViewModel.RefreshTheReactiveList();
}
The AViewModel setup.
public class TheListViewModel : SchedulersViewModelBase, ITheListViewModel
{
public ReactiveList<IAViewModel> TheReactiveList { get; } = new ReactiveList<IAViewModel> { ChangeTrackingEnabled = true };
protected readonly IDataService DataService;
public TheListViewModel(IScheduler mainScheduler,
IScheduler taskPoolScheduler,
IDataService dataService)
: base(mainScheduler, taskPoolScheduler)
{
DataService = dataService;
}
public void RefreshTheReactiveList()
{
DataService.RefreshData()
.SubscribeOn(MainScheduler)
.ObserveOn(MainScheduler)
.Subscribe(ClearDataAndAddRange,
ex => AppObservables.Errors.OnNext(ex))
.DisposeWith(Disposables);
}
void ClearDataAndAddRange(IEnumerable<IAViewModel> data)
{
using (TheReactiveList.SuppressChangeNotifications())
{
TheReactiveList.Clear();
TheReactiveList.AddRange(data);
}
}
}
I have to mention in this use case I have a parent UIViewController with two child controllers each with the same setup (UITableView, ReactiveTableViewSource, ViewModel.ReactiveList) and Hidden state is controlled for their UIView containers, but I noticed similar effects to UIViewController with one UITableView taking up to 3 seconds to show the results in the UITableView.
For your reference I am posting the ReactiveTableViewSourceBase<TViewModel> below, I have found this long time ago on the internet, so it might be suspicious. Changing the base class to a ReactiveTableViewSource<TViewModel> doesn't really make any difference though.
public abstract class ReactiveTableViewSourceBase<TViewModel> : ReactiveTableViewSource<TViewModel>, IInformsEnd
{
private readonly Subject<Unit> _requestMoreSubject = new Subject<Unit>();
private readonly Subject<CGPoint> _scrollSubject = new Subject<CGPoint>();
public IObservable<CGPoint> DidScroll
{
get { return _scrollSubject.AsObservable(); }
}
public IObservable<Unit> RequestMore
{
get { return _requestMoreSubject; }
}
public override void Scrolled(UIScrollView scrollView)
{
_scrollSubject.OnNext(scrollView.ContentOffset);
}
~ReactiveTableViewSourceBase()
{
Console.WriteLine("Destorying " + GetType().Name);
}
protected ReactiveTableViewSourceBase(UITableView tableView, nfloat height, nfloat? heightHint = null)
: base(tableView)
{
tableView.RowHeight = height;
tableView.EstimatedRowHeight = heightHint ?? tableView.EstimatedRowHeight;
}
protected ReactiveTableViewSourceBase(UITableView tableView, IReactiveNotifyCollectionChanged<TViewModel> collection,
Foundation.NSString cellKey, nfloat height, nfloat? heightHint = null, Action<UITableViewCell> initializeCellAction = null)
: base(tableView, collection, cellKey, (float)height, initializeCellAction)
{
tableView.RowHeight = height;
tableView.EstimatedRowHeight = heightHint ?? tableView.EstimatedRowHeight;
}
public override void WillDisplay(UITableView tableView, UITableViewCell cell, Foundation.NSIndexPath indexPath)
{
if (indexPath.Section == (NumberOfSections(tableView) - 1) &&
indexPath.Row == (RowsInSection(tableView, indexPath.Section) - 1))
{
// We need to skip an event loop to stay out of trouble
BeginInvokeOnMainThread(() => _requestMoreSubject.OnNext(Unit.Default));
}
}
public override void RowSelected(UITableView tableView, Foundation.NSIndexPath indexPath)
{
var item = ItemAt(indexPath) as ICanGoToViewModel;
if (item != null)
{
item.GoToCommand.Execute();
}
base.RowSelected(tableView, indexPath);
}
protected override void Dispose(bool disposing)
{
_requestMoreSubject.Dispose();
_scrollSubject.Dispose();
base.Dispose(disposing);
}
}
public interface IInformsEnd
{
IObservable<Unit> RequestMore { get; }
}
I am using MvvmCross for my Xamarin iOS project. For my tableview source I have say, a,b,c,d,e,f,g,h records and section(header): 0 of suitable view has 3 records and section: 1 had 4 then
Expected result
Section-0
a
b
c
Section-1
d
e
f
g
I'm getting:
Section-0
a
b
c
Section-1
a
b
c
d
The records are being duplicated. I checked the source but source has correct data.
ISSUE:
At the end of each section the source is restarting from the top again to fill the records
My source:
View:
base.DoBind();
var source = new TableSource<string> (Table, ViewModel);
Table.Source = source;
Table.ReloadData();
Table.AlwaysBounceVertical = false;
var set = this.CreateBindingSet<View, ViewModel>();
set.Bind(source).To(vm => vm.CardsList);
set.Apply();
Cell:
protected override void DoBind()
{
var set = this.CreateBindingSet<Cell, ListViewModel>();
set.Bind(LblNum).To(vm => vm.CardNumber);
set.Bind(Balance).To(vm => vm.Balance);
set.Apply();
}
CellViewModel
public class ListViewModel : MvxViewModel
{
private string _CardNumber;
public string Number
{
get { return _Number); }
set { _CardNumber = value; }
}
private string _Balance;
public string Balance
{
get { _Balance; }
set { _Balance = value; }
}
Can anyone please advice how to resolve this
Update
UITableView _tableView;
public TableSource(UITableView tableView, object item) : base(tableView)
{
this.viewModel = item as ViewModel;
this._tableView = tableView;
tableView.RegisterNibForCellReuse(HeaderCell.Nib, HeaderCell.Key); tableView.RegisterNibForCellReuse(UINib.FromName(Cell.Key, NSBundle.MainBundle), Cell.Key);
tableView.RegisterNibForHeaderFooterViewReuse(UINib.FromName(HeaderCell.Key, NSBundle.MainBundle), HeaderCell.Key);
tableView.ReloadData();
var DataDic = new Dictionary<string, List<string>>
{
{ "section1", new List<string> {}},
{ "section2", new List<string> {}},
{ "section3", new List<string> {}}
};
//create the data
var list = new List<TableModel<string>>();
foreach (var section in DataDic)
{
var sectionData = new TableModel<string>()
{
Title = section.Key
};
foreach (var row in section.Value)
{
sectionData.Add(row);
}
list.Add(sectionData);
}
TableItems = list;
}
public override nint NumberOfSections(UITableView tableView)
{
return TableItems.Count;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
int result = 0;
if (section == 0)
{
result = viewModel.NumOfGiftCards;
}
else if (section == 1)
{
result = viewModel.NumOfRewardsCerts;
}
return result;
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
return section == 2 ? 0f : 74f;
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return indexPath.Section == 2 ? 95f : 62f;
}
public override IEnumerable ItemsSource
{
get
{
return base.ItemsSource;
}
set
{
base.ItemsSource = value;
_tableView.ReloadData();
}
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
var header = tableView.DequeueReusableHeaderFooterView(HeaderCell.Key) as HeaderCell;
return header;
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
var cell = tableView.DequeueReusableCell(Cell.Key, indexPath) as Cell;
return cell;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
viewModel.CardDetailsCommand.Execute(null);
}
I don't think the ItemSource you are binding the CardsList to is not matching up to the TableItems, viewModel.NumOfRewardsCerts or viewModel.NumOfGiftCards that you are using the table source.
Have you had a looked at the MvxExpandableItemSource (the sample is here) It can handle 2 dimensional ItemSources:
private IEnumerable<TItemSource> _itemsSource;
public new IEnumerable<TItemSource> ItemsSource
{
get
{
return _itemsSource;
}
set
{
_itemsSource = value;
_sectionExpandableController.ResetState();
ReloadTableData();
}
}
It deals with expanding and collapsing the from the headers, see below, but you could disable that functionality and force them to be expanded all the time:
I am trying to bind grouped data to a table, but when I run the code the binding seems to be trying to create a cell for the Section level items rather than creating a section with the cells behind it.
First, I create my table:
table = new UITableView(CGRect.Empty, UITableViewStyle.Grouped);
table.RowHeight = UxConstants.UiDimensions.Dimension48;
table.AutoresizingMask = UIViewAutoresizing.All;
table.ScrollEnabled = true;
then I create my view source and bind it:
var source = new OperationalInfoTableViewSource(table);
table.Source = source;
var set = this.CreateBindingSet<IncidentInformationView, IncidentInformationViewModel>();
set.Bind(source).For(vm => vm.ItemsSource).To(vm => vm.TableSourceItem);
set.Apply();
I'm using a simplified class at the moment; so the binding target (TableSourceItem) is currently defined as:
public ObservableCollection<Stuff> TableSourceItem
{
get
{
return tableSourceItem;
}
set
{
tableSourceItem = value;
RaisePropertyChanged(() => TableSourceItem);
}
}
and the 'Stuff' class is simply
public class Stuff
{
public string Title {
get;
set;
}
public List<string> Items {
get;
set;
}
}
When I declare my data :
var items = new List<Stuff>();
items.Add(new Stuff { Title = "Group 1", Items = new List<string> { "Item 1", "Item 2" } });
items.Add(new Stuff { Title = "Group 2", Items = new List<string> { "Item 4", "Item 5"} });
TableSourceItem = new ObservableCollection<Stuff>(items);
The final bit of Code, is the table view source I have defined. This inherits from the MvxStandardTableView source:
public class OperationalInfoTableViewSource : MvxStandardTableViewSource
{
List<Stuff> items;
public OperationalInfoTableViewSource(UITableView tableView) : base (tableView)
{
TableView.RegisterClassForCellReuse (typeof(OperationalInfoViewCell), OperationalInfoViewCell.Key);
}
public override System.Collections.IEnumerable ItemsSource
{
get
{
return items;
}
set
{
if (value != null)
{
items = new List<Stuff>();
foreach (Stuff item in value)
{
items.Add(item);
}
base.ItemsSource = items;
}
}
}
public override string TitleForHeader(UITableView tableView, nint section)
{
return "Header";
}
public override nint NumberOfSections(UITableView tableView)
{
var i = items == null ? 0 : items.Count();
Mvx.Trace(String.Format( "Number of Sections {0}", i ));
return i;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return items == null ? 0 : items[(int)section].Items.Count();
}
public override bool CanEditRow(UITableView tableView, NSIndexPath indexPath)
{
return false;
}
public override bool CanPerformAction(UITableView tableView, ObjCRuntime.Selector action, NSIndexPath indexPath, NSObject sender)
{
return false;
}
public override bool CanMoveRow(UITableView tableView, NSIndexPath indexPath)
{
return false;
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
var cell = tableView.DequeueReusableCell(OperationalInfoViewCell.Key);
return cell;
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
return UxConstants.UiDimensions.Dimension32;
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
var operationalInfoTitle = new UILabel()
{
Lines = 1,
Font = UxConstants.UxFonts.MediumBold,
TextColor = UxConstants.UxColours.BBlue,
Text = "Guidance & Info"
};
var operationalInfoTitleDivider = new UIView()
{
BackgroundColor = UxConstants.UxColours.MGrey
};
var operationalInfoListView = new UIView();
operationalInfoListView.AddSubviews(operationalInfoTitle, operationalInfoTitleDivider);
operationalInfoListView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
operationalInfoListView.AddConstraints(operationalInfoTitle.FullWidthOf(operationalInfoListView, UxConstants.UiDimensions.Dimension16));
operationalInfoListView.AddConstraints(operationalInfoTitleDivider.FullWidthOf(operationalInfoListView, UxConstants.UiDimensions.Dimension16));
operationalInfoListView.AddConstraints(
operationalInfoTitle.Height().EqualTo(UxConstants.UiDimensions.Dimension32),
operationalInfoTitle.AtTopOf(operationalInfoListView),
operationalInfoTitleDivider.Below(operationalInfoTitle),
operationalInfoTitleDivider.Height().EqualTo(2));
return operationalInfoListView;
}
}
I would expect 2 sections, each with two cells, but its seems to be creating one cell per Stuff class rather than one section per Stuff class.
Any ideas?
Found out the issue, I have failed to implement the important override:
protected override object GetItemAt(NSIndexPath indexPath)
{
if (items == null)
{
return null;
}
return items[indexPath.Section].Items.ElementAt((int)indexPath.Item);
}
Once I had included this method override it worked fine!
I have been trying to implement a multi column table view by combining multiple UITableViews.
One problem I have run into, is that I am unable to get the override of RowSelected to be called when I select a row on one of the TableViews in my multi column view.
When running in the simulator, It highlights a row when clicked, so the ability to click a row is working, however the override is not called.
Below is the code for the UITableViewSource. Under that is the code that sets up the multi column view.
Any help would be greatly appreciated.
public class TableSource : UITableViewSource
{
protected List<object> _tableItems;
protected string cellIdentifier = "TableCell";
private string _propertyName;
public TableSource (List<object> items, string propertyName)
{
_tableItems = items;
_propertyName = propertyName;
}
public override int RowsInSection (UITableView tableview, int section)
{
return _tableItems.Count;
}
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
// request a recycled cell to save memory
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
// if there are no cells to reuse, create a new one
if (cell == null)
cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);
PropertyInfo property = _tableItems [indexPath.Row].GetType ().GetProperty (_propertyName);
cell.TextLabel.Text = property.GetValue(_tableItems[indexPath.Row]).ToString();
cell.UserInteractionEnabled = true;
return cell;
}
public override UIView GetViewForHeader (UITableView tableView, int section)
{
// NOTE: Don't call the base implementation on a Model class
// see http://docs.xamarin.com/guides/ios/application_fundamentals/delegates,_protocols,_and_events
throw new NotImplementedException ();
}
public override void RowDeselected (UITableView tableView, NSIndexPath indexPath)
{
// NOTE: Don't call the base implementation on a Model class
// see http://docs.xamarin.com/guides/ios/application_fundamentals/delegates,_protocols,_and_events
throw new NotImplementedException ();
}
public override void RowHighlighted (UITableView tableView, NSIndexPath rowIndexPath)
{
// NOTE: Don't call the base implementation on a Model class
// see http://docs.xamarin.com/guides/ios/application_fundamentals/delegates,_protocols,_and_events
throw new NotImplementedException ();
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
new UIAlertView("Row Selected", "", null, "OK", null).Show();
tableView.DeselectRow (indexPath, true);
}
}
[Register("MultiColumnTableView")]
public class MultiColumnTableView: UIView
{
List<SingleColumnView> _tableViews;
IEnumerable<object> _dataSource;
SingleColumnView scrollingTable = null;
int _nextColumnX;
public MultiColumnTableView(){
}
public MultiColumnTableView (IntPtr handle):base(handle)
{
}
public IEnumerable<object> DataSource
{
get
{
return _dataSource;
}
set
{
_dataSource = value;
}
}
public void AddColumn(string columnName, string propertyName, int width)
{
if(_tableViews== null)
_tableViews = new List<SingleColumnView>();
var newColumn = new SingleColumnView (columnName, propertyName, _dataSource, width, _nextColumnX);
this.AddSubview (newColumn);
_tableViews.Add (newColumn);
_nextColumnX += (width);
newColumn.Scrolled += Scrolled;
foreach(var table in _tableViews)
{
table.ShowsVerticalScrollIndicator = false;
}
_tableViews.Last ().ShowsVerticalScrollIndicator = true;
}
public void AddColumn(string columnName, string[] propertyNames, string format)
{
if(_tableViews== null)
_tableViews = new List<SingleColumnView>();
var column = new SingleColumnView (columnName, propertyNames, format, _dataSource);
_tableViews.Add (column);
column.Scrolled += Scrolled;
foreach(var table in _tableViews)
{
table.ShowsVerticalScrollIndicator = false;
table.AllowsSelection = true;
}
_tableViews.Last ().ShowsVerticalScrollIndicator = true;
}
private void Scrolled (object sender, EventArgs args)
{
if (scrollingTable == null) {
scrollingTable = sender as SingleColumnView;
}
if(scrollingTable != _tableViews.Last())
_tableViews.Last().FlashScrollIndicators();
foreach (var table in _tableViews) {
if (table != sender) {
table.ContentOffset = (sender as SingleColumnView).ContentOffset;
}
}
scrollingTable = null;
}
}
public class SingleColumnView: UITableView
{
public IEnumerable<object> _dataSource;
public string _propertyName;
public string[] _propertyNames;
public string _format;
public string _columnName;
public SingleColumnView(string columnName, string propertyName, IEnumerable<object> dataSource, int width, int offsetX):base(new RectangleF(offsetX,0,width,500))
{
_columnName = columnName;
_propertyName = propertyName;
_dataSource = dataSource;
this.Source = new TableSource (dataSource.ToList(), propertyName);
//this.BackgroundColor = new UIColor(new MonoTouch.CoreGraphics.CGColor(255,0,0));
//this.SeparatorStyle = UITableViewCellSeparatorStyle.None;
this.SeparatorInset = UIEdgeInsets.Zero;
}
public SingleColumnView(string columnName, string[] propertyNames, string format, IEnumerable<object> dataSource):base()
{
_columnName = columnName;
_propertyNames = propertyNames;
_format = format;
_dataSource = dataSource;
}
}
How are you implementing the custom UITableViewDelegate? I would suggest using Monotouch's UITableViewSource as it combines both the UITableViewDataSource and the UITableViewDelegate into one file which makes things so much easier.
Source:more details
Add to You project
I think you should try using WeakDelegate to make this work.
Can I create a Sectioned UITableView with MonoTouch.Dialog with alphabetic navigation?
In MonoTouch, I create sectioned UITableViews like so:
public EntityDataSource(UIViewController controller)
{
_controller = controller;
this._entities = repository.GetEntities();
sectionTitles = (from r in _entities
orderby r.StartsWith
select r.StartsWith).Distinct().ToList();
foreach (var entity in _entities)
{
int sectionNumber = sectionTitles.IndexOf(entity.StartsWith);
if (sectionElements.ContainsKey(sectionNumber)) {
sectionElements[sectionNumber].Add(entity);
}
else {
sectionElements.Add(sectionNumber, new List<Entity>() {entity});
}
}
}
public override int NumberOfSections (UITableView tableView)
{
return sectionTitles.Count;
}
public override string TitleForHeader (UITableView tableView, int section)
{
return sectionTitles[section];
}
public override string[] SectionIndexTitles (UITableView tableView)
{
return sectionTitles.ToArray();
}
public override int RowsInSection (UITableView tableview, int section)
{
return sectionElements[section].Count();
}
I want to do this same thing but with MonoTouch.Dialog. Is that possible?
I believe you can only get the index if the style of the UITableView is plain.
In addition, you need to override the method SectionIndexTitles on the UITableViewDataSource, since MonoTouch.Dialog uses its own implementation in the classes SizingSource or Source, what you have to do is create those two subclasses to return the values, and then hook them up by overwriting the DialogViewController.CreateSizingSource () method.
Based on Miguel's answer, here is what I did:
public class EntityViewController : DialogViewController {
DialogViewController parent;
List<string> sectionTitles;
class EntitySource : Source {
EntityViewController parent;
public EntitySource (EntityViewController parent) : base (parent)
{
this.parent = parent;
}
public override string[] SectionIndexTitles (UITableView tableView)
{
return parent.sectionTitles.ToArray();
}
}
class SizingIndexedSource : Source {
EntityViewController parent;
public SizingIndexedSource (EntityViewController parent) : base (parent)
{
this.parent = parent;
}
public override string[] SectionIndexTitles (UITableView tableView)
{
return parent.sectionTitles.ToArray();
}
}
public override Source CreateSizingSource (bool unevenRows)
{
if (unevenRows)
return new SizingIndexedSource (this);
else
return new EntitySource (this);;
}
private RootElement GetEntities() {
EntityRepository db = new EntityRepository();
List<Entity> _entities = db.GetEntities();
sectionTitles = (from r in _entities
orderby r.StartsWith
select r.StartsWith).Distinct().ToList();
var root = new RootElement ("Entities") ;
foreach (var item in sectionTitles) {
var section = new Section(item,String.Empty);
foreach (var entity in _entities.Where(e => e.StartsWith == item)) {
section.Add(new StringElement(entity.FirstName + " " + entity.LastName, "Title"));
}
root.Add(section);
}
return root;
}
public EntityViewController (DialogViewController parent) : base (UITableViewStyle.Grouped, null)
{
this.parent = parent;
Root = GetEntities();
this.Style = UITableViewStyle.Plain;
this.EnableSearch = true;
this.SearchPlaceholder = "Find a contact";
this.AutoHideSearch = true;
}
}