Right Justification in Listview

1 Answer 298 Views
ListView
almostEric
Top achievements
Rank 1
Iron
Iron
almostEric asked on 24 May 2023, 02:48 PM
I am using a RadListView with a Horizontal Linear layout.
While I can make the entire listview right justified with HorizontalOptions set to End, the individual items with in the list are still left aligned.
Is there a way to change this?

1 Answer, 1 is accepted

Sort by
0
Lance | Manager Technical Support
Telerik team
answered on 24 May 2023, 03:43 PM

Hi almostEric,

Without your code, I am unable to tell you where the problem lies. So, let me start with a blank slate and show you how to get to what you want... you can then take those lessons learned and apply to your current code or just use mine directly.

The default parent container style uses the default property values of whatever control you put inside it. The ListViewTemplateCell derives from the normal .NET MAUI ViewCell and doesn't override any styles that will affect your contents.

So for example, if we use a Grid, it will be stretched to fill the available space. Then if you wanted to right-align something, you can put that child inside the Grid and right-align it

<DataTemplate>
    <telerik:ListViewTemplateCell>
        <telerik:ListViewTemplateCell.View>
            <Grid>
                <HorizontalStackLayout HorizontalOptions="End">
                    <Label Text="One" />
                    <Label Text="Two" />
                </HorizontalStackLayout>
            </Grid>
        </telerik:ListViewTemplateCell.View>
    </telerik:ListViewTemplateCell>
</DataTemplate>

That said, I propose you use a slightly different approach to get more flexibility... take advantage of the Grid's highly flexible row/column layout features.

<DataTemplate>
    <telerik:ListViewTemplateCell>
        <telerik:ListViewTemplateCell.View>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                
                <!-- This is in the first star-sized column -->
                <Label Text="First column" Grid.Column="0"/>
                
                <!-- This is In the 2nd column, which is using Auto-sized width -->
                <HorizontalStackLayout Grid.Column="1" Spacing="10" Margin="10">
                    <Label Text="Second" />
                    <Label Text="Column" />
                </HorizontalStackLayout>
            </Grid>
        </telerik:ListViewTemplateCell.View>
    </telerik:ListViewTemplateCell>
</DataTemplate>

Regards,
Lance | Manager Technical Support
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

almostEric
Top achievements
Rank 1
Iron
Iron
commented on 24 May 2023, 04:31 PM



I don't think I am explaining myself clearly.

We are using the ListView to display the glassware icons in this screenshot. As you can see most of the beverages have one size, but some have two. We want the list to be right justified. Your solution is about the layout within the template, not the list itself
Lance | Manager Technical Support
Telerik team
commented on 24 May 2023, 07:31 PM | edited

