0

I am having an issue with laying out ItemTemplate in FlexLayout in maui. FlexLayout seems to act strange with laying out the item sources to it.

Here is a very simple code;

<DataTemplate x:Key="AndroidItemTemplate">
  <Label Text="{Binding .}" FontSize="18" LineBreakMode="WordWrap"/>
</DataTemplate>

<ScrollView>
  <FlexLayout JustifyContent="Start" 
              BindableLayout.ItemsSource="{Binding Sample}"
              BindableLayout.ItemTemplate="{StaticResource AndroidItemTemplate}">
  </FlexLayout>
</ScrollView>

MVVM with sets of string values;

public RangeObservableCollection<string> Sample { get; private set; } = new();
Sample.AddRange(new List<string>()
        {
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
            $"Hello, How are you",
        });

This is what is happening in Android I haven't tested it in iOS. Android Sample

However, I want that the text should appear next to each other until the row ends. Then it should start with the 2nd row and go on.

Should Show:

Hello, How are you Hello, How are you Hello, How are you Hello, How are you Hello, how are you Hello, how are you Hello, how are you...

ARH
  • 1,566
  • 3
  • 25
  • 56

2 Answers2

0

Bindable FlexLayout in MAUI is not Laying childs properly

The cause of this situation seems to be related to ViewModel.

Here is my code:

<ContentPage.Resources>
       <DataTemplate x:Key="AndroidItemTemplate">
              <Label Text="{Binding .}" FontSize="18" LineBreakMode="WordWrap"/>
       </DataTemplate>
</ContentPage.Resources>
<ScrollView>
       <FlexLayout JustifyContent="Start" 
                   BindableLayout.ItemsSource="{Binding Sample}" 
                   BindableLayout.ItemTemplate="{StaticResource AndroidItemTemplate}">
       </FlexLayout>
</ScrollView>

Page.xaml.cs:

public partial class MainPage : ContentPage    
{
   public MainPage()        
   {
        InitializeComponent ();
        BindingContext = new VM ();
   }
}

ViewMdodel:

public class VM
{
    public ObservableCollection<string> Sample { get; private set; } = new();
    public VM()
    {
        gen();
    }
    void gen()
    {
        Sample.
        Add("Hello, How are you Hello, How are you Hello, How are you Hello,"+
             "How are you Hello,Hello, How are you Hello, How are you Hello,"+ 
             "How are you Hello, How are you Hello, How are you Hello,");
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");            
        //Sample.Add($"Hello, How are you");                    
    }
}

When add only one item to Sample, it can display the effect that the text should appear next to each other until the row ends. Then it should start with the 2nd row and go on.

However, when you add more than one item to Sample, it will generate multiple labels in xaml. The effect you want cannot be achieved between labels, whether in FlexLayout or other layouts.

Jianwei Sun - MSFT
  • 2,289
  • 1
  • 3
  • 7
  • Actually, you didn't get the problem. I don't have issues with ViewModel/binding. I am adding multiple elements to the observable collection. It is a range observable that accepts an array. So I am adding a list of strings to the collection in the ViewModel. I am having an issue with the FlexLayout not placing the label correctly. – ARH Feb 09 '23 at 18:01
  • @ARH Yes, you are right. I tried to set [some properties to FlexLayout](https://learn.microsoft.com/en-us/dotnet/maui/user-interface/layouts/flexlayout?view=net-maui-7.0#wrap) by official doc but failed. I also attempted to use other Layouts but failed to achieve the effect you want. As my answer finally said, when add only one item to the observable collection, it's ok. In addition, you can [raise an issue to GitHub about FlexLayout](https://github.com/dotnet/maui/issues). – Jianwei Sun - MSFT Feb 10 '23 at 01:48
0

You have it almost right. Try this.

Page.xaml.cs

public partial class MainPage : ContentPage    
{
   public MainPage(VM viewmodel)        
   {
        InitializeComponent ();
        BindingContext = viewmodel;
   }
}

in MauiProgram.cs add:


public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>().ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
        }).UseMauiCommunityToolkit().UseMauiCommunityToolkitMediaElement();
#if DEBUG
        builder.Logging.AddDebug();
#endif
        builder.Services.AddSingleton<VM>();   // ADD view model here
        builder.Services.AddSingleton<Page>(); // ADD Page here
        return builder.Build();
    }
}

This is assuming you are using the MVVM model and are also using the community toolkit. But you need to add the above for dependancy injection.

Make sure to include the model, ViewModel, Class and data types at top of xaml page too like this.

 xmlns:model="clr-namespace:YourProgramName.Model"
             xmlns:viewmodel="clr-namespace:DirectoryOfViewModel.ViewModel"
             x:Class="YourProgramName.View.Page"
             x:DataType="viewmodel:PageViewModel"

adjust the above as needed for your program.

