Using SearchBar with ObservableCollection in MVVM - xamarin

I've created a search page in my app and I'd like to be able to search through my ObservableCollection of items in the ViewModel and display them onto a CollectionView. So far this is what I've done and I get an exception i.e System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.' every time I run the app.
SearchPage XAML
<!--Doctors Search Result-->
<Grid Grid.Row="1">
<CollectionView ItemsSource="{Binding RecentDoctors}">
<CollectionView.ItemsLayout>
<ListItemsLayout Orientation="Vertical" ItemSpacing="15"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<!--Image-->
<Frame BackgroundColor="Black"
HeightRequest="20"
WidthRequest="20"
CornerRadius="100"
Margin="20,0,0,0"
HorizontalOptions="Start"
VerticalOptions="Center"
IsClippedToBounds="True">
<Image HorizontalOptions="Center"
VerticalOptions="Center"/>
</Frame>
<StackLayout Orientation="Vertical"
VerticalOptions="Center"
Spacing="-3">
<!--Fullname-->
<Label Text="{Binding DoctorsName}"
FontSize="19"
FontAttributes="Bold"/>
<!--Specialization-->
<Label Text="{Binding Specialization}"
FontSize="14"
TextColor="LightGray"/>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<!--Doctors Search Bar-->
<Grid Grid.Row="0" ColumnSpacing="0" RowSpacing="0">
<pancake:PancakeView BackgroundColor="#0F8DF4"
HasShadow="True">
<Grid>
<!--The SearchBar-->
<renderers:CustomSearchBar x:Name="doctorsSearchBar"
Placeholder="Search Doctors by Name, Specialization"
VerticalOptions="Center"
FontSize="17"
TextColor="Black"
WidthRequest="320"
Text="{Binding SearchedText}"
SearchCommand="{Binding SearchBarCommand}"
SearchCommandParameter="{Binding Text, Source={x:Reference doctorsSearchBar}}"/>
</Grid>
</pancake:PancakeView>
</Grid>
SearchPage ViewModel
public class TelemedSearchPageViewModel : BaseViewModel
{
private string _searchedText;
public string SearchedText
{
get { return _searchedText; }
set
{
_searchedText = value;
OnPropertyChanged();
Search();
}
}
public ObservableCollection<RecentDoctorsInfo> RecentDoctors { get; set; } = new ObservableCollection<RecentDoctorsInfo>();
public ICommand SearchBarCommand { get; set; }
/// <summary>
/// Main Constructor
/// </summary>
public TelemedSearchPageViewModel()
{
SearchBarCommand = new RelayCommand(Search);
//RecentDoctorsList
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Steven Strange",
Specialization = "Sorcerer Supreme",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Peter Parker",
Specialization = "Spiderman",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Bruce Banner",
Specialization = "The Hulk",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Reed Richards",
Specialization = "Mr.Fantastic",
Location = "177a Bleecker St. | USA"
});
}
#region METHODS
public void Search()
{
if (RecentDoctors != null && RecentDoctors.Count >0)
{
var temp = RecentDoctors.Where(x => x.DoctorsName.ToLower().Contains(SearchedText.ToLower()));
foreach (var item in temp)
{
RecentDoctors.Add(item);
}
}
}
#endregion
}
Edit3:
if (RecentDoctors != null && RecentDoctors.Count > 0)
{
var results = RecentDoctors.Where(x => x.DoctorsName.ToLower().Contains(SearchedText.ToLower()));
SearchResults.Clear();
foreach (RecentDoctorsInfo item in results)
{
SearchResults.Add(item);
}
}
else
{
RecentDoctors.Clear();
}

