MAUI MVVM Architecture, doing a login system while consuming a API - xamarin

I am trying to learn MAUI in order to create a project, but it seems like i got stuck. I cant understand the MVVM architecture, as i never had any simillar experience before. I will now present my code, and would love to get answers that could explain why it is not working, and a possible solution for the problem.
I have three folders: Views, where i store the design. Models, where i store the classes. And ViewModels, that get the data. This is the xaml content page, which consists of a login page.
<?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"
x:Class="thebridgeproject.Views.login"
xmlns:ViewModels="clr-namespace:thebridgeproject.ViewModels"
Shell.NavBarIsVisible="False"
Title="LoginPage" >
<ContentPage.BindingContext>
<ViewModels:LoginViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image Source="loginicon.png" HeightRequest="150" WidthRequest="150" />
<VerticalStackLayout Spacing="5">
<Label Text="Welcome!" FontSize="28" TextColor="#3B7A5E" HorizontalTextAlignment="Center" />
<Label Text="Login to your account" FontSize="18" TextColor="Gray" HorizontalTextAlignment="Center" />
</VerticalStackLayout>
<StackLayout Orientation="Horizontal">
<Frame ZIndex="1" HasShadow="True" BorderColor="White" HeightRequest="56" WidthRequest="56" CornerRadius="28">
<Image Source="user.png" HeightRequest="20" WidthRequest="20" />
</Frame>
<Frame HeightRequest="45" Margin="-20,0,0,0" Padding="0" HasShadow="True" BorderColor="White" HorizontalOptions="FillAndExpand">
<Entry Text="{Binding Username}" Margin="20,0,0,0" VerticalOptions="Center" Placeholder="Username"/>
</Frame>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Frame ZIndex="1" HasShadow="True" BorderColor="White" HeightRequest="56" WidthRequest="56" CornerRadius="28">
<Image Source="lock.png" HeightRequest="20" WidthRequest="20" />
</Frame>
<Frame HeightRequest="45" Margin="-20,0,0,0" Padding="0" HasShadow="True" BorderColor="White" HorizontalOptions="FillAndExpand">
<Entry Text="{Binding Password}" Margin="20,0,0,0" VerticalOptions="Center" Placeholder="Password" IsPassword="True" />
</Frame>
</StackLayout>
<Button Text="Sign in" WidthRequest="100" CornerRadius="20" HorizontalOptions="Center" BackgroundColor="#3B7A5E" Command="{Binding LoginCommand}" />
<StackLayout Orientation="Horizontal" Spacing="5" HorizontalOptions="Center">
<Label Text="Dont have an account?" TextColor="Gray" />
<Label Text="Sign up here" TextColor="#50b3f2" />
</StackLayout>
</VerticalStackLayout>
</ContentPage>
Then i have the Model that holds the data for the API request.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace thebridgeproject.Models
{
class users
{
public class Result
{
public int NumUtente { get; set; }
public string Nome { get; set; }
public string Password { get; set; }
public string Morada { get; set; }
public string Cidade { get; set; }
public string DataNascimento { get; set; }
public string NumTlf { get; set; }
}
public class Root
{
public bool success { get; set; }
public string message { get; set; }
public List<Result> result { get; set; }
}
}
}
After that, we have the LoginViewModel:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using thebridgeproject.Models;
namespace thebridgeproject.ViewModels
{
public class LoginViewModel : INotifyPropertyChanged
{
private string _username;
public string Username
{
get { return _username; }
set
{
_username = value;
OnPropertyChanged(nameof(Username));
}
}
private string _password;
public string Password
{
get { return _password; }
set
{
_password = value;
OnPropertyChanged(nameof(Password));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private async Task Login()
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("Authorization", "RaV9N");
var response = await httpClient.GetAsync("http:///bp/utentes");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var users = JsonConvert.DeserializeObject<users.Root>(content);
if (users.success)
{
var user = users.result.FirstOrDefault(x => x.Nome == Username);
if (user != null && VerifyPassword(user.Password, Password))
{
// Login successful
// ...
}
else
{
// Login failed
// ...
}
}
else
{
// API request failed
// ...
}
}
else
{
// API request failed
// ...
}
}
}
private bool VerifyPassword(string hashedPassword, string enteredPassword)
{
// Use the BCrypt.Net library to verify the entered password
return BCrypt.Net.BCrypt.Verify(enteredPassword, hashedPassword);
}
}
}
Ignore the API link! But that is mostly it, i have no more code. It seems like it does nothing. I think the issue might be the lack of code in the file behind the design. Im open to suggestions, and i am thankfull for any productive answer!

