xamarin System.InvalidOperationException: 'Cannot convert "VerticalGrid, 2" into Xamarin.Forms.IItemsLayout' - xamarin.forms

I'm getting the error System.InvalidOperationException: 'Cannot convert "VerticalGrid, 2" into Xamarin.Forms.IItemsLayout' during InitalizeComponent() of my ContentPage containing a CollectionsView.
The code works properly on UWP, and the error occurs when running on Android. Being new to xamarin.forms, I'm not really sure what to start looking for.
EDIT: it works if I choose "VerticalList".
Here's a bit of my xaml:
<ContentPage ... >
<StackLayout>
<CollectionView
x:Name="DetailGrid"
ItemsLayout="VerticalGrid, 2" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="20" BackgroundColor="Crimson">
<Image Source= "{Binding Path}" WidthRequest="300"/>
<Label Text="{Binding FileName}" TextColor ="Bisque" LineBreakMode="WordWrap" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
And here's some code-behind:
namespace varlist
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CollectionPage : ContentPage
{
public ObservableCollection<NodeData> nodes = new ObservableCollection<NodeData>();
public CollectionPage ()
{
InitializeComponent ();
nodes.Add (new ImageData { FileName = "image_chair_pk.jpg" });
...
DetailGrid.ItemsSource = nodes;
}
}
}

I got the same error with you when I use a Xamarin.forms version 4.5.0.725.
After I update my Xamarin.forms to the latest version 4.8.0.1687, everything works well.
Please update your Xamarin.forms version to fix this problem:

I found a workaround. This appears to be a framework bug.
According to Xamarin.Forms CollectionView Layout the syntax I used should work - and it does work for UWP.
They also show an alternate declaration to specify ItemsLayout:
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" />
</CollectionView.ItemsLayout>
And, this syntax works on Android.
I filed a bug on github:[Bug] CollectionView with ItemsLayout=VerticalGrid crashes on Android #12920

You can omit this property if you wish it vertical, the default is set to VerticalList. If you want your collectionView to be Horizontal, just use HorizontalList.
See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout

Related

Images not shown in CarouselView on first load

The scenario is, a content page is opened with a carousel displaying multiple images.
The images are bound as an observable collection of ImageSource and they're fine
When the page is displayed, I can see the carousels empty.
I have tried with two different CarouselView. The one that xamarin brings by default and the one from CardsView nuget library. With both of them the result is the same.
The page shows the carousel views without the image, but with the right amount of items (i.e: I can see that the observable collection has 3 items).
I suspect it must be some kind of racing condition because as soon as I force a hot reload of the view while debuggin (ie: I save the XAML and the view reloads in the device) I can see the carousels with the images displayed properly.
To reproduce it I have also added the images separately outside the carousel to prove that they're there.
I've also added both carousels to the view. This is the result on the first load:
and after I save the xaml (without doing anything on it at all) and the view refreshes, this is what I see, and it's correct.
Notice how in the first load, the 3 images are displayed correctly outside the carousel, but not inside any of the carousels, although there is no problem if what I add in the carousel is text.
Any idea where may be the issue? Is it a racing condition? Or is it a problem with both different carousels?
This is my code
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Sasw.AforoPass.ViewModels;assembly=Sasw.AforoPass"
xmlns:panCardView="clr-namespace:PanCardView;assembly=PanCardView"
xmlns:controls="clr-namespace:PanCardView.Controls;assembly=PanCardView"
mc:Ignorable="d"
x:Class="Sasw.AforoPass.Views.MainPage"
x:DataType="viewModels:MainViewModel">
<ContentPage.Content>
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackLayout.Resources>
<CarouselView BackgroundColor="White"
x:Name="Carousel"
ItemsSource="{Binding Images}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Image Source="{Binding .}"></Image>
<Label Text="image should be here"></Label>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<panCardView:CarouselView
ItemsSource="{Binding Images}"
BackgroundColor="{StaticResource ColorPrimary}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<panCardView:CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Image Source="{Binding .}"></Image>
<Label Text="image should be here"></Label>
</StackLayout>
</DataTemplate>
</panCardView:CarouselView.ItemTemplate>
<controls:LeftArrowControl />
<controls:RightArrowControl />
<controls:IndicatorsControl ToFadeDuration="1500"/>
</panCardView:CarouselView>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[0]}"></Image>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[1]}"></Image>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[2]}"></Image>
<Button Text="Close"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
and the collection bound is:
public ObservableCollection<ImageSource> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
}
UPDATE 1:
I've just edited the whole question because at first I though the problem could be related with Rg.Plugins.Popup because it was happening within a popup. But I've tried the same with a normal content page and the problem remains, so it's definitely something to do with the carousel.
UPDATE 2
I keep investigating. So far, the problem seems to be with image streams in carousels, either the Xamarin carousel or the CardView's library carousel (which have different mechanisms).
I've tried adding options and height and width request to the image that accesses the stream without luck
<!--in DataTemplate within the carousel-->
<Image Source="{Binding .}" HeightRequest="100"
WidthRequest="100"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"></Image>
<Label Text="image should be here"></Label>
UPDATE 3:
Thanks to Jack Hua for his comment and repo, I updated it to reflect the issue I have and managed to reproduce it at this specific commit here
Basically I have taken the same repo, added a QRCoder library to quickly generate images as byte[] at runtime and added the image sources from stream.
public Model() {
Images = new ObservableCollection<ImageSource>();
var imageOne = GetQrImageAsBytes();
var imageTwo = GetQrImageAsBytes();
var imageThree = GetQrImageAsBytes();
// This works
//Images.Add(ImageSource.FromFile("logo.jpg"));
//Images.Add(ImageSource.FromFile("sample.jpg"));
//Images.Add(ImageSource.FromFile("ttt.png"));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageOne)));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageTwo)));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageThree)));
}
private byte[] GetQrImageAsBytes()
{
var randomText = Guid.NewGuid().ToString();
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(randomText, QRCodeGenerator.ECCLevel.L);
var qRCode = new PngByteQRCode(qrCodeData);
var qrCodeBytes = qRCode.GetGraphic(20);
return qrCodeBytes;
}
UPDATE 4:
I found the issue, I had to try a few times to make sure this really was the problem, but it certainly is.
Re-Sharper suggests me to make a change in ItemsSource="{Binding Images}" as it does not recognize Images. So I follow Re-Sharper recommendation and let it add a x:DataType="app265:Model" in xaml's ContentPage tag. That causes the problem. I don't really know why, but I'll sleep better tonight :)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:app265="clr-namespace:App265;assembly=App265"
mc:Ignorable="d"
x:Class="App265.MainPage"
x:DataType="app265:Model"><!--this line that Re-Sharper adds causes the issue-->
Is this a Xamarin bug? or does it make sense to you? An explanation to why this x:DataType breaks things but only on the first load would be chosen as the right answer.
x:DataType="ImageSource"
Add this to your carousel view DataTemplate. It should solve your problem.
It seems there some issues with BindignContext detecting, so you should "help" to detect it yourself.
Same problem here with xamarin.forms 5.0.0.2012 but in my case my image sources are urls. It worked with x:DataType="x:String" in the carousel view DataTemplate
<CarouselView
ItemsSource="{Binding ImageSources}"
>
<CarouselView.ItemTemplate>
<DataTemplate
x:DataType="x:String">
<forms:CachedImage
Aspect="AspectFit"
ErrorPlaceholder="Placeholder"
LoadingPlaceholder="Placeholder"
DownsampleWidth="300"
DownsampleToViewSize="True"
Source="{Binding .}">

