I have to integrate google sign in my project. i have followed this link https://www.pujolsluis.com/google-client-plugin-for-xamarin/ . I am not able to get the google username, email,picture.
xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:login2"
x:Class="login2.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<converter:InvertBooleanConverter x:Key="InvertBooleanConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Label Text="Welcome to Google Client App!"
HorizontalOptions="CenterAndExpand" VerticalOptions="Start"
FontSize="20"
TextColor="#212121"
Margin="0,32,0,0"/>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<Image HorizontalOptions="Center" VerticalOptions="Center"
WidthRequest="120" HeightRequest="120"
Margin="24"
Source="googlelogo.png"
IsVisible="{Binding IsLoggedIn, Converter={StaticResource InvertBooleanConverter}}"/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
IsVisible="{Binding IsLoggedIn}">
<Image WidthRequest="120"
HeightRequest="120"
Source="{Binding User.Picture}"
IsVisible="{Binding IsLoggedIn}"/>
<StackLayout Orientation="Vertical"
Spacing="8"
IsVisible="{Binding IsLoggedIn}"
Margin="16,0,0,0">
<Label Text="Name:"
TextColor="#212121"
FontSize="16"/>
<Label Text="{Binding User.Name}"
Margin="16,0,0,0"/>
<Label Text="Email:"
TextColor="#212121"
FontSize="16"/>
<Label Text="{Binding User.Email}"
Margin="16,0,0,0"/>
</StackLayout>
</StackLayout>
</StackLayout>
<Button Text="Login"
VerticalOptions="EndAndExpand"
IsVisible="{Binding IsLoggedIn, Converter={StaticResource InvertBooleanConverter}}"
Command="{Binding LoginCommand}"
BackgroundColor="Accent"/>
<Button Text="Logout"
VerticalOptions="EndAndExpand"
IsVisible="{Binding IsLoggedIn}"
Command="{Binding LogoutCommand}"
BackgroundColor="Accent"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
viewmodel:
internal class LoginPageViewModel : INotifyPropertyChanged
{
public GoogleUser User { get; set; } = new GoogleUser();
public string Name
{
get => User.Name;
set => User.Name = value;
}
public string Email
{
get => User.Email;
set => User.Email = value;
}
public Uri Picture
{
get => User.Picture;
set => User.Picture = value;
}
public bool IsLoggedIn { get; set; }
public ICommand LoginCommand { get; set; }
public ICommand LogoutCommand { get; set; }
public readonly IGoogleClientManager googleClientManager;
public event PropertyChangedEventHandler PropertyChanged;
public LoginPageViewModel()
{
LoginCommand = new Command(LoginAsync1);
LogoutCommand = new Command(Logout);
googleClientManager = CrossGoogleClient.Current;
IsLoggedIn = false;
}
public void LoginAsync1()
{
googleClientManager.LoginAsync();
googleClientManager.OnLogin += OnLoginCompleted;
}
public void OnLoginCompleted(object s, GoogleClientResultEventArgs<GoogleUser> loginEventArgs)
{
if (loginEventArgs.Data != null)
{
GoogleUser googleUser = loginEventArgs.Data;
User.Name = googleUser.Name;
User.Email = googleUser.Email;
User.Picture = googleUser.Picture;
// Log the current User email
Debug.WriteLine(User.Email);
IsLoggedIn = true;
}
else
{
App.Current.MainPage.DisplayAlert("Error", loginEventArgs.Message, "OK");
}
googleClientManager.OnLogin -= OnLoginCompleted;
}
public void Logout()
{
googleClientManager.OnLogout += OnLogoutCompleted;
googleClientManager.Logout();
}
private void OnLogoutCompleted(object sender, EventArgs loginEventArgs)
{
IsLoggedIn = false;
User.Email = "Offline";
googleClientManager.OnLogout -= OnLogoutCompleted;
}
}
Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="sample.com.login2" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="login2.Android"></application>
</manifest>
google_service.json:
{
"project_info": {
"project_number": "40263391396",
"firebase_url": "https://sign-e2687.firebaseio.com",
"project_id": "sign-e2687",
"storage_bucket": "sign-e2687.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:40263391396:android:8d889062d8a9f088",
"android_client_info": {
"package_name": "sample.com.login2"
}
},
"oauth_client": [
{
"client_id": "40263391396-es3vupbblfrulkup9rtd43sjdmoca7rn.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "sample.com.login2",
"certificate_hash": "6b5f4cfc5a249bac9cc88be5612e05dea6a11d65"
}
},
{
"client_id": "40263391396-db2cttbdftmjplcj1bej937eusjfv02g.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCCBrHGY89NSPa83YudpKDIwCn_n6x1Zjo"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "40263391396-db2cttbdftmjplcj1bej937eusjfv02g.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
MainActivity:
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
GoogleClientManager.Initialize(this);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
GoogleClientManager.OnAuthCompleted(requestCode, resultCode, data);
}
I have added the app to firebase. After selecting the account on app,Nothing happens. Debugging not going into OnLoginCompleted(object s, GoogleClientResultEventArgs<GoogleUser> loginEventArgs) method.Not getting the google name,email. Help please...
I have not put the Debug signing certificate SHA-1 while seeing optional when adding app to firebase. Now i entered it correct and works perfect...
Related
I have a Picker in Xamarin and I want to go to the page which is selected in Picker. So here is my view in Picker:
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="20">
<Label Text="List of tables" FontAttributes="Bold" HorizontalOptions="Center"/>
<Picker Title="Select table" ItemsSource="{Binding TablesFromViewModelCollector}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding SelectedTable}" />
<Label Text="{Binding SelectedTable.Name}" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}"/>
<Button Text="Select Table" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}" Clicked="Selected_Button"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
Here is it's contoller:
public TablePage()
{
InitializeComponent();
BindingContext = new TableViewModel();
}
async void Selected_Button(object sender, System.EventArgs e)
{
await Navigation.PushAsync(new TableDetails());
}
And here is TableViewModel where I can select the table from Picker:
class TableViewModel : INotifyPropertyChanged
{
public IList<Table> TablesFromViewModelCollector { get { return TableData.Tables; } }
Table selectedTable;
public Table SelectedTable
{
get { return selectedTable; }
set
{
if(SelectedTable != value)
{
selectedTable = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
So I added button under the Picker as you see. And what I am trying to achieve, when the Picker is selected and button is clicked, goes to the page of TableDetails. In this page, I want to show the data of selectedTable. Basically, here is my view for TableDetails:
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="20">
<Label Text="{Binding SelectedTable.Name}" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
And here is my controller:
public partial class TableDetails : ContentPage
{
public TableDetails()
{
InitializeComponent();
BindingContext = new TableViewModel();
}
}
But I dont get the value of the selectedTable.
just pass the value on the constructor
await Navigation.PushAsync(new TableDetails(VM.SelectedTable));
then modify the page constructor
public TableDetails(Table selected)
{
InitializeComponent();
BindingContext = new TableViewModel();
}
I have a Xamarin Forms App with a Carousel View and my plan is to filter the result by the Priorities shown in a Bottom Tap Bar.
When the View is on the first card, everything works fine, but if you hit one of the filters from a different card, then the first one, a System.ArgumentOutOfRangeException exception got fired.
I tried already a lot of things to set the position of the Carousel View to 0, but in vain.
using the CarouselView_PositionChanged event => not working
using the TabHost_SelectedTabIndexChanged event => not working
setting the property of the CarouselView by the selectedIndex changed from the Tab View from the ViewModel => not working
turn it into a CollectionView and everything is working => just looks ugly 🤮
Here the 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"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
xmlns:viewModels="clr-namespace:AmsXamarin.ViewModels"
xmlns:fontAwesome="clr-namespace:FontAwesome"
xmlns:cells="clr-namespace:AmsXamarin.Cells"
xmlns:tabs="http://sharpnado.com"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="AmsXamarin.Views.SnagListX.SnagListPage"
x:Name="mySnagListPage">
<ContentPage.BindingContext>
<viewModels:SnagListViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<xct:TabSelectionChangedEventArgs x:Key="TabSelectionChangedEventArgs" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Command="{Binding ShowMyJobsCommand}"
Order="Primary"
Priority="0"
Text="{Binding MyJobsTitle,Mode=TwoWay}">
</ToolbarItem>
<ToolbarItem Command="{Binding AddFaultCommand}"
Order="Primary"
Priority="0">
<ToolbarItem.IconImageSource>
<FontImageSource FontFamily="FAS"
Glyph="{x:Static fontAwesome:FontAwesomeIcons.Plus}"
Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
Size="Large" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
<ToolbarItem Command="{Binding AcceptCommand}"
Order="Primary"
Priority="0">
<ToolbarItem.IconImageSource>
<FontImageSource FontFamily="FAS"
Glyph="{x:Static fontAwesome:FontAwesomeIcons.UserCheck}"
Color="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Primary}}"
Size="Large" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout>
<Label Style="{StaticResource LabelLarge}"
Text="Snag List"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<StackLayout>
<IndicatorView x:Name="snagListIndicator"
Margin="0,10,0,0"
IndicatorColor="LightGray"
SelectedIndicatorColor="LightGray"
IndicatorSize="6" />
<RefreshView Command="{Binding RefreshCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
Style="{StaticResource BaseRefreshView}">
<CarouselView x:Name="snagListCV"
Margin="10"
IndicatorView="snagListIndicator"
ItemsLayout="HorizontalList"
ItemsSource="{Binding SnagListPriorities.SnagListApps}"
CurrentItem="{Binding SnagListItem}"
Position="{Binding PositionIdx,Mode=OneWay}"
PositionChanged="CarouselView_PositionChanged"
Loop="False"
IsEnabled="{Binding IsNotBusy}">
<CarouselView.EmptyView>
<StackLayout Padding="12">
<Label Style="{StaticResource LabelMedium}"
HorizontalOptions="Center"
Text="{Binding EmptyMessage,Mode=TwoWay}" />
</StackLayout>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<cells:SnagListCardX />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</RefreshView>
</StackLayout>
<tabs:TabHostView x:Name="TabHost"
Margin="13,0,13,10"
BackgroundColor="{AppThemeBinding Dark={StaticResource Third},Light={StaticResource Fourth}}"
CornerRadius="30"
IsSegmented="True"
Orientation="Horizontal"
TabType="Fixed"
SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}"
Shades="{StaticResource LightBottomShadow}">
<tabs:TabHostView.Behaviors>
<xct:EventToCommandBehavior EventName="SelectedTabIndexChanged"
Command="{Binding SelectedCommand}"
EventArgsConverter="{StaticResource TabSelectionChangedEventArgs}" />
</tabs:TabHostView.Behaviors>
<tabs:TabHostView.Tabs>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="All">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource All}"
TextColor="White"
Text="{Binding SnagListPriorities.All,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="High"
StyleClass="">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource High}"
TextColor="White"
Text="{Binding SnagListPriorities.High,Mode=OneWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="Medium">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource Medium}"
TextColor="Black"
Text="{Binding SnagListPriorities.Medium,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
<tabs:BottomTabItem Style="{StaticResource BottomTabsMedium}"
Label="Low">
<tabs:BottomTabItem.Badge>
<tabs:BadgeView Style="{StaticResource BadgeViewBase}"
BackgroundColor="{StaticResource Low}"
TextColor="Black"
Text="{Binding SnagListPriorities.Low,Mode=TwoWay}" />
</tabs:BottomTabItem.Badge>
</tabs:BottomTabItem>
</tabs:TabHostView.Tabs>
</tabs:TabHostView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
And the view model
using AmsXamarin.Helpers;
using AmsXamarin.Models;
using AmsXamarin.Services;
using MvvmHelpers;
using MvvmHelpers.Commands;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Command = MvvmHelpers.Commands.Command;
namespace AmsXamarin.ViewModels
{
public class SnagListViewModel : BaseViewModel
{
readonly ISnagListService snagListService;
List<SnagListAppX> SnagListAppXs;
SnagListAppX snagListItem;
public SnagListAppX SnagListItem
{
get { return snagListItem; }
set
{
snagListItem = value;
OnPropertyChanged();
}
}
SnagListPriorities snagListPriorities;
public SnagListPriorities SnagListPriorities
{
get { return snagListPriorities; }
set
{
snagListPriorities = value;
OnPropertyChanged();
}
}
public AsyncCommand AddFaultCommand { get; }
public AsyncCommand AcceptCommand { get; }
public Command RefreshCommand { get; }
public Command SelectedCommand { get; }
public Command ShowMyJobsCommand { get; }
string emptyMessage;
public string EmptyMessage
{
get { return emptyMessage; }
set
{
emptyMessage = value;
OnPropertyChanged();
}
}
int positionIdx;
public int PositionIdx
{
get { return positionIdx; }
set
{
positionIdx = value;
OnPropertyChanged();
}
}
int selectedIndex;
public int SelectedIndex
{
get => selectedIndex;
set => SetProperty(ref selectedIndex, value);
}
string myJobsTitle = "My Jobs";
int staffId;
public string MyJobsTitle
{
get { return myJobsTitle; }
set
{
myJobsTitle = value;
OnPropertyChanged();
}
}
bool myJobsEnabled = true;
bool updateProperty = false;
public bool AcceptToolBar { get; set; }
public SnagListViewModel()
{
SnagListItem = new SnagListAppX();
SnagListPriorities = new SnagListPriorities()
{
SnagListApps = new ObservableRangeCollection<SnagListAppX>()
};
SnagListAppXs = new List<SnagListAppX>();
AddFaultCommand = new AsyncCommand(ShowAddSnagListItem);
AcceptCommand = new AsyncCommand(ShowModalAccept);
SelectedCommand = new Command(Selected);
RefreshCommand = new Command(Refresh);
staffId = 0;
ShowMyJobsCommand = new Command(ShowModalMyJobs);
snagListService = DependencyService.Get<ISnagListService>();
Device.BeginInvokeOnMainThread(async () =>
{
await GetSnagListItems();
});
}
async Task GetSnagListItems()
{
try
{
IsBusy = true;
EmptyMessage = "Loading...";
SnagListPriorities slp = await snagListService.GetSnagLists(false, true);
SnagListPriorities.SnagListApps.Clear();
SnagListPriorities = slp;
SnagListAppXs.Clear();
SnagListAppXs.AddRange(slp.SnagListApps);
EmptyMessage = SnagListAppXs.Count == 0 ? "Good Job! \r\n\r\nCurrently nothing to fix" : "";
IsBusy = false;
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error", ex.Message, "Ok");
}
}
void Selected()
{
//PositionIdx = 0;
//OnPropertyChanged(nameof(PositionIdx));
//FilterSnagList();
Refresh();
}
void FilterSnagList()
{
var allJobs = SnagListAppXs;
if (staffId != 0) allJobs = allJobs.Where(x => x.StaffId == staffId).ToList();
if (updateProperty) OnPropertyChanged(nameof(SnagListPriorities));
if (selectedIndex > 0) allJobs = allJobs.Where(sli => sli.Priority == selectedIndex).ToList();
SnagListPriorities.SnagListApps.Clear();
//SnagListPriorities.SnagListApps = new ObservableRangeCollection<SnagListAppX>(allJobs);
SnagListPriorities.SnagListApps.AddRange(allJobs);
//OnPropertyChanged(nameof(SnagListPriorities.SnagListApps));
updateProperty = false;
}
void ShowModalMyJobs()
{
staffId = myJobsEnabled ? int.Parse(Settings.StaffIdSetting) : 0;
MyJobsTitle = myJobsEnabled ? "All Jobs" : "My Jobs";
AcceptToolBar = !myJobsEnabled;
myJobsEnabled = !myJobsEnabled;
SelectedIndex = 0;
updateProperty = true;
}
void Refresh()
{
IsBusy = true;
var allJobs = SnagListAppXs.Where(s => s.StaffId == 115);
SnagListPriorities.SnagListApps.Clear();
SnagListPriorities.SnagListApps.AddRange(allJobs);
IsBusy = false;
}
}
}
looks a bit messy, but wanted to show, that I tried already a lot of things.
Last try was a simple RefreshView.
It works as long as the itemsSource is not changing. Once it changes and the first card is not shown, it crashes.
Any ideas? Many thanks
I have a MDPage which is a MasterDetailPage which calls side menu items list page as Master. The Detail for this is added with a new NavigationPage of a home page.
My code is
public MDPage(){
Master = new SideMenuPage();
InitializeComponent();
masterPage.ListView.ItemSelected += OnItemSelected;
Detail = new NavigationPage(new HomePage())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};}
I have a requirement where the NavigationPage contains a bottom menu such that on clicking a bottom menu, the MDPage needs to be reinitialized to set a new NavigationPage of a different page (say AboutUs page). So i added a parameterized constructor for MDPage and on clicking the bottom menu item i am calling App.Current.MainPage = new MDPage("AboutUs").Below is the parameterized constructor.
public MDPage(string bottomMenuItem)
{
Master = new SideMenuPage();
InitializeComponent();
masterPage.ListView.ItemSelected += OnItemSelected;
if("AboutUs" == bottomMenuItem)
{
Detail = new NavigationPage(new AboutUs())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}}
Here the MDPage constructor is called initially when i open the app. Then from the side menu i have the option to open a HomePage which is added as Detail. Note that this HomePage contains a bottom sub menu, which is nothing but an image. On tapping this, it should again reinitialize the MDPage. Here this is working fine in Andorid. But in iOS, it is throwing null exception. It is not allowing me to set Master = new SideMenuPage(); I have found the root cause by trial and error as this code will not throw exception and it is throwing in the iOS Main.cs file. Please help me.
MainPage - XAML
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ClubApp.Views;assembly=ClubApp"
x:Class="ClubApp.Views.MainPage">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
MainPage.xaml.cs
using System.Linq;using System.Text;using System.Threading.Tasks;using Xamarin.Forms;namespace Test.Views{
public partial class MainPage : MasterDetailPage
{
public MainPage()
{
Master = new MasterPage();
InitializeComponent();
masterPage.ListView.ItemSelected += OnItemSelected;
Detail = new NavigationPage(new Views.HomePage())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
public MainPage(string bottomMenu)
{
Master = new MasterPage();
InitializeComponent();
masterPage.ListView.ItemSelected += OnItemSelected;
if ("News" == bottomMenu)
{
Detail = new NavigationPage(new Views.HomePage())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
else if ("Profile" == bottomMenu)
{
Detail = new NavigationPage(new Views.Profile())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
}
async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
try
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
if (item.Title == "News")
{
Detail = new NavigationPage(new Views.NewsPage())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
if (item.Title == "Home")
{
Detail = new NavigationPage(new Views.HomePage())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
if (item.Title == "Profile")
{
Detail = new NavigationPage(new Views.Profile())
{
BarBackgroundColor = Color.Black,
BarTextColor = Color.White,
};
}
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
catch (Exception ex)
{
}
}
}}
MasterPage.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="Test.Views.MasterPage"> <ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" BackgroundColor="#10000c" Padding = "0,50,0,0" >
<StackLayout x:Name="slUserProfile" Orientation="Vertical" Spacing = "0"
VerticalOptions="FillAndExpand">
<Image Source="{Binding member_image}" x:Name="imgSideImage"
HorizontalOptions="CenterAndExpand" Aspect="AspectFill" HeightRequest="100"
WidthRequest="100" />
<Label Text="{Binding name}" TextColor="#efa747"
FontSize ="17"
HorizontalOptions="CenterAndExpand"/>
</StackLayout >
<ListView x:Name="lvSideMenu" VerticalOptions="FillAndExpand" SeparatorVisibility="None"
BackgroundColor="Black">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="15,5,0,0" Orientation="Horizontal">
<Image Source="{Binding IconSource}"/>
<StackLayout Padding = "10,0,0,0">
<local:CustomLabel Text="{Binding Title}" VerticalOptions="CenterAndExpand"
TextColor="#dac6ac" FontSize ="14"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout></ContentPage.Content></ContentPage>
MasterPage.xaml.cs
using Xamarin.Forms;namespace Test.Views{
public partial class MasterPage : ContentPage
{
public ListView ListView { get { return lvSideMenu; } }
public Page Detail { get; set; }
public MasterPage()
{
InitializeComponent();
var masterPageItems = new List<MasterPageItem>();
//Fills side menu items.
masterPageItems.Add(new MasterPageItem
{
Title = "Profile",
IconSource = "profile_sidemenu.png"
});
masterPageItems.Add(new MasterPageItem
{
Title = "Home",
IconSource = "home_smenu.png"
});
masterPageItems.Add(new MasterPageItem
{
Title = "News",
IconSource = "news_smenu.png"
});
lvSideMenu.ItemsSource = masterPageItems;
Icon = "menu.png";
Title = "Menu";
}
}
public class MenuItems
{
public string MenuTitle { get; set; }
}
public class MasterPageItem
{
public string Title { get; set; }
public string IconSource { get; set; }
}}
HomePage.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="Test.Views.HomePage" Title="Home"><ContentPage.Content><StackLayout> <Label Text="This is Home page"/></StackLayout></ContentPage.Content></ContentPage>
Profile.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="Test.Views.Profile" Title="Profile"><ContentPage.Content> <StackLayout>
<StackLayout>
<Label Text="This is Profile page" />
</StackLayout>
<StackLayout HeightRequest="80">
<Grid RowSpacing="0" ColumnSpacing="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout x:Name="tProfile" BackgroundColor="Red" Grid.Column="0" Spacing="0" Padding="0,10,0,10">
<Image Source="bprofileSel.png" HorizontalOptions="Center" VerticalOptions="End"/>
<Label Text="Profile" FontSize="10" TextColor="White" HorizontalOptions="CenterAndExpand" VerticalTextAlignment="Center"/>
</StackLayout>
<StackLayout x:Name="tNews" BackgroundColor="Black" Grid.Column="1" Spacing="0" Padding="0,10,0,10">
<Image Source="bNewsUnsel.png" HorizontalOptions="Center" VerticalOptions="End"/>
<local:CustomLabel Text="News" FontSize="10" TextColor="White" HorizontalOptions="CenterAndExpand" VerticalTextAlignment="Center"/>
</StackLayout>
</Grid>
</StackLayout>
</StackLayout></ContentPage.Content></ContentPage>
Profile.xaml.cs
Using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Xamarin.Forms;namespace Test.Views{
public partial class Profile : ContentPage
{
public Profile()
{
InitializeComponent();
try
{
tProfile.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(() => ProfileClicked()),
});
tNews.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(() => NewsClicked()),
});
}
catch (Exception ex)
{
}
}
private void ProfileClicked()
{
App.Current.MainPage = new MainPage("Profile");
}
private void NewsClicked()
{
App.Current.MainPage = new MainPage("News");
}
}}
News page can be set similar to the profile page. In app.cs call MainPage = new MainPage()
Here when the app is launched, it will show the HomePage with the side menu. Now when i click a menu (say profile from side menu), it will take me to the profile page and according to the design a bottom submenus can be seen there. Clicking on it will cause the null exception occurring in iOS code. This is working fine in android. My assumption is the root cause is due to MasterPage reinitialization to Master. But i need this requirement to be flown like this. Please help me.
This is the error in iOS Main.cs
I have the listbox with four checkbox items. If I give the input as 2 then first two check boxes should select,this should happen programatically.
Please help me out..
Thanks,
.xaml page
<StackPanel Background="White" Margin="5,0,5,0">
<ListBox x:Name="listBox2" Foreground="Black" Height="300" SelectionMode="Multiple" SelectionChanged="listBox2_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Name="stackpanel1" Orientation="Horizontal" >
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Name}" Foreground="Black" Height="68" Margin="0,-15,0,-10" BorderThickness="0" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
</CheckBox>
</StackPanel>
<Rectangle Fill="Gray" Height=".5" HorizontalAlignment="Stretch" Width="440" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
.cs code:
List<Items> source = new List<Items>();
source.Add(new Items() { Name = "Item 1" });
source.Add(new Items() { Name = "Item 2" });
source.Add(new Items() { Name = "Item 3" });
source.Add(new Items() { Name = "Item 4" });
source.Add(new Items() { Name = "Item 5" });
source.Add(new Items() { Name = "Item 6" });
`` listBox2.ItemsSource = source;
Items class
public class Items
{
public string Name
{
get;
set;
}
}
Here's a really crude way of doing what I think you've described. It doesn't use much of your code directly but should get you moving in the right direction.
XAML
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<toolkit:ListPicker SelectionChanged="ListPicker_OnSelectionChanged">
<toolkit:ListPickerItem>0</toolkit:ListPickerItem>
<toolkit:ListPickerItem>1</toolkit:ListPickerItem>
<toolkit:ListPickerItem>2</toolkit:ListPickerItem>
<toolkit:ListPickerItem>3</toolkit:ListPickerItem>
<toolkit:ListPickerItem>4</toolkit:ListPickerItem>
</toolkit:ListPicker>
<ItemsControl x:Name="TheListBox" Grid.Row="1" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Note that I'm using the Windows Phone Toolkit (get it from http://phone.codeplex.com/ or nuget)
C#
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<Items> source = new ObservableCollection<Items>();
public MainPage()
{
InitializeComponent();
source.Add(new Items { Id = 1 });
source.Add(new Items { Id = 2 });
source.Add(new Items { Id = 3 });
source.Add(new Items { Id = 4 });
TheListBox.ItemsSource = source;
}
private void ListPicker_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (source.Count > 0 && e.AddedItems.Count > 0)
{
for (int i = 0; i < source.Count; i++)
{
this.source[i].IsChecked = int.Parse(((ContentControl)(e.AddedItems[0])).Content.ToString()) >= this.source[i].Id;
}
}
}
}
public class Items : INotifyPropertyChanged
{
private bool isChecked;
public int Id { get; set; }
public string Name
{
get
{
return "Item " + this.Id;
}
}
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
this.isChecked = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have a textbox that is on the same screen as a listbox. When I remove an item from the listbox, which is bound to an observable collection, the textbox gains focus. Is there a way to stop this from happening?
I am using MVVM but am open to putting some code in the code behind if it fixes this problem.
EDIT:
CodeBehind View:
namespace Offload.WinPhone.Views
{
using System.Windows.Controls;
using Microsoft.Phone.Controls;
public partial class MainPageView : PhoneApplicationPage
{
public MainPageView()
{
InitializeComponent();
}
private void QuickNoteBodyEditor_TextChanged(object sender, TextChangedEventArgs e)
{
var senderAsTextbox = (TextBox)sender;
if (senderAsTextbox.Text.Length == 0)
this.Focus();
}
}
}
View:
<Grid x:Name="LayoutRoot" Background="Transparent" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="OFFLOAD" FontFamily="Segoe WP Bold" Grid.Row="0"/>
<ListBox x:Name="QuickNotes" Grid.Row="1" TabIndex="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,5,0,5" Orientation="Vertical">
<TextBlock Style="{StaticResource PhoneTextExtraLargeStyle}" TextWrapping="Wrap" Text="{Binding Body}"/>
<TextBlock Style="{StaticResource PhoneTextAccentStyle}" Text="{Binding Timestamp, StringFormat='{}{0:dd MMM HH:mm}'}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="QuickNoteBodyEditor" AcceptsReturn="True" Grid.Row="2" InputScope="Chat" TextChanged="QuickNoteBodyEditor_TextChanged" IsTabStop="True" />
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBar.Buttons>
<cal:AppBarButton IconUri="/AppFramework/Resources/Icons/Add.png" Text="Add" Message="AddQuickNote" />
<cal:AppBarButton IconUri="/AppFramework/Resources/Icons/Delete.png" Text="Delete" Message="DeleteSelectedQuickNote" />
</shell:ApplicationBar.Buttons>
<shell:ApplicationBar.MenuItems>
<cal:AppBarMenuItem Text="About" Message="NavigateToAddAccountView"/>
<cal:AppBarMenuItem Text="Review" Message="NavigateToAddAccountView"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
ViewModel:
public class MainPageViewModel : PropertyChangedBase
{
private ObservableCollection<QuickNote> quickNotes;
private string quickNoteBodyEditor;
private QuickNote selectedQuickNote;
// Databound Properties
public string QuickNoteBodyEditor
{
get { return quickNoteBodyEditor; }
set { quickNoteBodyEditor = value; NotifyOfPropertyChange(() => QuickNoteBodyEditor); NotifyOfPropertyChange(() => CanAddQuickNote);}
}
public QuickNote SelectedQuickNote
{
get { return selectedQuickNote; }
set { selectedQuickNote = value; NotifyOfPropertyChange(() => SelectedQuickNote); NotifyOfPropertyChange(() => CanDeleteSelectedQuickNote); }
}
public ObservableCollection<QuickNote> QuickNotes
{
get { return quickNotes; }
set { quickNotes = value; NotifyOfPropertyChange(() => QuickNotes); }
}
// Guard Clauses
public bool CanAddQuickNote
{
get { return !string.IsNullOrWhiteSpace(quickNoteBodyEditor); }
}
public bool CanDeleteSelectedQuickNote
{
get{ return selectedQuickNote == null ? false : true; }
}
// Constructors
public MainPageViewModel()
{
GetQuickNotesFromIsolatedStorage();
WatchWhatsGotFocus.StartWatching();
}
// Public Methods
public void AddQuickNote()
{
if (CanAddQuickNote)
{
quickNotes.Add(new QuickNote(quickNoteBodyEditor, DateTime.Now));
AddQuickNotesToIsolatedStorage();
quickNoteBodyEditor = string.Empty;
NotifyOfPropertyChange(() => QuickNoteBodyEditor);
NotifyOfPropertyChange(() => QuickNotes);
}
}
public void DeleteSelectedQuickNote()
{
if (CanDeleteSelectedQuickNote)
{
quickNotes.Remove(selectedQuickNote);
AddQuickNotesToIsolatedStorage();
selectedQuickNote = null;
NotifyOfPropertyChange(() => SelectedQuickNote);
NotifyOfPropertyChange(() => QuickNotes);
}
}
private void GetQuickNotesFromIsolatedStorage()
{
quickNotes = IsolatedStorage.Get<ObservableCollection<QuickNote>>("QuickNoteList");
}
private void AddQuickNotesToIsolatedStorage()
{
IsolatedStorage.Add("QuickNoteList", quickNotes);
}
}
I had the same issue, a possible solution is to set the TextBox to disabled before modifying your collection and enable it afterwards again:
TextBox.IsEnabled = false;
ObservableCollection.Remove(object);
TextBox.IsEnabled = true;
I wouldn't call it a clean approach, but it worked for me :).