Xamarin.Forms: Creating a SearchBar - xaml

Good Day everyone. I'm trying to create a SearchBar that do the searching on a ListView. The records on the ListView are retrieved from a Database in Visual Studio.
I have tried some codes but wasn't able to make it work. And I'm just a newbie here in Xamarin.Forms. Hope you guys can help me.
These are some of the codes I've tried:
CustomerList.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinFormsDemo.Views.ClientListPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="Client List">
<ContentPage.BindingContext>
<ViewModels:CustomerVM/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<SearchBar x:Name="searchcustomer"
Placeholder="Search"
Text="{Binding SearchedText, Mode=TwoWay}"
SearchCommand="{Binding SearchCommand}"/>
<ListView ItemsSource="{Binding CustomerList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding CUSTOMER_NAME}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding CUSTOMER_CODE}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="2"
Text="{Binding CUSTOMER_CONTACT}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2016 SMESOFT.COM.PH All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
CustomerViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using XamarinFormsDemo.Services;
namespace XamarinFormsDemo.ViewModels
{
public class CustomerVM : INotifyPropertyChanged
{
private void SearchCommandExecute()
{
var tempRecords = _customerList.Where(c => c.CUSTOMER_NAME.Contains(SearchedText));
CustomerList = new List<Customer>(tempRecords);
}
public ICommand SearchCommand { get; set; }
private string _searchedText;
public string SearchedText
{
get { return _searchedText; }
set { _searchedText = value; OnPropertyChanged(); }
}
private List<Customer> _customerList;
public List<Customer> CustomerList
{
get
{
return _customerList;
}
set
{
_customerList = value;
OnPropertyChanged();
}
}
public CustomerVM()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var customerServices = new CustomerServices();
CustomerList = await customerServices.GetCustomerAsync();
SearchCommand = new Command(() => Debug.WriteLine("Command executed"));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If you need to see more codes, just please let me know. Thanks a lot.

First: Change
private List<Customer> _customerList;
public List<Customer> CustomerList
{
get
{
return _customerList;
}
set
{
_customerList = value;
OnPropertyChanged();
}
}
for
private ObservableList <Customer> _customerList;
public ObservableList <Customer> CustomerList
{
get
{
return _customerList;
}
set
{
_customerList = value;
OnPropertyChanged();
}
}
And change this
private void SearchCommandExecute()
{
var tempRecords = _customerList.Where(c => c.CUSTOMER_NAME.Contains(SearchedText));
CustomerList = new List<Customer>(tempRecords);
}
to
private void SearchCommandExecute()
{
var tempRecords = _customerList.Where(c => c.CUSTOMER_NAME.Contains(SearchedText));
CustomerList.Clear();
foreach (var item in tempRecords)
{
CustomerList.Add(item);
}
}
Basically you need to use an observable list to notify the view about the changes and avoid create a new instance every time.

Related

Saving with SQL

Hi all I have a page that I'm trying to ADD to a list and save it with viewmodel and SQL. but my list is empty Can you tell me where am I wrong??
on my Xaml (Page1):
<Entry Margin="5" Text="{Binding Xname}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" FontSize="16" x:Name="entry1" Placeholder="Enter X-rays (6 MV)" PlaceholderColor="White" BackgroundColor="Gray"/>
<Button Grid.Column="0"
Grid.Row="2"
FontAttributes="Bold"
Command="{Binding AddXrayCommand}"
Text="Add" />
<ListView Grid.Row="3" Grid.ColumnSpan="2" HasUnevenRows="True" Margin="40"
ItemsSource="{Binding energyX}" SelectedItem="{Binding selecteditemX}"
HorizontalOptions="Start" WidthRequest="150" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" BackgroundColor="White" Text="{Binding xray}" FontSize="Large" HorizontalTextAlignment="Center" TextColor="Black" WidthRequest="35"/>
<Frame Grid.Row="1" BackgroundColor="Gray"></Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
my Model (Energypage):
public class EnergyX
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string xray { get; set; }
}
And on my EnergyViewModel:
public class EnergyViewModel
{
public SQLiteConnection conn;
public ObservableCollection<EnergyX> energyX { get; set; } = new ObservableCollection<EnergyX>();
public string Xname { get; set; }
public ICommand AddXrayCommand => new Command(AddX);
public SQLiteConnection GetSQLiteConnection()
{
var fileName = "Energys.db";
var documentPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
var path = Path.Combine(documentPath, fileName);
var connection = new SQLiteConnection(path);
return connection;
}
public void AddX()
{
if (Xname != null && Xname.Length > 0)
{
EnergyX XX = new EnergyX();
XX.xray = Xname;
conn = GetSQLiteConnection();
conn.CreateTable<EnergyX>();
var dataX = conn.Table<EnergyX>();
var resultX = conn.Insert(XX);
energyX = new ObservableCollection<EnergyX>(conn.Table<EnergyX>().ToList());
}
}
}
}
I did bond my page1 to Energyviewmodel, and it works find when I didn't use SQL service, I think my SQL has problem...
I have debug the viewmodel and program run to the end of the Viewmodel but my xaml page list is empty.
this line creates a completely new instance of energyX when your ListView is bound to the old instance
energyX = new ObservableCollection(conn.Table().ToList());
there are two ways you could fix this
option 1, use INotifyPropertyChanged and raise a PropertyChanged event in the setter of energyX
option 2, don't create a new instance of energyX. Instead just add the new item to the existing instance
energyX.Add(XX);