How to remove the child added to the stackLayout

I am using the scrollView and a stackLayout in it. Adding the View dynamically to the stackLayout. I am getting the following exception when I am trying to remove the View added:
The calling thread cannot access this object because a different
thread owns it.
My code is as following
<ScrollView Orientation="Vertical" VerticalOptions="FillAndExpand"
x:Name="MessagesScrollView">
<StackLayout Padding="7" x:Name="MessagesStackLayout"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
</StackLayout>
</ScrollView>
Add the code behind is:
MessagesStackLayout.Children.Clear();
foreach (var chat in messagesList)
{
MessagesStackLayout.Children.Add(new CustomViewCell(chat));
}
I have a feeling it is not running the code on MainThread try the following and see if that works for you:
Device.BeginInvokeOnMainThread(() =>
{
MessagesStackLayout.Children.Add(new CustomViewCell(chat));
});

Image flickering on Listview Search - xamarin.forms

In my xamarin.forms application, I have a Listview.The Listview contains images which are binded to photoURL.I have implemented a searchbox at the top of listview.Everything worked fine.But the problem now I facing is whenever I search for anything in list,the item appears.But the image will be gets flicker on each character type.I am getting the data from API that binding to the listview.
Please refer the link : https://gfycat.com/WaterloggedBeneficialGlobefish
My Image binding
<Grid>
<ci1:CircleImage
HeightRequest="200"
Source="empavatar.png"
Aspect="AspectFit">
</ci1:CircleImage>
<ci1:CircleImage
HeightRequest="200"
Source="{Binding PhotoURL}"
Aspect="AspectFit">
</ci1:CircleImage>
</Grid>
Iam using circular imageview and a template image when ImageURL is null.
My Search
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(e.NewTextValue))
{
EmployeeListView.ItemsSource = resultObjForEmployee;
}
else
{
EmployeeListView.ItemsSource = resultObjForEmployee.Where(x => x.Name.ToLower().StartsWith(e.NewTextValue));
}
}
resultObjForEmployee is the result that I getting from json.
Please help me to recover this problem.
Try setting the CachingStrategy of Listview as "RecycleElement"
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<ci1:CircleImage
HeightRequest="200"
Source="empavatar.png"
Aspect="AspectFit"></ci1:CircleImage>
<ci1:CircleImage
HeightRequest="200"
Source="{Binding PhotoURL}"
Aspect="AspectFit"></ci1:CircleImage>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

UWP Cannot bind ICommand to User control

