locked
Setting delegate of control in custom renderer results in lost functionality RRS feed

  • Question

  • User113462 posted

    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());
        }
    }
    
    Thursday, June 4, 2015 8:12 PM

Answers

  • User37696 posted

    @ChrisNielsen,

    You're correct that Xamarin.Forms is already using that Delegate. If you set it to something else, you'll lose all the built-in functionality. To work around that, you could copy the existing delegate into a new variable, then in your custom delegate, call those same methods on that variable, with any customizations following it.

    I think that should work :wink:

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, June 4, 2015 8:14 PM

All replies

  • User37696 posted

    @ChrisNielsen,

    You're correct that Xamarin.Forms is already using that Delegate. If you set it to something else, you'll lose all the built-in functionality. To work around that, you could copy the existing delegate into a new variable, then in your custom delegate, call those same methods on that variable, with any customizations following it.

    I think that should work :wink:

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, June 4, 2015 8:14 PM
  • User113462 posted

    @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)?

    Thursday, June 4, 2015 8:50 PM
  • User113462 posted

    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?

    Friday, June 5, 2015 1:07 AM
  • User37696 posted

    @ChrisNielsen,

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

    Friday, June 5, 2015 2:22 PM
  • User113462 posted

    @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"_

    Friday, June 5, 2015 3:06 PM
  • User113462 posted

    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.YouShouldNotCallbaseInThis_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
    }
    
    Friday, June 5, 2015 3:23 PM
  • User37696 posted

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

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

    Friday, June 5, 2015 3:32 PM
  • User113462 posted

    @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!

    Friday, June 5, 2015 3:37 PM
  • User113462 posted

    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());
        }
    }
    
    Friday, June 5, 2015 3:53 PM
  • User37696 posted

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

    Glad you figured it out though!

    Friday, June 5, 2015 5:31 PM
  • User155499 posted

    Thanks @ChrisNielsen

    Friday, January 13, 2017 4:56 AM
  • User1884 posted

    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.

    Sunday, April 23, 2017 5:41 PM