How to change CollectionView size according sizes of its items

I am trying to create a custom Action Sheet using CollectionView, but I have a problem with auto resizing the height. When I insert the CollectionView into the page, it takes up all the free space. What needs to be done so that the height of the CollectionView changes depending on the height of its elements?
<?xml version="1.0" encoding="utf-8"?>
<StackLayout
Margin="60, 0">
<CollectionView
BackgroundColor="White"
x:Name="CollectionViewControl">
<CollectionView.Header>
<Label
x:Name="HeaderLabel"
TextTransform="Uppercase"
HorizontalTextAlignment="Center"
FontAttributes="Bold" />
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0, 10" x:DataType="modalDialogs:ActionSheetItem">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" />
<Image Grid.Column="1" Source="arrow_down" IsVisible="{Binding IsSelected}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Button
BackgroundColor="Transparent"
TextColor="Green"
Text="Cancel"
HorizontalOptions="End" />
</CollectionView.Footer>
</CollectionView>
</StackLayout>
Current UI
When I insert the CollectionView into the page, it takes up all the free space. What needs to be done so that the height of the CollectionView changes depending on the height of its elements?
From Jason's opinion, little rows in Collectionview, you can set a value to collectionView.HeightRequest. when item increase, the height will increase as well.
I create a property called.RowHeigth. If I add item in the CollectionView(with AddCommand), RowHeigth will increase.
<StackLayout Margin="60,0">
<CollectionView
x:Name="CollectionViewControl"
BackgroundColor="Blue"
HeightRequest="{Binding rowHeigth}"
ItemsSource="{Binding items}">
<CollectionView.Header>
<Label
x:Name="HeaderLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center"
TextTransform="Uppercase" />
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" />
<Image
Grid.Column="1"
HeightRequest="30"
IsVisible="{Binding IsSelected}"
Source="a11.png" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Button
BackgroundColor="Transparent"
HorizontalOptions="End"
Text="Cancel"
TextColor="Green" />
</CollectionView.Footer>
</CollectionView>
<Button
x:Name="btnadd"
Command="{Binding AddCommand}"
Text="add item" />
</StackLayout>
public partial class Page5 : ContentPage
{
public Page5()
{
InitializeComponent();
this.BindingContext = new itemviewmodel();
}
}
public class itemviewmodel:ViewModelBase
{
public ObservableCollection<ActionSheetItem> items { get; set; }
private int _rowHeigth;
public int rowHeigth
{
get { return _rowHeigth; }
set
{
_rowHeigth = value;
RaisePropertyChanged("rowHeigth");
}
}
public ICommand AddCommand { protected set; get; }
public itemviewmodel()
{
items = new ObservableCollection<ActionSheetItem>();
for(int i=0;i<5;i++)
{
ActionSheetItem item = new ActionSheetItem();
item.Name = "name " + i;
item.IsSelected = true;
items.Add(item);
}
rowHeigth = items.Count * 60;
AddCommand = new Command<ActionSheetItem>(async (key) => {
items.Add(new ActionSheetItem() { Name = "test letter ", IsSelected=true });
rowHeigth = items.Count * 60;
});
}
}
The ViewModelBase is class that implementing INotifyPropertyChanged interface.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In my case BindableLayaout attached to StackLayout was best solution.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/layouts/bindablelayout

Back To Basics: Xamarin Forms Binding