I am currently working on a UWP project and I am trying to bind some command from the current page to an usercontrol.
While every other properties seems to be correctly bound, the command is not and thus is not working.
Do you have any idea from where it might come ?
Here is my code (main page):
<Grid Visibility="{Binding LoginStep, Converter={StaticResource LogConverter}, ConverterParameter='InputPin'}"
Height="450px" Width="280px">
<PIN:PinInput Pin="{Binding CurrentUser.Pin, Mode=TwoWay}" x:Uid="btnPin" SubmitCommand="{Binding AddPinCommand, Mode=TwoWay}"></PIN:PinInput>
</Grid>
View model:
private ICommand _addPinCommand;
public ICommand AddPinCommand
{
get
{
return _addPinCommand ?? (_addPinCommand = new CommandBase((o) =>
{
this.LoginStep = LoginStep.ChoseCompany;
}));
}
}
User control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Style="{StaticResource btnStyle}" Command="{Binding SubmitCommand, Mode=TwoWay}">
<StackPanel>
<Border Style="{StaticResource btnInnerStyle}" Width="100px" CornerRadius="10">
<TextBlock Text="{Binding SubmitButtonText}" Style="{StaticResource btnText}"></TextBlock>
</Border>
</StackPanel>
</Button>
</Grid>
public static readonly DependencyProperty submitCommandProperty = DependencyProperty.Register("SubmitCommand", typeof(ICommand), typeof(PinInput), null);
public ICommand SubmitCommand
{
get
{
return (ICommand)GetValue(submitCommandProperty);
}
set
{
SetValue(submitCommandProperty, value);
}
}
Note:
in the error log I have:
Error: BindingExpression path error: 'AddPinCommand' property not found on 'PAT.Mobile.UTrakk.UWP.Views.Components.PinInput'. BindingExpression: Path='AddPinCommand' DataItem='PAT.Mobile.UTrakk.UWP.Views.Components.PinInput'; target element is 'PAT.Mobile.UTrakk.UWP.Views.Components.PinInput' (Name='null'); target property is 'SubmitCommand' (type 'ICommand')
Thanks in advance,
Thomas K. T.
Ok this one was weird but here is what seems to do the trick:
<PIN:PinInput SubmitCommand="{Binding DataContext.AddPinCommand, ElementName=curPage}" Pin="{Binding CurrentUser.Pin, Mode=TwoWay}" x:Uid="btnPin" ></PIN:PinInput>
With "curPage" = name of the current page.
I do not fully understand why I have to do this, since CurrentUser.Pin is working perfectly fine without this work around.
Can someone explain ?

Tab icons in a Xamarin.Forms UWP TabbedPage?

When putting together a TabbedPage in Xamarin.Forms, how do I get UWP to use the page's Icon property?
It looks like UWP could support this just fine, if I configure my Forms attributes/files correctly.
Here's my TabbedPage XAML. The icons are all set up and working for iOS and Android, and even the on-page Image in UWP renders fine (meaning the files are likely in the project correctly).
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:tabbed"
x:Class="tabbed.MainPage">
<TabbedPage.Children>
<local:InitialPage Title="Tab1" Icon="star.png" />
<ContentPage Title="Tab2" Icon="gear.png">
<ContentPage.Content>
<StackLayout>
<Label Text="A nice label." />
<Image Source="star.png" /><!-- works here -->
</StackLayout>
</ContentPage.Content>
</ContentPage>
</TabbedPage.Children>
</TabbedPage>
I outlined how this is possible here http://depblog.weblogs.us/2017/07/12/xamarin-forms-tabbed-page-uwp-with-images/
In short, you need to change the default HeaderTemplate that is being used by UWP. But due to the way Xamarin forms is started, this is not straightforward.
So you need to inject a custom template into the resource dictionary.
Example project is up on Github here https://github.com/Depechie/XamarinFormsTabbedPageUWPWithIcons
Longer detail:
You need to supply your own TabbedPageStyle and switch out the one that Xamarin is using for their UWP rendering.
So the new style contains an Image where we data bind the Source to the Xamarin Icon property.
<Style x:Key="TabbedPageStyle2" TargetType="uwp:FormsPivot">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Icon, Converter={StaticResource IconConverter}}" Width="15" Height="15" />
<TextBlock Name="TabbedPageHeaderTextBlock" Text="{Binding Title}"
Style="{ThemeResource BodyTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The actual style switching is done in the App.Xaml.cs like this
((Style)this.Resources["TabbedPageStyle"]).Setters[0] = ((Style)this.Resources["TabbedPageStyle2"]).Setters[0];
You'll also need a converter to be sure the Image control understands the Icon source giving by Xamarin
public class IconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null && value is Xamarin.Forms.FileImageSource)
return ((Xamarin.Forms.FileImageSource)value).File;
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
As it is currently, the UWP TabbedPage renderer does not use the Icon property at all, so getting tab icons will require a custom renderer. Even the official UWP samples don't actually seem to have this baked-in, requiring a custom UserControl.
The Android TabbedPageRenderer and iOS TabbedRenderer, and even the macOS TabbedPageRenderer, use the Icon property to adjust the tab UI, but the UWP renderer would need updating to make this work.

Resources