I have this in my ViewModel:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
and this in the xaml:
<Image Source="delete.jpg" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />
</Image.GestureRecognizers>
</Image>
but when the image is clicked nothing appears in output log. What I'm missing ?
Note 1: I've followed the guide here
Note 2: I'm debugging on Android Device
Update 1: full xaml here
You have not provided any code behind, so I had to create my own (see below).
You also would save me 15 minutes if commented out ListView IsEnabled="False"
When you set Command="{Binding TapCommand} the TapCommand corresponds to the TapCommand of the list item, not to the model itself. So, you have 2 options
You can implement click in MyItem (I don't think you want that, so partial code for it commented below in MyItem class)
Define context binding on image itself.
Because of that our view model can be created few times - we don't want that, so the best solution is to define static resource of view model and use it everywhere on page.
The solution is below (you will just need to modify some namespaces and change delte.png to your delete.jpg):
page xml
<?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:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
x:Class="ButtonRendererDemo.ImageTapComplexPage"
BindingContext="{StaticResource viewModel}">
<ContentPage.Resources>
<ResourceDictionary>
<local:TapComplexViewModel x:Key="viewModel"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Label Text="text2" VerticalOptions="Center" HorizontalOptions="Center" />
<StackLayout Padding="0,20,0,20">
<Button Text="text" Clicked="onclick" BackgroundColor="#009688"></Button>
</StackLayout>
<Label Text="List Type" VerticalOptions="Center" HorizontalOptions="Center" />
<ListView x:Name="lstItems" RowHeight="60" ItemsSource="{Binding Items}" > <!--IsEnabled="False"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Fill" Padding="0,2,0,2">
<StackLayout Padding="5,5,5,5">
<Image Source="{Binding source}" HorizontalOptions="End" HeightRequest="40" WidthRequest="40" />
</StackLayout>
<StackLayout Orientation="Vertical" Spacing="1">
<Label Text = "{Binding name}" HeightRequest="20" FontAttributes="Bold"/>
<Label Text = "{Binding data, StringFormat='{0:F0}'}" />
</StackLayout>
<StackLayout HorizontalOptions="EndAndExpand" Padding="0,0,15,0" Orientation="Horizontal">
<Image Source="delete.png" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<!--<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />-->
<TapGestureRecognizer Command="{Binding Source={StaticResource viewModel}, Path=TapCommand}" CommandParameter="{Binding name}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Code behind
namespace ButtonRendererDemo
{
public partial class ImageTapComplexPage : ContentPage
{
public ImageTapComplexPage()
{
InitializeComponent();
}
void onclick(object sender, EventArgs args)
{
}
}
public class TapComplexViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public ObservableCollection<MyItem> Items { get; private set; }
public TapComplexViewModel()
{
Items = new ObservableCollection<MyItem>()
{
new MyItem { name="First", source="Icon.png", data=0.5f },
new MyItem { name="Second", source="Icon.png", data=0.6f },
new MyItem { name="Third", source="Icon.png", data=0.7f }
};
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
public class MyItem
{
public string name { get; set; }
public string source { get; set; }
public float data { get; set; }
//public ICommand TapCommand
//{
// get { return new Command(() => { }); }
//}
}
}
In the back end of the xaml file make sure u set the BindingContext to the view model: Something like this:
xaml.cs
public partial class TapInsideFrameXaml : ContentPage
{
public TapInsideFrameXaml ()
{
InitializeComponent ();
// The TapViewModel contains the TapCommand which is wired up in Xaml
BindingContext = new TapViewModel ();
}
}
Also check:
On Menu > tools > options > debugging > General:
Ensure "Redirect all output window text to the immediate window" is NOT checked
On Project Properties > Build:
Configuration: Debug
"Define DEBUG constant" is checked
"Define TRACE constant" is checked
On the Output window:
Show output from: Debug
Right-click in the output window and ensure "Program output" is checked
Also make sure you are debugging using F5 and not Ctrl f5
You declared the PropertyChangedEvent event but you never actually use it. Try this:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
OnPropertyChanged();
}
protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler (this, new PropertyChangedEventArgs (propertyName));
}
}
Related
Is there a way to show / hide gui when I click on another GUI element? I am doing MAUI GUI and using CollectionView I generate list of buttons. When I click on one I want to show option 1 and 2 for that item, if I click on another I want to show it´s options and hide the old ones.
I use MVVM, that is why I ask for GUI option only.
Code for list:
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MyItem">
<StackLayout>
<Frame Margin="10,5">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="{Binding Name}"/>
<Label Grid.Column="1" Text="{Binding Number}" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Text="{Binding Description}"/>
</Grid>
</Frame>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="2" Text="Option 1" Margin="5"/>
<Button Grid.Row="2" Grid.Column="1" Text="Option 2" Margin="5"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
As i understand, your case needs to implement behavior which could be expressed by phrase "only one of many".
Jason's solution (bool property bound to IsVisible of your control) will work, however it will get messy along with your button count rising - you would need to handle every single button and it's bound property - why would you want to do that?
In addition i'm sure you will eventually need this again in another View, so you would have to repeat that - it's not really DRY, isn't it?
I would suggest to use more reusable approach.
1. Create a converter class:
[ValueConversion(typeof(object), typeof(bool))]
public class IsEqualConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return targetType == typeof(bool)
? Equals(value, parameter)
: throw new InvalidOperationException("Converter can only convert to value of type bool.");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("Invalid call - one way only");
}
}
2.Reference your converter in desired View :
<ContentPage.Resources>
<converters:IsEqualConverter x:Key="IsEqualConverter" />
</ContentPage.Resources>
3. Set the SelectionMode of your CollectionView to Single.
<CollectionView SelectionMode="Single">
4. Bind IsVisible of your Grid, to SelectedItem of your CollectionView using syntax below:
<Grid IsVisible="{Binding Name, Converter={StaticResource IsEqualConverter}, ConverterParameter={Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type CollectionView}}, Path=SelectedItem.Name}}">
Assuming Name is unique of course.
[Optional]
5. Create and implement Interface providing unique identification.
public interface IIdentifyUniquely
{
public Guid UniqueIdentifier { get; set; }
}
public class MyItem : IIdentifyUniquely
{
public string Name { get; set; }
public int Number { get; set; }
public Guid UniqueIdentifier { get; }
public MyItem()
{
UniqueIdentifier = Guid.NewGuid();
}
}
Then bind to implemented UniqueIdentifier instead.
I will point out here few options you have, using "MVVM and UI only"
You can use triggers.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/triggers?view=net-maui-7.0
With this you can make your UI dependent on properties of your Model.
You can use Styling and VisualStates.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/visual-states?view=net-maui-7.0
With this you can change the appearance of your UI Elements, depending on their visual state. For example, if you decide make use of "Selected" property of your item.
You can use alternatives for showing additional UI elements for each item. For example - SwipeView.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/swipeview?view=net-maui-7.0
You can try writing your own, reusable ControlTemplate.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/controltemplate?view=net-maui-7.0
You can get 3rd party controls. Here is one of the CommunityToolkit, that does something very similar:
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/views/expander
There are really so many ways to handle your problem. You can also do what Jason said, and just bind to IsVisible and deal with it manually.
You can add a TapGestureRecognizer attached to Frame:
<ScrollView>
<CollectionView>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MyItem">
<StackLayout>
<Frame Margin="10,5">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</Frame.GestureRecognizers>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="{Binding Name}"/>
<Label Grid.Column="1" Text="{Binding Number}" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Text="{Binding Description}"/>
</Grid>
</Frame>
<Grid IsVisible="false">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="2" Text="Option 1" Margin="5"/>
<Button Grid.Row="2" Grid.Column="1" Text="Option 2" Margin="5"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
And Page.xaml.cs:
public partial class NewPage : ContentPage
{
Grid grid = new Grid();
public NewPage()
{
InitializeComponent();
...
}
private void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
{
grid.IsVisible = false;
Frame frame = (Frame)sender;
StackLayout stacklayout = (StackLayout)frame.Parent;
grid = (Grid)stacklayout.Children[stacklayout.Children.Count - 1];
grid.IsVisible = true;
}
}
I have a lot of tried to show EmptyView after Loader but did not get success might i missed some code configuration for the EmptyView and Loader.
I'm using ActivityIndicator loader for the loading view if it will hide once data will come.
Below code is for EmptyView for the CarouselView
<CarouselView.EmptyView>
<Frame BackgroundColor="Transparent" Margin="20,150">
<Frame BackgroundColor="White" CornerRadius="20" HeightRequest="150" WidthRequest="300" HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="Data not Found" FontSize="Medium" HorizontalOptions="Center" VerticalOptions="Center"/>
</Frame>
</Frame>
</CarouselView.EmptyView>
Below code is for ActivityIndicator
<StackLayout IsVisible="{Binding IsBusy}" Padding="12" AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator IsRunning="{Binding IsBusy}" Color="#BFA464" x:Name="PaymentLoader"/>
<Label Text="Data is Loading..." HorizontalOptions="Center" TextColor="#BFA464"/>
</StackLayout>
Below image is my whole code screenshot
here is full code image
If anybody have idea, please share your great and valuable knowledge.
According to your description and code, I guess that you want to display CarouselView's EmptyView and ActivityIndicator when no data found, am I right?
From Xamarin.Forms CarouselView EmptyView, we can see that EmptyView, of type object, the string, binding, or view that will be displayed when the ItemsSource property is null, or when the collection specified by the ItemsSource property is null or empty. The default value is null.
I do one sample that you can take a look:
<StackLayout>
<!-- Place new controls here -->
<CarouselView ItemsSource="{Binding models}">
<CarouselView.EmptyView>
<Frame Margin="20,150" BackgroundColor="Transparent">
<Frame
BackgroundColor="White"
CornerRadius="20"
HeightRequest="150"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="300">
<Label
FontSize="Medium"
HorizontalOptions="Center"
Text="Data not Found"
VerticalOptions="Center" />
</Frame>
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame
Margin="8"
BorderColor="Gray"
CornerRadius="20"
HasShadow="True"
HeightRequest="250"
VerticalOptions="CenterAndExpand"
WidthRequest="300">
<StackLayout>
<Image Source="{Binding ImagePath}" />
<Label
FontAttributes="Bold"
FontSize="24"
HorizontalTextAlignment="Center"
Text="{Binding Title}" />
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<StackLayout
Padding="12"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1"
AbsoluteLayout.LayoutFlags="PositionProportional"
IsVisible="{Binding IsBusy}">
<ActivityIndicator
x:Name="PaymentLoader"
IsRunning="{Binding IsBusy}"
Color="#BFA464" />
<Label
HorizontalOptions="Center"
Text="Data is Loading..."
TextColor="#BFA464" />
</StackLayout>
</StackLayout>
public partial class MainPage : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<BirdsModel> models { get; set; }
private Boolean _IsBusy;
public Boolean IsBusy
{
get { return _IsBusy; }
set
{
_IsBusy = value;
RaisePropertyChanged("IsBusy");
}
}
public MainPage()
{
InitializeComponent();
IsBusy = true;
models = new ObservableCollection<BirdsModel>();
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Task.Delay(2000);
models.Add(new BirdsModel() { Title = "title 1", ImagePath = "a5.jpg" });
models.Add(new BirdsModel() { Title = "title 2", ImagePath = "a6.jpg" });
models.Add(new BirdsModel() { Title = "title 3", ImagePath = "a7.jpg" });
models.Add(new BirdsModel() { Title = "title 4", ImagePath = "a8.jpg" });
IsBusy = false;
}
}
public class BirdsModel
{
public string Title { get; set; }
public ImageSource ImagePath { get; set; }
}
This is the sample at github, you can take a look:
https://github.com/CherryBu/CarouselViewSample
Carousel page in Xamarin Forms Working Fine in iOS but in Android working only on "next" Images, But Scrolling Back to Previous Images (which shown before) images gives error "Source not found/ Available" error.
All images loaded from Images Bytes.
Here is my Code. (Source from URL is not working in Some Countries like KSA..)
XAML
<?xml version="1.0" encoding="UTF-8"?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" Title=""
x:Class="Prj.XAML.ImageCarouselPg" x:Name="CRV" ItemsSource="{Binding ImageList}" BackgroundColor="{StaticResource primaryLight}" >
<CarouselPage.ItemTemplate >
<DataTemplate >
<ContentPage >
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS, Android" Value="0,20,0,0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="10,5,10,5" BackgroundColor="{StaticResource primaryLight}">
<!--<Label x:Name="TxtCode" Text="{Binding Code}" HorizontalOptions="Center" HeightRequest="30" TextColor="Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontSize="Subtitle"/>-->
<Label x:Name="TxtCode" Text="{Binding Code}" HorizontalOptions="Center" HeightRequest="30" TextColor="Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontSize="Subtitle" FontAttributes="Bold"/>
<Label x:Name="Txtdesc" Text="{Binding Description}" HorizontalOptions="Center" HeightRequest="30" TextColor="Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontSize="Subtitle"/>
<Image x:Name="img" Source="{Binding BrandImage}" Aspect="AspectFit" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ></Image>
</StackLayout>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
</CarouselPage>`
Loading Code Back end code
private async void PopulateImageListNew(DataSet dtImgList)
{
string Errstr = ""; string TblName;
try
{
if (dtImgList != null)
{
if (dtImgList.Tables[0].Rows.Count > 0)
{
for (int i = 0; i < dtImgList.Tables[0].Rows.Count; i++)
{
string Code = dtImgList.Tables[0].Rows[i]["Code"].ToString();
string ImageName = dtImgList.Tables[0].Rows[i]["Description"].ToString();
string ImageURL = dtImgList.Tables[0].Rows[i]["ImageUrl"].ToString();
byte[] byteArray = (byte[])(dtImgList.Tables[0].Rows[i]["Image1x"]);
Stream strm = new MemoryStream(byteArray);
Brandimg.Source = ImageSource.FromStream(() => { return strm; });
ImageList.Add(new Models.OurBrands { Code = Code, Description = ImageName,
ImageUrl = ImageURL, BrandImage = Brandimg.Source });
}
}
}
CRV.ItemsSource = ImageList;
//BindingContext = this;
if (CurIdx >= 0)
{
CRV.SelectedItem = ImageList[CurIdx];
}
}
catch (WebException WebEx)
{
WebErrStr = WebEx.Message.ToString();
}
catch (Exception Ex)
{
WebErrStr = Ex.Message.ToString();
}
finally
{
WebErrStr = Errstr;
}
}
Class
public class Brands
{
public string Code { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public ImageSource BrandImage { get; set; }
public string VideoUrl { get; set; }
}
I've created a rendered picker's to center center in the middle of the control but at the moment of entering the page, it shows the following error: System.ArgumentException.
I attach the code to see if you can help me.
In project new class...
using Xamarin.Forms;
namespace MaterialFUR
{
public class PickerCentrat : Picker
{
}
}
In Android ...
using MaterialFUR;
using MaterialFUR.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PickerCentrat), typeof(RendererPicker))]
namespace MaterialFUR.Droid
{
public class RendererPicker : EntryRenderer
{
public RendererPicker(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Gravity = GravityFlags.CenterHorizontal;
}
}
}
}
In XAML page...
<?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="MaterialFUR.AgafarMaterialPage"
xmlns:local="clr-namespace:MaterialFUR"
Title="Agafar Material">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" Spacing="25">
<Button x:Name="btnScan" Clicked="btnScan_Clicked" BackgroundColor="Gray" TextColor="White" Text="Llegir codi de barres" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
<Entry x:Name="entCodi" TextColor="Black" FontSize="30" FontAttributes="Bold" Keyboard="Default" Placeholder="Codi" HorizontalTextAlignment="Center"/>
<Entry x:Name="entQtat" TextColor="Black" FontSize="30" FontAttributes="Bold" Keyboard="Numeric" Placeholder="Quantitat" HorizontalTextAlignment="Center"/>
<local:PickerCentrat x:Name="pckUbicacio" Title="Ubicació" TextColor="Black" FontSize="30" FontAttributes="Bold">
<Picker.Items>
<x:String>SATMOBIL-1</x:String>
<x:String>SATMOBIL-2</x:String>
<x:String>SATMOBIL-3</x:String>
<x:String>SATMOBIL-4</x:String>
<x:String>SATMOBIL-5</x:String>
<x:String>SATMOBIL-6</x:String>
<x:String>SATMOBIL-7</x:String>
<x:String>SATMOBIL-8</x:String>
<x:String>SATMOBIL-9</x:String>
<x:String>TALLER</x:String>
</Picker.Items>
</local:PickerCentrat>
<StackLayout Orientation="Horizontal" Margin="0,100">
<Button x:Name="btnConfirmar" Clicked="BtnConfirmar_Clicked" BackgroundColor="SteelBlue" TextColor="White" Text="Confirmar" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
<Button x:Name="btnCancelar" Clicked="BtnCancelar_Clicked" BackgroundColor="Gray" TextColor="White" Text="Cancelar" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
Error displayed.
I appreciate your help, thanks!
Your class needs to Inherit from PickerRenderer
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PickerCentrat), typeof(RendererPicker))]
namespace MaterialFUR.Droid
{
public class RendererPicker : PickerRenderer
{
public RendererPicker(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Gravity = GravityFlags.CenterHorizontal;
}
}
}
}
When in doubt about which class you need to Inherit from for your CustomRenderer, you can always check on the Xamarin.Forms source code here (for Android).
Hope this helps.
I am working on a Xamarin.Forms (Portable) using Visual Studio 2015.
The application allows me to retrieve information from a Database and display it.
I am able to display all the created records in a ListView on UWP. However, on Android they are not all showing up.
From my Menu Page, you can clearly see the big difference in terms of UI size. I am curious to know why this is happening on the Android platform.
Below are some of the relevant code. If you need to see more, please let me know.
MenuPage.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="XamarinFormsDemo.Views.MenuPage"
BackgroundImage="bg3.jpg">
<StackLayout>
<StackLayout Orientation="Vertical"
Padding="30"
HeightRequest="30"
BackgroundColor="#24e97d">
<Image Source="ebmspersonnellogo1.png"
HeightRequest="40"/>
</StackLayout>
<StackLayout Orientation="Vertical"
VerticalOptions="Center"
Padding="45,60,45,60">
<Image x:Name="sales"
Source="salesicon.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="SalesTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="personnel"
Source="personnelicon.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="PersonnelTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="crm"
Source="crmicon.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="CRMTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="asset"
Source="asseticon.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="AssetTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="receivables"
Source="receivables.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="ReceivablesTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="prapprovals"
Source="prapprovals.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="ApprovalsTapGestureRecognizer_OnTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2015 smesoft.com.ph All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
MenuPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XamarinFormsDemo.Views
{
public partial class MenuPage : ContentPage
{
public MenuPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
private async void NavigateButton_OnClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new EmployeeRecordsPage());
}
private async void SalesTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await sales.ScaleTo(0.95, 80, Easing.CubicOut);
await sales.ScaleTo(1, 80, Easing.CubicIn);
await Navigation.PushModalAsync(new SalesAndExpensePage());
}
private async void PersonnelTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await personnel.ScaleTo(0.90, 75, Easing.CubicOut);
await personnel.ScaleTo(1, 75, Easing.CubicIn);
await Navigation.PushModalAsync(new PersonnelPage());
}
private async void CRMTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await crm.ScaleTo(0.90, 75, Easing.CubicOut);
await crm.ScaleTo(1, 75, Easing.CubicIn);
await Navigation.PushModalAsync(new CRMPage());
}
private async void AssetTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await asset.ScaleTo(0.90, 75, Easing.CubicOut);
await asset.ScaleTo(1, 75, Easing.CubicIn);
await Navigation.PushModalAsync(new AssetManagementPage());
}
private async void ReceivablesTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await receivables.ScaleTo(0.90, 75, Easing.CubicOut);
await receivables.ScaleTo(1, 75, Easing.CubicIn);
await Navigation.PushModalAsync(new ReceivablesAndPayablesPage());
}
private async void ApprovalsTapGestureRecognizer_OnTapped(Object sender, EventArgs e)
{
await prapprovals.ScaleTo(0.90, 75, Easing.CubicOut);
await prapprovals.ScaleTo(1, 75, Easing.CubicIn);
await Navigation.PushModalAsync(new ApprovalsPage());
}
}
}
Here's a screen shot of UWP and Android. First image is UWP, second image is Android.
First of all. Your view is awfully nested, which can lead to performance problems.
Second of all. You are saying you are using a ListView. However, I don't see it anywhere in your XAML. There you are just using a StackLayout and stacking views below each other.
This means that any item that cannot fit within the screen will not be displayed at all. Consider rewriting your UI and use a ListView instead. Your items are very similar to each other and only title, image and click handler differ from each other. Using a ListView could also eliminate repeating yourself as you do currently with all those tap handlers, and you could limit yourself to only have one.
You could have something like this:
public class MenuItemViewModel
{
public string Name { get; set; }
public string ImageUrl { get; set; }
}
Then in your ViewModel for the page:
public List<MenuItemViewModel> MenuItems { get; } = new List<MenuItemViewModel>();
MenuItems.Add(new MenuItemViewModel {
Name = "Sales and Expense",
ImageUrl = "salesicon.png"
});
MenuItems.Add(new MenuItemViewModel {
Name = "Personnel",
ImageUrl = "personnelicon.png"
});
...
Then in your XAML something like this:
<ListView x:Name="MenuList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="15,0">
<Label Text="{Binding Name}" />
<Image Source="{Binding ImageUrl}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Then set ItemsSource of the ListView to your MenuItems from your ViewModel.
You can get the selected item by subscribing to ItemSelected on the ListView:
MenuList.ItemSelected += ItemSelected;
private async void ItemSelected(object sender, ItemTappedEventArgs e)
{
var menuItem = e.Item as MenuItemViewModel;
if (menuItem == null) return;
Page nextPage = null;
switch(menuItem.Name)
{
case "Sales and Expense":
nextPage = new SalesAndExpensePage();
break;
case "Personnel":
nextPage = new PersonnelPage();
break;
}
await Navigation.PushModalAsync(nextPage);
}