I need some help with the basics of Xamarin.Forms in Visual Studio 2017, please.
I have written a class to hold certain properties that I can bind to on my content page. I have read a number of examples but each time I still can not get it right!
When I compile the page comes up with the label
<Label
Text="TestBinding"
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Start"
WidthRequest="100"
VerticalOptions="Center"/>
correctly, but the
<Label
Text="{Binding TestBinding}"
Grid.Row="0"
Grid.Column="1"
HorizontalOptions="Start"
WidthRequest="100"
VerticalOptions="Center"/>
comes up empty instead of the text Test binding so clearly I have got something wrong
Please can someone suggest what I am missing
I have stripped back the code to something simple here
So I believe my view class is
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Xamarin.Forms;
namespace FitRestults_Dev1
{
public class AddStudentView : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string _testtext="Test binding";
public string TestBinding
{
get=> _testtext;
set
{
if (string.Equals(_testtext, value))
return;
_testtext = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TestBinding)));
}
}
public ObservableCollection<GradeCollection> GradeCollection { get; set; }
public AddStudentView()
{ }
}
}
And my content page is
<?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="FitRestults_Dev1.AddStudent"
xmlns:local="clr-namespace:FitRestults_Dev1.AddStudentView;assembly=FitRestults_Dev1"
BindingContext="x:local "
>
<ContentPage.Content>
<StackLayout Padding="10" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Grid>
<Label Text="TestBinding " Grid.Row="0" Grid.Column="0" HorizontalOptions="Start" WidthRequest="100" VerticalOptions="Center"/>
<Label Text="{Binding TestBinding} " Grid.Row="0" Grid.Column="1" HorizontalOptions="Start" WidthRequest="100" VerticalOptions="Center"/>
</Grid>
<Button Text="Save" HorizontalOptions="FillAndExpand" BackgroundColor="Blue" TextColor="White" Clicked="Save_Clicked" />
<Button Text="Cancel" HorizontalOptions="FillAndExpand" BackgroundColor="Blue" TextColor="White" Clicked="Cancel_Clicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
And finally behind page code is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace FitRestults_Dev1
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AddStudent : ContentPage
{
public AddStudentView Source = new AddStudentView();
public AddStudent ()
{
InitializeComponent ();
BindingContext = Source;
}
async void Save_Clicked(object sender, System.EventArgs e)
{
var personItem = (Student)BindingContext;
await App.Database.SaveStudentAsync(personItem);
await Navigation.PopAsync();
}
async void Cancel_Clicked(object sender, System.EventArgs e)
{
await Navigation.PopAsync();
}
}
}
You can try the following code and let me know if you have any issues with this code. Thank you.
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="FitRestults_Dev1.AddStudent"
xmlns:local="clr
namespace:FitRestults_Dev1.AddStudentView;assembly=FitRestults_Dev1"
BindingContext="x:local ">
<ContentPage.Content>
<StackLayout Padding="10" HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<Grid>
<Label Text="TestBinding " Grid.Row="0" Grid.Column="0"
HorizontalOptions="Start" WidthRequest="100" VerticalOptions="Center"/>
<Label Text="{Binding TestBinding} " Grid.Row="0" Grid.Column="1"
HorizontalOptions="Start" WidthRequest="100" VerticalOptions="Center"/>
</Grid>
<Button Text="Save" HorizontalOptions="FillAndExpand" BackgroundColor="Blue"
TextColor="White" Clicked="SaveCommand" />
<Button Text="Cancel" HorizontalOptions="FillAndExpand"
BackgroundColor="Blue" TextColor="White" Clicked="CancelCommand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AddStudent : ContentPage
{
public AddStudentView Source = new AddStudentView(this.Navigation);
public AddStudent ()
{
InitializeComponent ();
BindingContext = Source;
}
}
ViewModel :
public class AddStudentView : INotifyPropertyChanged
{
private ICommand _navigation;
public AddStudentView(INavigation navigation)
{
_navigation = naviation;
SaveCommand = new Command(SaveCommandHandler);
CancelCommand = new Command(CancelCommandHandler)
}
public string TestBinding
{
get {return _testBinding;}
set
{
_testBinding = value;
OnPropertyChanged();
}
}
public Command SaveCommand {get;set;}
public Command CancelCommand {get;set;}
public void SaveCommandHandler()
{
var value = TestBinding
_navigation.PopAsync();
}
public void CancelCommandHandler()
{
_navigation.PopAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Refresh data in TabbedPage using MVVM

I have a TabbedPage where I add entries and in the other tabs I list data. How can I refresh the data after I've added an entry to database using MVVM?
MainPage:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:myMood.Views"
x:Class="myMood.Views.MainPage"
BackgroundImage="logo.png">
<local:Register></local:Register>
<local:Entries></local:Entries>
<local:Statistics></local:Statistics>
<local:Settings></local:Settings>
<local:About></local:About>
</TabbedPage>
Entries:
<?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="myMood.Views.Entries"
Icon="ic_view_headline_white_24dp.png"
xmlns:viewModels="clr-namespace:myMood.ViewModels">
<ContentPage.BindingContext>
<viewModels:EntriesViewModel />
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding MoodEntries}"
HasUnevenRows="True"
Margin="20">
<ListView.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Image Source="ic_date_range_black_24dp.png" Grid.Column="0" Grid.Row="0" HorizontalOptions="Center"></Image>
<Image Source="ic_local_hotel_black_24dp.png" Grid.Column="1" Grid.Row="0" HorizontalOptions="Center"></Image>
<Image Source="ic_directions_run_black_24dp.png" Grid.Column="2" Grid.Row="0" HorizontalOptions="Center"></Image>
<Image Source="ic_done_all_black_24dp.png" Grid.Column="3" Grid.Row="0" HorizontalOptions="Center"></Image>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate, StringFormat='{0:d/M}'}" Grid.Column="0" Grid.Row="0" HorizontalOptions="Center"></Label>
<Label Text="{Binding Sleep, StringFormat='{0:0.0}'}" Grid.Column="1" Grid.Row="0" HorizontalOptions="Center"></Label>
<Label Text="{Binding Stress, StringFormat='{0:0.0}'}" Grid.Column="2" Grid.Row="0" HorizontalOptions="Center"></Label>
<Label Text="{Binding AchivedGoals, StringFormat='{0:0.0}'}" Grid.Column="3" Grid.Row="0" HorizontalOptions="Center"></Label>
<Label Text="{Binding Comment, StringFormat='{0:0.0}'}" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="1"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
ViewModel:
using myMood.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace myMood.ViewModels
{
public class EntriesViewModel
{
public List<MoodEntry> MoodEntries { get; set; }
public EntriesViewModel()
{
MoodEntries = App.MoodDatabase.GetMoodEntries();
}
}
}
Register Viewmodel:
namespace myMood.ViewModels
{
public class RegisterViewModel : INotifyPropertyChanged
{
public MoodEntry MoodEntry { get; set; }
public DateTime LowerLimitDate { get; set; }
public DateTime HighLimitDate { get; set; }
public Command SaveEntry
{
get
{
return new Command(() =>
App.MoodDatabase.SaveMoodEntry(MoodEntry));
}
}
public RegisterViewModel()
{
MoodEntry = App.MoodDatabase.GetMoodEntry(DateTime.Today);
if (MoodEntry == null)
MoodEntry = new MoodEntry();
LowerLimitDate = new DateTime(2018, 1, 1);
HighLimitDate = DateTime.Today;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You need to implement the INotifyPropertyChanged Interface in your viewmodel. Because Currently the viewmodel sends no signal to the UI that the data has changed.
So your viewmodel should look like this:
public class EntriesViewModel : INotifyPropertyChanged
{
private List<MoodEntry> _moodEntries;
public List<MoodEntry> MoodEntries
{
get { return _moodEntries; }
set { _moodEntries = value; OnPropertyChanged(); }
}
public EntriesViewModel()
{
MoodEntries = App.MoodDatabase.GetMoodEntries();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Xamarin.Forms: Creating a SearchBar and search on a ListView

Good Day everyone. I'm currently doing an application that allows the User to CRUD record of an Customer and save it on a database.
All the created records are displayed on a ListView.
What I want to do is to create a SearchBar that allows me to search Customer Records that is inside my ListView. In a separate program, I've tried to create a searchbar but I was only able to search records from a pre-defined ListView.
The searchbar I need to do should allow me to search on a ListView that comes from a database.
Hope you can help me with this.
Here are some of my codes. If you need to see more. Please let me know. Thanks a lot.
CustomerViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using XamarinFormsDemo.Services;
namespace XamarinFormsDemo.ViewModels
{
public class CustomerVM : INotifyPropertyChanged
{
private List<Customer> _customerList;
public List<Customer> CustomerList
{
get { return _customerList; }
set
{
_customerList = value;
OnPropertyChanged();
}
}
public CustomerVM()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var customerServices = new CustomerServices();
CustomerList = await customerServices.GetCustomerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
CustomerPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinFormsDemo.Views.ClientListPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="Client List">
<ContentPage.BindingContext>
<ViewModels:CustomerVM/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<ListView x:Name="CustomerListView"
ItemsSource="{Binding CustomerList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding CUSTOMER_NAME}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding CUSTOMER_CODE}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="2"
Text="{Binding CUSTOMER_CONTACT}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2016 SMESOFT.COM.PH All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
SearchBar has SearchCommand property. Bind it to some your ICommand on view model. Also bind Text property of SearchBar to string property on view model. Keep all records in some common collection _allCustomers and on UI show only ObservableCollection<Customer> Customers. In execution method for this command you could add:
private void SearchCommandExecute()
{
var tempRecords = _allCustomers.Where(c=>c.FullName.Contains(Text));
Customers = new ObservableCollection<Customer>(tempRecords);
}
This is basically an addition to the answer of Egor Gromadskiy, but as there are so many comments on other subjects I post it here.
Keep in mind that when in the search bar X button or cancel button is pressed, search command will not be triggered. So if you want to show all results on this event, call this command from the SearchText property (in this case, SearchText is set to null, at least on iOS, for Android refer to this forum).
The same can be done if you want an as-you-type search and don't wait till Search button is hit.
public string SearchText
{
get { ... }
set {
... = value;
SearchCommand.Execute();
}
}