The view model implements properties and commands to which the view can data bind to, and notifies the view of any state changes through change notification events. The view model is also responsible for coordinating the view's interactions with any model classes that are required. There's typically a one-to-many relationship between the view model and the model classes. You can refer to The Model-View-ViewModel Pattern for more details.
You can refer to my sample code below on how to use LoginCommand in your LoginCommand. Notice that I put it in a Label TapGestureRecognizer, however its usage is the same as that you use it in a Button.
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" xmlns:ViewModel="clr-namespace:MyApp.ViewModels"
xmlns:local="clr-namespace:MyApp"
x:Class="MyApp.Views.LoginPage"
BackgroundColor="#112B47"
>
<ContentPage.BindingContext>
<ViewModel:LoginViewModel/>
</ContentPage.BindingContext>
<StackLayout Padding="15" VerticalOptions="Center" HorizontalOptions="FillAndExpand">
<Label HorizontalOptions="Center">
<Label.FormattedText>
<FormattedString>
<Span Text="Don't have an account?" TextColor="Gray"></Span>
<Span Text="Register" TextColor="Gray" FontAttributes="Bold" TextDecorations="Underline"></Span>
</FormattedString>
</Label.FormattedText>
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LoginCommand}"></TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ContentPage>
LoginViewModel:
public class LoginViewModel : INotifyPropertyChanged
{
public ICommand LoginCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public LoginViewModel()
{
LoginCommand = new Command(async () => await Login());
}
public async Task Login()
{
// add your logic here
}
}

Related

Data Template Selector not called

I have two types of users (user/stylist and stylist inherits from user) and in profile page I need to build it different so I'm using data template selector but the view is not calling the selector
Here is the view
<ContentPage.Resources>
<DataTemplate x:Key="UserTemplate">
<StackLayout>
<Label Text="I'm a user"/>
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="StylistTemplate">
<StackLayout BackgroundColor="DarkBlue">
<Label Text="I'm a stylist"/>
</StackLayout>
</DataTemplate>
<local:UserTypeSelector
x:Key="personDataTemplateSelector"
StylistTemplate="{StaticResource StylistTemplate}"
UserTemplate="{StaticResource UserTemplate}" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout
Margin="10"
BindableLayout.ItemTemplateSelector="{StaticResource personDataTemplateSelector}"
BindableLayout.ItemsSource="{Binding User}" />
</ContentPage.Content>
The selector class
IMPORTANT
I debugged it and the OnSelectTemplate it's not called at all
public class UserTypeSelector : DataTemplateSelector
{
public DataTemplate UserTemplate { get; set; }
public DataTemplate StylistTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
//return ((User)item).IsStylist ? StylistTemplate : UserTemplate
return StylistTemplate; this is for testing the selector
}
}
The code behind
public partial class ProfilePage : ContentPage
{
public ProfilePage()
{
InitializeComponent();
BindingContext = new ProfileViewModel();
}
}
And the view model
private User user { get; set; }
public User User { get; set; }
public ProfileViewModel()
{
//User = new User()
//{
// IsStylist = true, I tried to use this to test the selector but it throws
//}; an error **Object must implement IConvertible**
}
BindableLayout.ItemsSource should specifie the collection of IEnumerable items to be displayed by the layout.
So i modify your viewmodel,then it will work.
ProfileViewModel:
class ProfileViewModel
{
public ObservableCollection<User> Users { get; set; }
public ProfileViewModel()
{
Users = new ObservableCollection<User>()
{
new User(){ IsStylist = false},new User(){ IsStylist = false}
};
}
public class User
{
public bool IsStylist { get; set; }
}
}
the xaml :
<ContentPage.Resources>
<DataTemplate x:Key="UserTemplate">
<StackLayout>
<Label Text="I'm a user"/>
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="StylistTemplate">
<StackLayout BackgroundColor="DarkBlue">
<Label Text="I'm a stylist"/>
</StackLayout>
</DataTemplate>
<local:UserTypeSelector
x:Key="personDataTemplateSelector"
StylistTemplate="{StaticResource StylistTemplate}"
UserTemplate="{StaticResource UserTemplate}" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout
Margin="10"
BindableLayout.ItemTemplateSelector="{StaticResource personDataTemplateSelector}"
BindableLayout.ItemsSource="{Binding Users}" />
</ContentPage.Content>

How to open a page from a class?

