Unable to use FlexLayout in a BindableLayout with MAUI - flexbox

I'm trying to use a FlexLayout in the DataTemplate section of a BindableLayout with MAUI. I'm using a StackLayout as a BindableLayout. However, the app does not even start because of an error that seems to come from the Xaml code. Everything is fine when I use a StackLayout or a Grid instead but I want to use a FlexLayout if possible. What am I doing wrong?
Here is a picture of the error:
XAML Error
Exception details 1
Exception details 2
The error message is "Élément introuvable" which means Unable to find element.
Here is the xaml code:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:tests="clr-namespace:Tests"
xmlns:components="clr-namespace:Tests.Components"
Shell.NavBarIsVisible="False"
x:Class="Tests.MainPage">
<StackLayout>
<StackLayout x:Name="Items">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="tests:Phone">
<FlexLayout>
<Image MaximumHeightRequest="150" Source="{Binding Image}"/>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Price}"/>
<Label Text="{Binding Year}"/>
</FlexLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentPage>
Here is the .cs code:
public partial class MainPage : ContentPage {
private ObservableCollection<Phone> phones = new();
public MainPage() {
InitializeComponent();
InitPhoneCollection();
}
private void InitPhoneCollection() {
Phone phone1 = new Phone {
Name = "iPhone",
Price = 100,
Year = "2019",
Image = "iphone.jpg"
};
Phone phone2 = new Phone {
Name = "Samsung",
Price = 150,
Year = "2021",
Image = "samsung.jpg"
};
Phone phone3 = new Phone {
Name = "Blackberry",
Price = 200,
Year = "2022",
Image = "blackberry.jpg"
};
phones.Add(phone1);
phones.Add(phone2);
phones.Add(phone3);
BindableLayout.SetItemsSource(Items, phones);
}
}
Thanks
I tried a StackLayout and a Grid instead but for best results but I would prefer a FlexLayout.

This is a known issue that being tracked in the links below, you can follow up there.
https://github.com/dotnet/maui/issues/9905
https://github.com/dotnet/maui/issues/11909
https://github.com/dotnet/maui/issues/9075
As an alternative workaround, you can use StackLayout or Grid instead of FlexLayout like below:
<StackLayout x:Name="Items">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="tests:Phone">
<StackLayout>
<Image MaximumHeightRequest="150" Source="{Binding Image}"/>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Price}"/>
<Label Text="{Binding Year}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>

Related

ObservableValidator CustomValidation validates too early