Hi Eric, thank you, the screenshot helps understand the layout a little more, but I still have to make some assumptions again without any code to go on (if you don't want to share your code in public, please open a Support Ticket instead of a forum post).

Review

If I understand correctly, you're using an entire RadListView (inside another RadListView), for those to glassware items in that last column.

Yes, a ListViewLinearLayout will render it's items starting from left to right, into the virtualization panel. Here are some callout using your screeshot.

  • The red box is the viewport for realized items
  • The green boxes are the containers that get realized and recycled
  • The yellow arrow is the direction of scrollview panel, from the first item onwards.

My understanding of what you are asking for, is you would like the items to be right aligned.

Answer

Since the LinearLayout can only render items from the left to right, this means it is not possible to right-align scrolling canvas that extends to the right... (there is just no right edge to align, that edge goes into infinity).

In order for these to be right-aligned, you would need the items panel to start on the right. Then, as new items are added, they get added to the left. This would be a mirror implementation of what you see in the screenshot's callouts.

Alternate Solution

So let me propose a different solution. Nesting one UI virtualized control as a child of another UI virtualized control is usually a recipe for problems anyways. Since I imagine you're not going to have thousands of glassware items, let's get rid of the RadListView, it's too powerful of a control for what you need here.

Instead, use a simple HorizontalStackLayout and programmatically add the items to it. Since you want to reuse this UI, it's best to make it into a ContentView.

Existing Example

For inspiration, I have already written code that does exactly this thing. I wanted a SegmentedView that had RadButtons as items see https://github.com/LanceMcCarthy/CustomXamarinDemos/tree/main/src/SegmentedCustomControl 

The only difference in what you need, and what I did there, is I use RadButton for the children of the StackLayout  here https://github.com/LanceMcCarthy/CustomXamarinDemos/blob/e2b044f1139f403ec277a63a33d9c5b8d42d97fc/src/SegmentedCustomControl/SegmentedCustomControl/Portable/Controls/ButtonSegments.xaml.cs#L135-L162 

In your case, you can just recreate the XAML inside the DataTemplate instead of a RadButton (line 143).

Head Start on Your Custom Control

I give you a pretty good running start by showing you the important parts.

Glassware Data Model

Before starting with any code for the view, I'm starting with this following mockup data model for the glassware items:

public class GlasswareModel
{
    public string ImagePath { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
}
GlasswareItem

Now, we can move the XAML from inside your ItemTemplate into it's own ContentView. For this demo, I've named it "GlasswareItem". Since I don't have your ItemTemplate code, I'll just make it up based on the screenshot:

  • An Image bound to "ImagePath"
  • Two Labels, bound to "Name" and "Price"
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TelerikMauiAppTest"
             x:Class="TelerikMauiAppTest.GlasswareItem"
             x:DataType="local:GlasswareModel">

    <Grid RowDefinitions="Auto,Auto"
          ColumnDefinitions="40, Auto">
        <Image Source="{Binding ImagePath}"
               WidthRequest="38"
               Grid.RowSpan="2"
               Grid.Row="0"
               Grid.Column="0"/>
        <Label Text="{Binding Name}"
               Grid.Column="1"
               Grid.Row="0"/>
        <Label Text="{Binding Price, StringFormat='{0:C2}'}"
               Grid.Column="1"
               Grid.Row="1"/>
    </Grid>
</ContentView>

Note: That does not need any code behind because all the work is done by setting the BindingContext when we instantiate the control.

GlasswareView

Now, for the important parent control, this is the one that will eventually get right-aligned.

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TelerikMauiAppTest.GlasswareView">
    <HorizontalStackLayout x:Name="RootLayout"/>
</ContentView>

In the code-behind, we

  • Have an ItemsSource bindable property
  • In the ItemsSourceChanged method we invoke a DrawItems method
  • Then inside DrawItems, you create a new GlasswareItem UI element and set it's BindingContext to that glassware data item
public partial class GlasswareView : ContentView
{
	public GlasswareView ()
	{
		InitializeComponent();
	}

    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IList<GlasswareModel>), typeof(GlasswareView), null, propertyChanged: OnItemsSourceChanged);

    public IList<GlasswareModel> ItemsSource
    {
        get => (IList<GlasswareModel>)GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }

    private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is GlasswareView self && newValue is List<GlasswareModel> items)
        {
            self.DrawItems();
        }
    }

    // This method clears any existing children and draws a new one for each glassware
    private void DrawItems()
    {
        RootLayout.Children.Clear();
        
        foreach (var glasswareModel in ItemsSource)
        {
            RootLayout.Children.Add(new GlasswareItem
            {
                BindingContext = glasswareModel
            });
        }
    }
}

Implementation

Finally, you can now use the new control in your layout and it can be right-aligned:

<local:GlasswareView ItemsSource="{Binding AvailableGlassware}" HorizontalOptions="End"/>

[EDIT] Fixed typos, phrasing and formatting.

Lance | Manager Technical Support
Telerik team
commented on 24 May 2023, 08:35 PM

Hi almostEric,

Quick follow up, I wanted to share some screenshots of the result at runtime (with some simple data and images):

In case it helps, I've attached the relevant files

almostEric
Top achievements
Rank 1
Iron
Iron
commented on 25 May 2023, 12:07 PM

Hi, Thanks for all your work, I'll take stab at implementing it.
One thing, this is actually a port from an existing WPF app, and making a Telerik RadListView right justify is trivial there :/
Lance | Manager Technical Support
Telerik team
commented on 25 May 2023, 03:10 PM

Excellent! Just in case you had any issues with my files, I've attached a complete runnable project for you.

While I was moving the code to an isolated project, I happened to also put some extra polish on it by surfacing the panel's spacing and padding values through two new BindableProperties. Here's what it looks like now:

and was created with:

<controls:GlasswareView ItemsSource="{Binding AvailableGlassware}"
                        ItemPanelPadding="5"
                        ItemPanelSpacing="5"
                        HorizontalOptions="End"
                        Grid.Column="1" />

*custom properties in bold

About comparison to WPF, I am originally a WPF/Silverlight/Windows Phone/UWP dev myself... so, I can completely relate to the implicit expectations to have the same XAML experience that I've become accustomed to. However, when working with Xamarin.Forms or .NET MAUI, it's good to keep in mind that MAUI doesn't actually use the XAML (except for WinUI native layer).

Instead, .NET MAUI translates the objects into native equivalent items and tries to map the XAML-defined property values into the native equivalent item's properties. However, they don't always have the same features across all the platforms. So, although something might be easy to do in WinUI, it may not be possible in the Android, which means that feature is not raised up to the MAUI layer. It's boils down to the fact that the lowest common denominator feature set achievable everywhere, thus little things like this do creep up from time to time.

Tags
ListView
Asked by
almostEric
Top achievements
Rank 1
Iron
Iron
Answers by
Lance | Manager Technical Support
Telerik team
Share this question
or