I am trying to create a little application using MVVM. I would like to open a second page that scan a QR code and then back the value of the QR code to the main page.
I have this code:
Main view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="QRScannerXamarinForms.Views.MainPageView"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlns:vm="clr-namespace:QRScannerXamarinForms.ViewModels">
<ContentPage.BindingContext>
<vm:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Barcode Sample" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
</Frame>
<Button x:Name="ucBtnEscanear" Text="Escanear"
Command="{Binding EscanearCommand}"/>
</StackLayout>
</ContentPage>
Main view model:
private INavigationService _navigationService;
public ICommand EscanearCommand { get; private set; }
private void Escanear()
{
string miCodigoQr = _navigationService.AbrirPaginaEscaner();
}
Second view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="QRScannerXamarinForms.Views.ScannerView"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlns:vm="clr-namespace:QRScannerXamarinForms.ViewModels">
<ContentPage.BindingContext>
<vm:ScannerViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Barcode Sample" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
</Frame>
<Label x:Name="ucScanResultText" />
<zxing:ZXingScannerView x:Name="ucZXingScannerView" IsScanning="True" IsAnalyzing="True" Result="{Binding CodigoQr}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Second view model:
class ScannerViewModel : BaseViewModel
{
private string _codigoQr;
public string CodigoQr
{
get { return _codigoQr; }
set { _codigoQr = value; }
}
}
Interface to open pages:
namespace QRScannerXamarinForms.Services
{
interface INavigationService
{
string AbrirPaginaEscaner();
}
}
Implementation of the interface that allows navigation.
namespace QRScannerXamarinForms.Services
{
class ViewNavigationService : INavigationService
{
public string AbrirPaginaEscaner()
{
ScannerView miScannerView = new ScannerView();
ScannerViewModel miScannerViewModel = miScannerView.BindingContext as ScannerViewModel;
Application.Current.MainPage.Navigation.PushModalAsync(miScannerView);
return miScannerViewModel.CodigoQr;
}
}
}
But in the implementation of the interface, the second page is not shown. So this line doesn't work:
Application.Current.MainPage.Navigation.PushModalAsync(miScannerView);
How could I open from this class?
Thanks.

Xamarin: From list of urls to populating a CollectionView with Images