Here is a example of this in a Program I am developing atm. TabletPodcastPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:model="clr-namespace:NerdNewsNavigator2.Model"
             xmlns:viewmodel="clr-namespace:NerdNewsNavigator2.ViewModel.Tablet"
             x:Class="NerdNewsNavigator2.View.Tablet.TabletPodcastPage"
             x:DataType="viewmodel:TabletPodcastViewModel"
             BackgroundColor="Wheat"
               Shell.NavBarIsVisible="False"
             Title="">
    <Shell.BackButtonBehavior>
        <BackButtonBehavior IsEnabled="True" IsVisible="False"></BackButtonBehavior>
    </Shell.BackButtonBehavior>
    <CollectionView ItemsSource="{Binding Podcasts}" Margin="5" BackgroundColor="Wheat" SelectionMode="None">
        <CollectionView.ItemsLayout>
            <GridItemsLayout Orientation="Vertical" Span="{Binding Orientation}"/>
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Podcast">
                <Grid RowDefinitions="30,400,*" ColumnDefinitions="400">
                    <Grid.GestureRecognizers>
                        <TapGestureRecognizer 
                            Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:TabletPodcastViewModel}}, Path=TapCommand}"
                            CommandParameter="{Binding Url}"/>
                    </Grid.GestureRecognizers>
                    <Label  Grid.Row="0"
                        Text="{Binding Title}" 
                        TextColor="Black" 
                            HorizontalTextAlignment="Center"
                        FontSize="16" 
                            FontAttributes="Bold"
                        LineBreakMode="WordWrap"/>
                    <Image Grid.Row="1"
                    Aspect="AspectFit" 
                    MaximumHeightRequest="400" 
                    MaximumWidthRequest="400">
                        <Image.Source>
                        <UriImageSource Uri="{Binding Image}"
                                            CacheValidity="40"
                                            CachingEnabled="True"/>
                        </Image.Source>
                    </Image>
                    <Label Grid.Row="2"
                           Margin="5"
                           Text="{Binding Description}" 
                           FontSize="12" 
                           TextColor="Black" 
                           LineBreakMode="WordWrap"/>
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

TabletPodcastPage.xaml.cs

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace NerdNewsNavigator2.View.Tablet;

public partial class TabletPodcastPage : ContentPage
{
    public TabletPodcastPage(TabletPodcastViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
    protected override bool OnBackButtonPressed()
    {
        Application.Current.Quit();
        return true;
    }
}

MauiProgram.cs

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if WINDOWS
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Windows.Graphics;
#endif
using NerdNewsNavigator2.Data;

namespace NerdNewsNavigator2;
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>().ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
        }).UseMauiCommunityToolkit().UseMauiCommunityToolkitMediaElement();
#if WINDOWS
        builder.ConfigureLifecycleEvents(events =>
               {
                   events.AddWindows(wndLifeCycleBuilder =>
            {
                wndLifeCycleBuilder.OnWindowCreated(window =>
                {
                    window.ExtendsContentIntoTitleBar = false;
                    IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
                    WindowId myWndId = Win32Interop.GetWindowIdFromWindow(hWnd);
                    var appWindow = AppWindow.GetFromWindowId(myWndId);
                    appWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
                });
            });
               });
#endif

#if DEBUG
        builder.Logging.AddDebug();
#endif
        builder.Services.AddTransient<FirstPage>();
        builder.Services.AddTransient<FirstVieModel>();

        builder.Services.AddSingleton<PhonePodcastPage>();
        builder.Services.AddSingleton<PhonePodcastViewModel>();

        builder.Services.AddTransient<PhoneShowPage>();
        builder.Services.AddTransient<PhoneShowViewModel>();

        builder.Services.AddTransient<PhonePlayPodcastPage>();
        builder.Services.AddTransient<PhonePlayPodcastViewModel>();

        builder.Services.AddTransient<PhoneLivePage>();
        builder.Services.AddTransient<PhoneLiveViewModel>();

        builder.Services.AddSingleton<DesktopPodcastPage>();
        builder.Services.AddSingleton<DesktopPodcastViewModel>();

        builder.Services.AddTransient<DesktopShowPage>();
        builder.Services.AddTransient<DesktopShowViewModel>();

        builder.Services.AddTransient<DesktopPlayPodcastPage>();
        builder.Services.AddTransient<DesktopPlayPodcastViewModel>();

        builder.Services.AddTransient<DesktopLivePage>();
        builder.Services.AddTransient<DesktopLiveViewModel>();

        builder.Services.AddSingleton<TabletPodcastPage>();
        builder.Services.AddSingleton<TabletPodcastViewModel>();

        builder.Services.AddTransient<TabletShowPage>();
        builder.Services.AddTransient<TabletShowViewModel>();

        builder.Services.AddTransient<TabletPlayPodcastPage>();
        builder.Services.AddTransient<TabletPlayPodcastViewModel>();

        builder.Services.AddTransient<TabletLivePage>();
        builder.Services.AddTransient<TabletLiveViewModel>();

        builder.Services.AddTransient<LivePage>();
        builder.Services.AddTransient<LiveViewModel>();

        builder.Services.AddSingleton<TwitService>();
        builder.Services.AddSingleton<FeedService>();
        builder.Services.AddSingleton<Position>();

        builder.Services.AddSingleton<PlaybackService>();
        builder.Services.AddSingleton<PositionServices>();

        builder.Services.AddSingleton<PositionDataBase>();
        builder.Services.AddSingleton<FileService>();

        return builder.Build();
    }
}

That example above works in my program. It is an example for you to work from. It is not complete. It is missing all the things like model, data, etc. This is just so you have an idea for where to go from here.