Setting delegate of control in custom renderer results in lost functionality

ChrisNielsenChrisNielsen DKMember
edited June 2015 in Xamarin.Forms

I am trying to make a custom ListView that can track where the list is in the view.
To do this I need to set the delegate of the control, but if I do this I loose original functionality (Like ItemSelected).

I guess that Xamarin have made a delegate for the ListView and I am setting my CustomListView to a new delegate, but I can't "get" a delegate from control.delegate.

Should I override Xamarins delegate or how can I do this?

My CustomListView Renderer

public class CustomListViewRenderer : ListViewRenderer, IUITableViewDelegate, IUIScrollViewDelegate
{
    CustomListView list;

    protected override void OnElementChanged (ElementChangedEventArgs<ListView> e)
    {
        base.OnElementChanged (e);

        if (list == null)
            list = this.Element as CustomListView;

        Control.WeakDelegate = this;
    }

    [Export ("scrollViewDidScroll:")]
    public void Scrolled (UIKit.UIScrollView scrollView)
    {
        System.Diagnostics.Debug.WriteLine (scrollView.ContentOffset.Y.ToString());
    }
}

Best Answer

Answers

  • ChrisNielsenChrisNielsen DKMember

    @JohnMiller Thank you for the quick answer :)

    Just to be sure, is this what you mean?

    [Export ("tableView:didSelectRowAtIndexPath:")]
    public void RowSelected (UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
    {
        oldDelegate.RowSelected (tableView, indexPath);
    }
    

    And I need to do this for every method that ListView uses (At least the ones I need)?

  • ChrisNielsenChrisNielsen DKMember

    Another thing.

    The variable that is going to hold the existing delegate have to be public.
    When I try to get the type of the existing delegate I get this "Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource"

    I have never seen a plus operator in a type name, what is this?

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    @ChrisNielsen,

    Yes, that is what I was thinking. The + sign is the way reflection denotes a nested type.

  • ChrisNielsenChrisNielsen DKMember
    edited June 2015

    @JohnMiller I can't figure out what type to use for the variable that is going to hold the existing delegate?
    I can't say var oldDelegate = Control.weakDelegate; because I need the variable to be public, and I don't know how to allocate it with the type of "Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource"
    _
    "The class ListViewDataSource is internal"_

  • ChrisNielsenChrisNielsen DKMember

    If I use "UITableViewSource" as a type for my public variable and cast the existing delegate to "UITableViewSource" I am getting the error "Exception of type 'Foundation.You_Should_Not_Call_base_In_This_Method' was thrown." when I use it in WillSelectRow

    public UITableViewSource oldDelegate;
    //...
    oldDelegate = (UITableViewSource)Control.WeakDelegate;
    //...
    [Export ("tableView:willSelectRowAtIndexPath:")]
    public Foundation.NSIndexPath WillSelectRow (UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
    {   
    return oldDelegate.WillSelectRow(tableView, indexPath); <- Error
    }
    
  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    Bummer, I didn't realize it was internal. :(

    I don't think this is possible then, unfortunately. What do you want to customize / add?

  • ChrisNielsenChrisNielsen DKMember

    @JohnMiller I just realized that WillSelectRow is not a part of the Xamarin assembly.
    It's working like a dream now :)

    Thank you for your help!

  • ChrisNielsenChrisNielsen DKMember
    edited June 2015

    If someone is interested in what the final code is looking like.

    public class MyListViewRenderer : ListViewRenderer, IUITableViewDelegate, IUIScrollViewDelegate
    {
        MyListView list;
        public UITableViewSource oldDelegate;
    
        protected override void OnElementChanged (ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged (e);
    
            if (list == null)
                list = this.Element as MyListView;
    
            if (oldDelegate == null &&
                Control.WeakDelegate != null)
            {
                oldDelegate = (UITableViewSource)Control.WeakDelegate;
            }
    
            Control.WeakDelegate = this;    
        }
    
        // We can see what functions Xamarin already implemented in the ListViewRenderer assembly.
        [Export ("tableView:didSelectRowAtIndexPath:")]
        public void RowSelected (UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
        {
            oldDelegate.RowSelected (tableView, indexPath);
        }
    
        [Export ("scrollViewDidScroll:")]
        public void Scrolled (UIKit.UIScrollView scrollView)
        {
            System.Diagnostics.Debug.WriteLine (scrollView.ContentOffset.Y.ToString());
        }
    }
    
  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    Oops, I didn't comprehend the error message you mentioned at first. :sweat_smile:

    Glad you figured it out though!

  • CarlosForeroCarlosForero USUniversity ✭✭

    Thanks @ChrisNielsen

  • AlanSpiresAlanSpires USBeta ✭✭

    By setting the Control.WeakDelegate = this you lose a lot of built in functionality. For instance if that was a grouped listiview you would notice that grouping no longer works. You technically need to wrap all the methods used by the internal datasource.

    https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs#L724

    So everything thats "overriden" in ListViewDataSource needs to be exported and pointed back to the oldDelegate.

Sign In or Register to comment.