I cannot figure out how this is done. I have a page with a Collection view and Image inside of each cell.
I do also have a list of URLs, each one pointing to a jpg. What I would like to do is display each jpg in one of those cells.
<StackLayout Margin="20">
<CollectionView ItemsSource="{Binding UrlCollection}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageUrl}"
Aspect="AspectFill" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
So far It looks to me that i have to make a class, called UrlCollection, and one item inside that class ist the ImageUrl. But i feel lost and cannot find a example to follow.
UPDATE My current version. its not working, the display is simply blank.
Gallery.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"
xmlns:vm="clr-namespace:GalShare.ViewModel"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="GalShare.Views.Gallery">
<ContentPage.BindingContext>
<vm:GalleryViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<CollectionView ItemsSource="{Binding Gallery}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageUrl}"
Aspect="AspectFit" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Gallery.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace GalShare.Model
{
class Gallery
{
public string ImageName { get; set; }
public string ImageUrl { get; set; }
}
}
GalleryService.cs
using GalShare.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace GalShare.Service
{
class GalleryService
{
public ObservableCollection<Gallery> GetImageList()
{
return new ObservableCollection<Gallery>()
{
new Gallery() { ImageName="Image1", ImageUrl="https://www.igormasin.it/fileuploads/tanja_23a6id/IMG_0992-Edit_a.jpg"},
new Gallery() { ImageName="Image2", ImageUrl="https://www.igormasin.it/fileuploads/tanja_23a6id/IMG_1024-Edit_a.jpg"},
new Gallery() { ImageName="Image3", ImageUrl="https://www.igormasin.it/fileuploads/tanja_23a6id/IMG_1074-Edit_a.jpg"}
};
}
}
}
GalleryViewModel.cs
using GalShare.Model;
using GalShare.Service;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace GalShare.ViewModel
{
class GalleryViewModel
{
public ObservableCollection<Gallery> Images { get; set; }
public GalleryViewModel()
{
Images = new GalleryService().GetImageList();
}
}
}
Main page call:
MainPage = new NavigationPage(new Gallery());
First create a Model and ViewModel and populate a ViewModel property with a List of Model object.
public class ViewModel
{
public ObservableCollection<ImageData> UriCollection { get; set; }
public ViewModel()
{
UriCollection = new ObservableCollection<ImageData>();
for(var i=0; i<10; i++)
{
UriCollection.Add(new ImageData()
{
ImgeUri = "https://i.stack.imgur.com/di65V.jpg?s=328&g=1"
};
}
}
}
public class ImageData
{
public string ImgeUri { get; set; }
}
Set a ViewModel containing the UriCollection as BindingContext to the Page
MainPage.Xaml.cs
public MainPage()
{
InitializeComponent();
this.BindingContext = new ViewModel();
}
Usage in Xaml
<CollectionView ItemsSource="{Binding UriCollection}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Image Aspect="AspectFit" Source="{Binding ImgeUri}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

How to use Xamarin.Forms MediaManager library with MVVM to play youtube videos

I'm trying to get working the MediaManager library playing a couple videos from youtube using MVVM approach.
My idea is to have a single view where initially is loaded a video, once the user watched the first loaded video he can click a button to see another video in the same view.
I can't find many examples on the internet related about how to accomplish this using MVVM, every example I've found uses the codebehind approach but my app is created in full MVVM so I need to get it working like that.
This is what I've done by now.
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"
xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:mediamanager="clr-namespace:Plugin.MediaManager.Forms;assembly=Plugin.MediaManager.Forms"
x:Class="VideoView"
BindingContext="{Binding VideoViewModel, Source={StaticResource ServiceLocator}}">
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
<StackLayout Margin="10,60,10,0">
<Label x:Name="LblMsg"/>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<mediamanager:VideoView x:Name="TrainingVideoPlayer"
AspectMode="AspectFill"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
<StackLayout VerticalOptions="End" HorizontalOptions="FillAndExpand">
<ProgressBar x:Name="progress" HeightRequest="10" Progress="{Binding ProgressStatus, Mode=TwoWay}" />
<StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Spacing="10" VerticalOptions="End">
<Image x:Name="ImgPlay"
Source="video_play.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PlayTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgPause"
Source="video_pause.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PauseTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgStop"
Source="video_stop.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StopTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</Grid>
<Button x:Name="BtnNext" Command="{Binding NextCommand}"/>
</StackLayout>
</ContentPage>
ViewModel
public class VideoViewModel : AppBaseViewModel
{
//Commands
public ICommand PageAppearingCommand { get; set; }
public ICommand PlayTappedCommand { get; set; }
public ICommand PauseTappedCommand { get; set; }
public ICommand StopTappedCommand { get; set; }
public ICommand NextCommand { get; set; }
//Fields
private double _progressStatus;
private string youtubevideourl;
//Properties
public double ProgressStatus
{
get => _progressStatus;
set
{
if (Set(ref _progressStatus, value))
{
RaisePropertyChanged(() => ProgressStatus);
}
}
}
public VideoViewModel()
{
PageAppearingCommand = new Command(OnPageAppearing);
PlayTappedCommand = new Command(OnImgPlay_Tapped);
PauseTappedCommand = new Command(OnImgPause_Tapped);
StopTappedCommand = new Command(OnImgStop_Tapped);
NextCommand = new Command(OnBtnNext_Click);
youtubevideourl = "https://urloftheyoutubevideo";
}
private async void OnPageAppearing()
{
await CrossMediaManager.Current.Stop();
//Sets the videoplayer events to control playback status
CrossMediaManager.Current.PlayingChanged += VideoPlayer_PlayingChanged;
CrossMediaManager.Current.MediaFinished += VideoPlayer_MediaFinished;
}
private async void OnImgPlay_Tapped()
{
await CrossMediaManager.Current.Play(youtubevideourl, MediaFileType.Video);
}
private async void OnImgPause_Tapped()
{
await CrossMediaManager.Current.Pause();
}
private async void OnImgStop_Tapped()
{
await CrossMediaManager.Current.Stop();
}
private void VideoPlayer_PlayingChanged(object sender, PlayingChangedEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
ProgressStatus = e.Progress;
});
}
private void VideoPlayer_MediaFinished(object sender, MediaFinishedEventArgs e)
{
//Some logic
}
private async void OnBtnNext_Click()
{
//logic to load the next video url
}
}
Using this code I can execute play/pause/stop methods of the MediaManager but nothing happens in the View, I cant even see 1 second of the youtube video.
I will appreciate your help
Note: I installed the MediaManager nuget package in my three projects: Android, iOS and NetStandard library
Note2: One of my requirements is to guarantee as much as possible the user watched the whole video before moving to the next one (the value will be stored in an external DB), so any solution different to this one must follow this requirement

