Open gtbuchanan opened 5 years ago
I came up with a workaround using a proxy. Not really an ideal solution, in my opinion. Nonetheless, it seems to work without error.
internal sealed class CustomListViewRenderer : ListViewRenderer
{
private new CustomListView Element => (CustomListView)base.Element;
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Element == null || Control == null) return;
Control.Source = new ListViewSource(Control.Source, Element);
}
private sealed class ListViewSource : UITableViewSourceProxy
{
private CustomListView ListView { get; }
public ListViewSource(UITableViewSource inner, CustomListView listView) : base(inner) => ListView = listView;
public override void Scrolled(UIScrollView scrollView)
{
base.Scrolled(scrollView);
var percentage = (scrollView.ContentOffset.Y + scrollView.Bounds.Height) / scrollView.ContentSize.Height;
ListView.ScrollYPercentage = Math.Min(Math.Round(percentage, 3), 1);
}
}
}
internal abstract class UITableViewSourceProxy : UITableViewSource
{
private UITableViewSource Inner { get; }
protected UITableViewSourceProxy(UITableViewSource inner) => Inner = inner;
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) => Inner.GetCell(tableView, indexPath);
public override nint RowsInSection(UITableView tableview, nint section) => Inner.RowsInSection(tableview, section);
public override void DraggingEnded(UIScrollView scrollView, bool willDecelerate) => Inner.DraggingEnded(scrollView, willDecelerate);
public override void DraggingStarted(UIScrollView scrollView) => Inner.DraggingStarted(scrollView);
public override nfloat GetHeightForHeader(UITableView tableView, nint section) => Inner.GetHeightForHeader(tableView, section);
public override UIView GetViewForHeader(UITableView tableView, nint section) => Inner.GetViewForHeader(tableView, section);
public override void HeaderViewDisplayingEnded(UITableView tableView, UIView headerView, nint section) =>
Inner.HeaderViewDisplayingEnded(tableView, headerView, section);
public override nint NumberOfSections(UITableView tableView) => Inner.NumberOfSections(tableView);
public override void RowDeselected(UITableView tableView, NSIndexPath indexPath) => Inner.RowDeselected(tableView, indexPath);
public override void RowSelected(UITableView tableView, NSIndexPath indexPath) => Inner.RowSelected(tableView, indexPath);
public override void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath) =>
Inner.WillDisplay(tableView, cell, indexPath);
public override void Scrolled(UIScrollView scrollView) => Inner.Scrolled(scrollView);
public override string[] SectionIndexTitles(UITableView tableView) => Inner.SectionIndexTitles(tableView);
protected override void Dispose(bool disposing) => Inner.Dispose();
}
@hartez Did we ever decide to add the Scrolling
event to #3172? @gtbuchanan Would a Scrolling
event that reports scroll position meet your needs? Thanks!
@samhouts Yes, as long as it also reported the total scroll/content height as well. This is currently my only use of a custom ListViewRenderer
.
Met the same error on ios platform while I want override some method like on android platform
Another one workaround
[assembly: ExportRenderer(typeof(MyListView), typeof(MyListViewRender))]
namespace MyApp.Mobile.iOS.CustomRenderers
{
public class MyListViewRender : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var scalableListView = Element as MyListView;
if (Control == null || scalableListView == null)
{
return;
}
var tvDelegate = new TableViewDelegate();
Control.Delegate = tvDelegate;
tvDelegate.OnDraggingStarted += (s, ev) =>
{
scalableListView.InvokeScrollEventDraggingStarted();
};
}
}
public class TableViewDelegate : UITableViewDelegate
{
public event EventHandler OnDraggingStarted;
public override void DraggingStarted(UIScrollView scrollView)
{
OnDraggingStarted?.Invoke(scrollView, null);
}
}
}
I've confirmed this is no longer an issue in Xamarin.Forms 4.6.0.847. You seem to be able to use a custom Delegate
again. There is also CollectionView now which has the Scrolled event that could possibly meet my needs without a native renderer.
Turns out I spoke too soon (and forgot what the original issue was). While you can set Delegate
without error, doing so appears to break item selection. Attempting to use the Scrolled
event on UIScrollView
still results in the same error from the OP. Unfortunately, my workaround using UITableViewSourceProxy
now results in a Foundation.You_Should_Not_Call_base_In_This_Method
exception. I added try...catch
statements to swallow this exception since it doesn't matter in my use case and that seemed to help. This will work for now while I figure out CollectionView
rendering issues I'm having on UWP.
Description
Control event registration in custom
ListViewRenderer
child class throws anInvalidOperationException
. In my particular case, I'm trying to listen onUITableView.Scrolled
because there is no equivalent Xamarin Forms property/event I can use to track scroll position.The
ListViewRenderer
class itself setsControl.Source
, which is what I believe is causing the problem. There may be a workaround for this I just don't know about. I'd like to avoid implementing my ownListViewRenderer
from scratch.Related to #2619
Related Xamarin Forum Post
Steps to Reproduce
ListViewRenderer
OnElementChanged
Expected Behavior
Event registration works unless I manually override
UITableView.Delegate
.Actual Behavior
Basic Information
Reproduction Link
ListViewDelegateIssue.zip