If you want to execute the search when user type you should use a behavior as doscs suggest
public class SearchBarTextChangedBehavior : Behavior<SearchBar>
{
protected override void OnAttachedTo(SearchBar bindable)
{
base.OnAttachedTo(bindable);
bindable.TextChanged += this.SearchBar_TextChanged;
}
protected override void OnDetachingFrom(SearchBar bindable)
{
base.OnDetachingFrom(bindable);
bindable.TextChanged -= this.SearchBar_TextChanged;
}
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
((SearchBar)sender).SearchBarCommand?.Execute(e.NewTextValue);
}
}
Then attach the behavior to your SearchBar
<renderers:CustomSearchBar
x:Name="doctorsSearchBar"
Placeholder="Search Doctors by Name, Specialization"
VerticalOptions="Center"
FontSize="17"
TextColor="Black"
WidthRequest="320"
Text="{Binding SearchedText}"
SearchCommand="{Binding SearchBarCommand}">
<renderers:CustomSearchBar.Behaviors>
<behaviors:SearchBarTextChangedBehavior />
</renderers:CustomSearchBar.Behaviors>
</renderers:CustomSearchBar>
By the other hand, you should create a private copy of the original list and add the same items as the public collection
private List<RecentDoctorsInfo> originalRecentDoctorsList = new List<RecentDoctorsInfo>();
public ObservableCollection<RecentDoctorsInfo> RecentDoctors { get; set; } = new ObservableCollection<RecentDoctorsInfo>();
public ICommand SearchBarCommand { get; set; }
public TelemedSearchPageViewModel()
{
SearchBarCommand = new RelayCommand(Search);
//RecentDoctorsList
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Steven Strange",
Specialization = "Sorcerer Supreme",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Peter Parker",
Specialization = "Spiderman",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Bruce Banner",
Specialization = "The Hulk",
Location = "177a Bleecker St. | USA"
});
RecentDoctors.Add(new RecentDoctorsInfo()
{
DoctorsName = "Reed Richards",
Specialization = "Mr.Fantastic",
Location = "177a Bleecker St. | USA"
});
// Backup copy list.
originalRecentDoctorsList.Add(new RecentDoctorsInfo()
{
DoctorsName = "Steven Strange",
Specialization = "Sorcerer Supreme",
Location = "177a Bleecker St. | USA"
});
originalRecentDoctorsList.Add(new RecentDoctorsInfo()
{
DoctorsName = "Peter Parker",
Specialization = "Spiderman",
Location = "177a Bleecker St. | USA"
});
originalRecentDoctorsList.Add(new RecentDoctorsInfo()
{
DoctorsName = "Bruce Banner",
Specialization = "The Hulk",
Location = "177a Bleecker St. | USA"
});
originalRecentDoctorsList.Add(new RecentDoctorsInfo()
{
DoctorsName = "Reed Richards",
Specialization = "Mr.Fantastic",
Location = "177a Bleecker St. | USA"
});
}
And by last, your Search method should clean the public collection (the one you're showing) and use the private as backup
private void Search()
{
if (!string.IsNullOrEmpty(SearchedText))
{
var filteredDoctors = RecentDoctors
.Where(x =>
x.DoctorsName.ToLower().Contains(SearchedText.ToLower()))
.ToList();
RecentDoctors.Clear();
foreach(var recentDoctor in filteredDoctors)
RecentDoctors.Add(recentDoctor);
}
else
{
// This is when you clean the text from the search
RecentDoctors.Clear();
foreach(var originalRecentDoctor in originalRecentDoctorsList)
RecentDoctors.Add(originalRecentDoctor);
}
}

Related

Xamarin CarouselView throws OutOfRangeException when ItemsSource changes and Positon is not 0

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

Xamarin: Binding to ImageSource property is not working

I am trying to display images dynamically through binding:
I have ObservableCollection of TabItem to which I load a png into the Icon Property:
TabItems.Add(new TabItem()
{
Title = AppResources.Home,
IsSelected = true,
Icon = ImageSource.FromResource("Home.png")
});
Where:
public class TabItem : BindableObject
{
private bool _isSelected;
public ImageSource Icon { get; set; }
public string Title { get; set; }
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
}
And I have a DataTemplate of TabItem in a CollectionView that displays these items and uses the Icon Property to bind to Image:
<DataTemplate x:DataType="local:TabItem" x:Key="TabItem_DataTemplate">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
WidthRequest="{Binding Title, Converter={StaticResource ProportionateConverter}}"
>
<Image Source="{Binding Icon}" InputTransparent="True" HeightRequest="50" />
<Label HorizontalOptions="Center" VerticalOptions="End" FontSize="Small"
Text="{Binding Title}"
InputTransparent="True"
TextColor="{Binding IsSelected, Converter={StaticResource IsSelectedToTextColorConverter}}"/>
</StackLayout>
</DataTemplate>
I get an Error in runtime:
ImageLoaderSourceHandler: Image data was invalid: Xamarin.Forms.StreamImageSource
The images are in the Resources folder and are marked as Embedded resource
Updated solution:
(Thanks Jason)
The solution was using my markup extension when initializing the Icon property (I'll be glad to hear of a more elegant way)
var homeExt = new ImageResourceExtension()
{
Source = "TimeManager.Resources.Icons.Home.png"
};
TabItems.Add(new TabItem()
{
Title = AppResources.Home,
IsSelected = true,
Icon = (ImageSource)(homeExt.ProvideValue(null))
});

Xamarin Forms Google SignIn issue

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...

How to check the check box dynamically in listbox in windows phone 7?

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));
}
}
}

Removing Item from observable collection causes textbox to focus

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 :).

Resources