I need to create a vertical listview. on Xamarin.Forms

I need to create a vertical listview with 3 options but how?
Like this:
A friend help me a little bit! but how I can add 'CornerRadius' in Layout?
Video I need to replace 'Label' with 'Frame' to add 'CornerRadius' right? how i can replace
My MainPage.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"
xmlns:local="clr-namespace:App11"
x:Class="App11.MainPage">
<StackLayout>
<ListView ItemsSource="{Binding MyModel}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid HorizontalOptions="FillAndExpand" BackgroundColor="#3e1519" HeightRequest="70">
<!--1st View-->
<StackLayout Orientation="Vertical" Grid.Column="0" Grid.Row="0" HeightRequest="40">
<Label Text="{Binding Title}" TextColor="White" />
<Label Text="{Binding SubTitle}" TextColor="White" />
</StackLayout>
<StackLayout Orientation="Horizontal" Grid.Column="1" Grid.Row="0">
<Label Text="{Binding Dollar}" TextColor="White" />
<Label Text="+" Margin="30,0,0,0" FontSize="Large" TextColor="White">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="ShowSubView" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
<!--SubView-->
<StackLayout BackgroundColor="#722f38" IsVisible="False" HeightRequest="30">
<Label Text="{Binding SubViewText}" TextColor="White" />
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My MainPage.xml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace App11
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainPageViewModel();
}
private void ShowSubView(object sender, EventArgs e)
{
var item1 = ((Label)sender);
var FirstView = ((Grid)((StackLayout)item1.Parent).Parent);
var item2 = ((StackLayout)FirstView.Children[2]);
if (item2.IsVisible)
{
Grid.SetRow(item2, 0);
item2.IsVisible = false;
}
else
{
Grid.SetRow(item2, 1);
item2.IsVisible = true;
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace App11
{
public class MainPageModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private string title;
public string Title
{
get { return title; }
set
{
title = value;
OnPropertyChanged("Title");
}
}
private string subTitle;
public string SubTitle
{
get { return subTitle; }
set
{
subTitle = value;
OnPropertyChanged("SubTitle");
}
}
private string dollar;
public string Dollar
{
get { return dollar; }
set
{
dollar = value;
OnPropertyChanged("Dollar");
}
}
private string subViewText;
public string SubViewText
{
get { return subViewText; }
set {
subViewText = value;
OnPropertyChanged("subViewText");
}
}
}
}
MY MainPageViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
namespace App11
{
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<MainPageModel> myModel;
public ObservableCollection<MainPageModel> MyModel
{
get { return myModel; }
set
{
myModel = value;
OnPropertyChanged("MyModel");
}
}
public MainPageViewModel()
{
MyModel = new ObservableCollection<MainPageModel>()
{
new MainPageModel()
{
Title = "MENSAL",
SubTitle = "cobranças a cada 30 dias",
Dollar = "R$ 22,90",
SubViewText = "Acesso a 10 campanhas por mês Até 3 campanhas por mês Visualização de perfil limitada"
},
new MainPageModel()
{
Title = "TRIMESTRAL",
SubTitle = "cobranças a cada 90 dias",
Dollar = "R$ 49,90",
SubViewText = "Acesso a rola do kid bengala quase ilimitada"
},
new MainPageModel()
{
Title = "SEMESTRAL",
SubTitle = "cobranças a cada 180 dias",
Dollar = "R$ 99,90",
SubViewText = "Acesso a rola do kid bengala ilimitada"
}
};
}
}
}
Add frame with zero padding may resolve your issue. The default padding for Frame is 20.
Try the sample code given below (not tested)
<Frame CornerRadius="10" Padding="0">
<StackLayout Orientation="Vertical" >
<Frame CornerRadius="10" Padding="0">
<StackLayout Orientation="Horizontal">
<StackLayout Orientation="Vertical">
<Label Text="{Binding Title}" TextColor="White" />
<Label Text="{Binding SubTitle}" TextColor="White" />
</StackLayout>
<Label Text="{Binding Dollar}" TextColor="White" />
<Label Text="+" Margin="30,0,0,0" FontSize="Large" TextColor="White">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="ShowSubView" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</Frame>
<StackLayout BackgroundColor="#722f38" IsVisible="False" HeightRequest="30">
<Label Text="{Binding SubViewText}" TextColor="White" />
</StackLayout>
</StackLayout>
</Frame>

Resources