I have a .Net MAUI app that uses CommunityToolkit.Mvvm. The viewmodel is validated using ObservableValidator with custom validation:
public partial class BasePageModel : ObservableValidator
...
public partial class DistributorsLocatorPageModel : BasePageModel
{
...
[ObservableProperty]
[CustomValidation(typeof(DistributorsLocatorPageModel), nameof(ValidateProductLines))]
private List<ProductLineListItem> _productLines;
...
public static ValidationResult ValidateProductLines(List<ProductLineListItem> productLines, ValidationContext context)
{
bool isValid = productLines.Where(pl => pl.Selected).Any();
if (isValid)
{
return ValidationResult.Success;
}
return new("Please Choose Product Line!");
}
Here is XAML:
<StackLayout BindableLayout.ItemsSource="{Binding ProductLines}" >
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="viewmodel:ProductLineListItem">
<Grid
ColumnDefinitions="*,auto"
Margin="0,0,0,10">
<StackLayout VerticalOptions="CenterAndExpand" Grid.Column="0">
<Label Text="{Binding Name}" />
</StackLayout>
<Switch IsToggled="{Binding Selected}" Style="{StaticResource SwitchStyle}" Grid.Column="1" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
For some reason, this validation fires without any call to ValidateAllProperties(), as soon as the view is loaded, thus always showing the error message "Please Choose Product Line!" before the user has chance to choose anything. What am I missing?
P.S. Regular (non-custom) validation behaves as expected.

Login detail across all pages of xamarin portable forms

How can i display Login detail of user across all pages in xamarin portable project(iOS and Andriod). I am not able to implement MasterPage concept as we use to do in ASP.net or Frame in Silverlight.
I have already gone through MasterDetailPage and I think it doesn't fulfill my requirement.
Create a static variable UserName in your App.cs file.
public class App : Application
{
public static bool IsUserLoggedIn { get; set; }
public static string UserName { get; set; }
public App()
{
if (!IsUserLoggedIn)
{
MainPage = new NavigationPage(new LoginPage());
}
else
{
MainPage = new NavigationPage(new MenuPage());
}
}
}
On LoginPage.cs file, store the username in static variable of App.cs file when user login for the first time. I am assuming that you have login page with two entry fields such as username and password field along with a login button. So when user click on login button store the username from entry field as :
App.UserName = usernameEntry.Text.ToString();
On successful login authentication you might redirect to MenuPage.
On MenuPage.cs file you can easily access the static variable of App.cs file as :
public partial class MenuPage : MasterDetailPage
{
public MenuPage()
{
InitializeComponent();
string name = App.UserName;
}
}
Likewise you can access this Static variable across any pages of your portable project. This static variable will persist till you don't remove the app from the device's current running application tray list of android/ios.
Sample project of master detail page :
https://github.com/xamarin/xamarin-forms-samples/tree/master/Navigation
Sample project of Login flow :
https://github.com/xamarin/xamarin-forms-samples/tree/master/Navigation/LoginFlow
Here is something that you want to do:
on login you get the image and name to display on the drawer header and you store in a constant.
your Drawer page here
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MasterDetailsPageLogin.Views.DrawerPage"
Title="Menu"
BackgroundColor="#8999A6"
Padding="0,20,0,0">
<StackLayout Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<BoxView Grid.ColumnSpan="3"
Grid.RowSpan="4"
BackgroundColor="#8999A6"/>
<Image
x:Name="img"
Grid.Column="1"
Grid.Row="1"
HorizontalOptions="Start"
VerticalOptions="End"
WidthRequest="75" HeightRequest="75"/>
<Label
x:Name="myName"
Grid.Column="1"
Grid.Row="2"/>
</Grid>
<ListView x:Name="listed" SeparatorColor="Transparent">
</ListView>
</StackLayout>
</ContentPage>
Code in your drawerpage.xaml.cs
public DrawerPage()
{
InitializeComponent();
string howILook = Constants.Image;
img.Source = new UriImageSource
{
Uri = new Uri(howILook),
CachingEnabled = true,
CacheValidity = new TimeSpan(1, 0, 0, 0) //Caching image for a day
};
myName.Text = "Welcome Mr. " + Constants.Username;
}
Here is a template I have created for the login logic and master details page.
https://github.com/ak47akshaykulkarni/MasterDetailsPageLogin

Pure XAML approach to evenly distributing an unknown number of children in a container

Let's say I have a StackPanel (Orientation="Horizontal") with a fixed width and a variable/unknown amount of children, how do I tell all the children in XAML (not in code) to set their widths' to all be equal and fill up the entire StackPanel's width?
It's easy enough to do with Grid when you know the number of children (just use ColumnDefinitions with Width="*"), but I'm not sure how to do it with panels (StackPanel, PivotHeaderPanel, etc) or lists (GridView, ListView, etc).
According to your description, what you need is a panel like the UniformGrid in WPF. UniformGrid provides a way to arrange content in a grid where all the cells in the grid have the same size. However there is no build-in UniformGrid in UWP. We can implement it by ourselves or use a third party UniformGrid like what in WinRTXamlToolkit.
Here using the UniformGrid in WinRTXamlToolkit for example. To use WinRTXamlToolkit, we can install it from nuget: WinRT XAML Toolkit for Windows 10.
Then add xmlns in the page like:
xmlns:toolkit="using:WinRTXamlToolkit.Controls"
After this, we can use the UniformGrid like:
<toolkit:UniformGrid Rows="1" />
Here I set the Rows property to 1 and the Columns property to the default value 0. A value of zero (0) for the Columns property specifies that the column count is computed based on the number of rows and the number of visible child elements that are in the Grid. So all the children in this UniformGrid will be put in one row and as this is a UniformGrid, all the columns will have the same width.
Following is a sample that uses UniformGrid as the ItemsPanel of a GridView:
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<GridView x:Name="MyGridView" Height="60">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:UniformGrid Rows="1" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Background="Red">
<TextBlock HorizontalAlignment="Center" FontSize="30" Text="{Binding }" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button Click="Button_Click">Add a item</Button>
</StackPanel>
</Grid>
Code-behind:
public sealed partial class MainPage : Page
{
private ObservableCollection<string> myList = new ObservableCollection<string> { "1", "2", "3", "4", "5" };
private int i = 6;
public MainPage()
{
this.InitializeComponent();
MyGridView.ItemsSource = myList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myList.Add(i.ToString());
i += 1;
}
}
And it looks like:

How to set textwrapping in Button

did a search on internet, could not find the solution. I think I missed something for below code to make the text wrap:
<Button x:Name="btnCustomerAging" Background="Green" BorderBrush="Green" Foreground="White" FontSize="33" HorizontalAlignment="Left" Margin="662,106,0,0" Grid.Row="1" VerticalAlignment="Top" Height="213" Width="238">
<TextWrapping>
Customer Locations
</TextWrapping>
</Button>
This will work.
<Button x:Name="btnCustomerAging" Background="Green" BorderBrush="Green" Foreground="White" FontSize="33"
HorizontalAlignment="Left" Margin="662,106,0,0" Grid.Row="1" VerticalAlignment="Top" Height="213" Width="238">
<TextBlock Text="Customer Locations" TextWrapping="Wrap" />
</Button>
You can create your own WrapButton and use it in XAML like this:
<local:WrapButton x:Name="MyButton" Text="Text that will wrap"/>
This is the code for WrapButton:
public sealed class WrapButton : Button
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(WrapButton), new PropertyMetadata(string.Empty));
public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
public WrapButton()
{
var textBlock = new TextBlock { TextAlignment = TextAlignment.Center, TextWrapping = TextWrapping.Wrap };
textBlock.SetBinding(TextBlock.TextProperty, new Binding { Source = this, Path = new PropertyPath("Text") });
Content = textBlock;
}
}

print the text box value in list view in windows phone 8

here I attach a code to get the value in text block, but I want to print the txt1 and date1 value in list view
xaml code
<ListBox Margin="0,0,-12,0" x:Name="myList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="txt1" Text="{Binding date1}" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<TextBlock x:Name="txt2" Margin="5,0,0,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
c# code
textBlockNumber.Text = "1232"
txt1.Text = textBlockNumber.Text;
DateTime date1 = DateTime.Now;
you will have to add a item source of the list box ..
List<data> asd = new List<data>);
asd.Add(new data(textBlockNumber.Text,DateTime.Now));
myList.ItemSource = asd;
make a data class
public class data
{
public string liststing {get;set;}
public DateTime datetime {get;set;}
public data(string textblocktext,DateTime date)
{
this.liststring = textblocktext;
this.datetime = date;
}
}
then bind the text of textblock in xaml like Text="{Binding liststring}" and your other Text="{Binding datetime}"
i hope it helps ...

Resources