Cell insets when using ContextActions?

KeithRomeKeithRome USUniversity ✭✭

I've not found a solution to this, despite spending a lot of time attempting to defeat some internal implementations of the ListView / Cell renderers. I'm hoping someone else has tackled this and found a way to do it. What I'm doing is fairly simple - if you create a new empty Forms app and add a ListView, by default all of the rows will be inset on the left side (in iOS). I wish to remove that inset so that the separator lines go all the way to the edge of the view - like the default behavior in Android. I have a set of custom renderers that can do this - however as soon as I add ContextActions to a cell, my tweak no longer works as well. Here is some example code if an app that demonstrates the problem:

public class App : Application
{
    public App ()
    {
        var data = new [] { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };

        var grid = new Grid () {
            RowDefinitions = new RowDefinitionCollection () {
                new RowDefinition () { Height = new GridLength (0.5, GridUnitType.Star) },
                new RowDefinition () { Height = new GridLength (0.5, GridUnitType.Star) }
            }
        };
        grid.Children.Add (new ListView {
            ItemsSource = data,
            ItemTemplate = new DataTemplate (() => MakeTemplate(false))
        });
        grid.Children.Add (new ListView {
            ItemsSource = data,
            ItemTemplate = new DataTemplate (() => MakeTemplate(true))
        });
        grid.Children [1].SetValue (Grid.RowProperty, 1);

        MainPage = new ContentPage {
            Padding = new Thickness (0, 20, 0, 0),
            Content = grid
        };
    }

    static TextCell MakeTemplate (bool withContextActions)
    {
        var cell = new TextCell ();
        if (withContextActions) {
            cell.ContextActions.Add (new MenuItem () { Text = "Demo" });
        }
        cell.SetBinding (TextCell.TextProperty, ".");
        return cell;
    }
}

This is fairly straightforward - it creates two ListViews. The first one does not use ContextActions, but the second one does. These appear identically when using default renderers, but they also have the annoying left inset. To correct the inset, here is my custom renderer implementation:

[assembly: ExportRenderer(typeof(ListView), typeof(CustomListViewRenderer))]
[assembly: ExportRenderer(typeof(TextCell), typeof(CustomTextCellRenderer))]

public class CustomListViewRenderer : ListViewRenderer
{
    protected override void UpdateNativeWidget ()
    {
        // remove extra lines in a table
        Control.TableFooterView = new UIView (CGRect.Empty);

        base.UpdateNativeWidget ();

        // fix the default insets on table rows
        if (Control.RespondsToSelector (new Selector ("setSeparatorInset:"))) {
            Control.SeparatorInset = UIEdgeInsets.Zero;
        }
        if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
            Control.LayoutMargins = UIEdgeInsets.Zero;
        }
    }
}

public class CustomTextCellRenderer : TextCellRenderer
{
    public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
    {
        var cell = base.GetCell(item, reusableCell, tv);

        // fix the default inset on table cells
        if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
            cell.LayoutMargins = UIEdgeInsets.Zero;
        }

        return cell;
    }
}

After dropping these two renderers into the iOS project, the top ListView works properly however the bottom ListView has additional padding that is no longer being compensated for (the extra padding only seems to affect the separator line and not the cell content). It seems there is some private implementation of the default renderer that does some rather weird things when ContextActions are present on a cell. I've not found a way around this - has anyone tried and succeeded yet?

Answers

Sign In or Register to comment.