3

I would like to have my columns in my ListViews ViewCell to be all of the same size. It is currently set to auto and the widest name should win and all other columns should be set to the widest labels width. Currently they have all different widths in each row.

ListView ItemTemplate Example:

<ViewCell>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" /> /
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Text="{Binding Name}"/>
[....]

In WPF we have the property SharedSizeGroup, is there something similar in Xamarin.Forms? Is there a workaround without hacking too much?

Jannik
  • 2,310
  • 6
  • 32
  • 61

1 Answers1

3

Unfortunately, no - there is no such property.

This is a rather difficult thing to do since you need to measure all rows before you can figure out which one has the widest text. There is not a cross-platform way to do that. There are ways to do it at the platform level for Android and iOS, but I don't think there is a way for WinPhone.

[edited to reflect a better approach identified in the comments]:

An alternative is to use OneWayToSource bindings and keeping track of the ColumnDefinition entries for each listview row.

XamlPage.Xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="FormsSandbox.XamlPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0" Android="0" WinPhone="0"/>
    </ContentPage.Padding>

    <AbsoluteLayout>
        <ListView x:Name="MyLV" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid RowSpacing="0" ColumnSpacing="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Label Grid.Column="0" Text="{Binding .}" MinimumWidthRequest="50" HorizontalOptions="StartAndExpand" SizeChanged="LabelSizeChanged" />
                            <Label Grid.Column="1" Text="Second Column"/>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </AbsoluteLayout>

</ContentPage>

XamlPage.xaml.cs:

using System;
using Xamarin.Forms;
using System.Collections.Generic;

namespace FormsSandbox
{
    public partial class XamlPage : ContentPage
    {
        private double _colSize = 0.0;
        private List<ColumnDefinition> _columns = new List<ColumnDefinition>();

        public XamlPage ()
        {
            InitializeComponent ();

            var data = new List<string> ();

            data.Add ("Lorem ipsum");
            data.Add ("Foo");
            data.Add ("Dolor semet");
            data.Add ("Test");
            data.Add (".");
            data.Add ("Xamarin Forms Is Great");
            data.Add ("Short");
            data.Add ("Longer than Short");
            data.Add ("");
            data.Add ("Hyphenated");
            data.Add ("Non-hyphenated");
            data.Add ("Ironic, eh?");

            MyLV.ItemsSource = data;
        }

        public void LabelSizeChanged (object sender, EventArgs e)
        {
            var label = (Label)sender;
            var grid = (Grid)label.Parent;
            var column = grid.ColumnDefinitions [0];
            if (!_columns.Contains (column)) {
                _columns.Add (column);
            }
            var adjustments = new List<ColumnDefinition> ();
            if (label.Width > _colSize) {
                _colSize = label.Width;
                adjustments.AddRange (_columns);
            } else {
                adjustments.Add (column);
            }
            foreach (var col in adjustments) {
                col.Width = new GridLength (_colSize);
            }
        }
    }
}

Screenshot of current solution

Keith Rome
  • 3,208
  • 19
  • 15
  • Ok, I see. Do you want me to Bind the font aswell and font size awell to compute the width or do you have something similar in mind? – Jannik Apr 13 '16 at 05:40
  • My example uses a slider to adjust the simple multiplier for pixels per character. Obviously for any fonts that aren't fixed-width (most fonts are not), this will not be accurate. But it is simple and might be accurate enough for you. If you need higher accuracy then on iOS and Android you can add some native code to measure the text (it will take into account Font settings). The simplest way to do this is by using DependencyService to provide the measuring code from the native projects. If you haven't used that technique before then let me know and I'll update the answer to include it. – Keith Rome Apr 13 '16 at 23:49
  • I havent used it before, sorry for the inconvience... I rethought my question last night: Wouldn't it be possible to bind the width of each label (one way to source) and bind width of all the rows in the template to use the widest Labels width? Or is that a bad approach? – Jannik Apr 14 '16 at 05:04
  • You could try it - it might work. Be sure to bind the `Width` and not `WidthRequest` so that it operates on the actual width after layout. – Keith Rome Apr 14 '16 at 14:36
  • Actually, that works great. I'll update my answer with the (much simplified) version that does it that way. – Keith Rome Apr 14 '16 at 14:53
  • Thank you, it worked. See also my second answer for a bit of a sidenote – Jannik Apr 18 '16 at 05:28
  • Thanks, I agree that shared sizing groups would be a nice addition, but I also understand why it wasn't done - the way table rows are handled on iOS/Android makes it really difficult to get right. – Keith Rome Apr 21 '16 at 03:59
  • Maybe one day I will customize / extent the RowDefinition / ColumnDefinition and implement the "hack" and a new property, could be of great use for others aswell. – Jannik Apr 21 '16 at 05:01