Last active
June 25, 2020 16:09
-
-
Save kevinmutlow/77cf36ff2b3ddeabddeda5115020bb8e to your computer and use it in GitHub Desktop.
Custom SearchBar for Xamarin.Forms with Filter icon and auto-search on text changed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="App.Core.Controls.ExtSearchBar" | |
x:Name="Root"> | |
<Grid> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="50" /> | |
</Grid.RowDefinitions> | |
<!-- | |
WidthRequest="1" ==> Xamarin bug ==> SearchBar inside a Grid, needs WidthRequest set (workaround) | |
https://bugzilla.xamarin.com/show_bug.cgi?id=59595 | |
--> | |
<SearchBar | |
x:Name="searchBar" | |
WidthRequest="1" | |
Placeholder="{Binding Source={x:Reference Root}, Path=Placeholder}" | |
SearchCommand="{Binding Source={x:Reference Root}, Path=SearchCommand}" | |
Text="{Binding Source={x:Reference Root}, Path=Text}" | |
TextChanged="SearchBar_TextChanged"/> | |
<Image | |
x:Name="iconFilter" | |
Source="ic_equalizer_ver.png" | |
IsVisible="{Binding Source={x:Reference Root}, Path=HasFilterIcon}" | |
VerticalOptions="Fill" | |
HorizontalOptions="End" | |
WidthRequest="25" | |
Aspect="AspectFit" | |
Margin="0,0,20,0"> | |
<Image.GestureRecognizers> | |
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="IconFilter_Tapped" /> | |
</Image.GestureRecognizers> | |
</Image> | |
</Grid> | |
</ContentView> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
namespace App.Core.Controls | |
{ | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class ExtSearchBar : ContentView | |
{ | |
CancellationTokenSource cts = null; | |
public ExtSearchBar() | |
{ | |
InitializeComponent(); | |
} | |
#region Properties | |
private int _textChangedDelay; | |
public int TextChangedDelay | |
{ | |
get { return _textChangedDelay; } | |
set { _textChangedDelay = value; } | |
} | |
#endregion | |
#region Bindable Properties | |
#region HasFilterIcon | |
public static BindableProperty HasFilterIconProperty = | |
BindableProperty.Create( | |
nameof(HasFilterIcon), | |
typeof(bool), | |
typeof(ExtSearchBar), | |
defaultValue: default(bool), | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public bool HasFilterIcon | |
{ | |
get { return (bool)GetValue(HasFilterIconProperty); } | |
set { SetValue(HasFilterIconProperty, value); } | |
} | |
#endregion | |
#region Placeholder | |
public static BindableProperty PlaceholderProperty = | |
BindableProperty.Create( | |
nameof(Placeholder), | |
typeof(string), | |
typeof(ExtSearchBar), | |
defaultValue: default(string), | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public string Placeholder | |
{ | |
get { return (string)GetValue(PlaceholderProperty); } | |
set { SetValue(PlaceholderProperty, value); } | |
} | |
#endregion | |
#region Text | |
public static BindableProperty TextProperty = | |
BindableProperty.Create( | |
nameof(Text), | |
typeof(string), | |
typeof(SearchBar), | |
defaultValue: default(string), | |
defaultBindingMode: BindingMode.TwoWay | |
); | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
#endregion | |
#region TextChangedCommand | |
public static BindableProperty TextChangedCommandProperty = | |
BindableProperty.Create( | |
nameof(TextChangedCommand), | |
typeof(Command), | |
typeof(SearchBar), | |
defaultValue: default(Command), | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public Command TextChangedCommand | |
{ | |
get { return (Command)GetValue(TextChangedCommandProperty); } | |
set { SetValue(TextChangedCommandProperty, value); } | |
} | |
#endregion | |
#region FilterCommand | |
public static BindableProperty FilterCommandProperty = | |
BindableProperty.Create( | |
nameof(FilterCommand), | |
typeof(ICommand), | |
typeof(SearchBar), | |
defaultValue: null, | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public ICommand FilterCommand | |
{ | |
get { return (ICommand)GetValue(FilterCommandProperty); } | |
set { SetValue(FilterCommandProperty, value); } | |
} | |
#endregion | |
#region FilterCommandParameter | |
public static BindableProperty FilterCommandParameterProperty = | |
BindableProperty.Create( | |
nameof(FilterCommandParameter), | |
typeof(object), | |
typeof(SearchBar), | |
defaultValue: null, | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public object FilterCommandParameter | |
{ | |
get { return (object)GetValue(FilterCommandParameterProperty); } | |
set { SetValue(FilterCommandParameterProperty, value); } | |
} | |
#endregion | |
#region SearchCommand | |
public static BindableProperty SearchCommandProperty = | |
BindableProperty.Create( | |
nameof(SearchCommand), | |
typeof(Command), | |
typeof(SearchBar), | |
defaultValue: default(Command), | |
defaultBindingMode: BindingMode.OneWay | |
); | |
public Command SearchCommand | |
{ | |
get { return (Command)GetValue(SearchCommandProperty); } | |
set { SetValue(SearchCommandProperty, value); } | |
} | |
#endregion | |
#endregion | |
#region Methods | |
public async void SearchBar_TextChanged(object sender, TextChangedEventArgs e) | |
{ | |
if (HasFilterIcon) | |
{ | |
var sbar = sender as Xamarin.Forms.SearchBar; | |
if (string.IsNullOrWhiteSpace(sbar?.Text)) | |
{ | |
iconFilter.TranslateTo(0, 0); | |
} | |
else | |
{ | |
int transX = -25; //Good for Android | |
if(Device.RuntimePlatform == Device.iOS) | |
transX = -80; //Good for iOS | |
if (iconFilter.TranslationX != transX) | |
iconFilter.TranslateTo(transX, 0, 100); | |
} | |
} | |
if (TextChangedCommand != null) | |
{ | |
if (cts != null) cts.Cancel(); | |
cts = new CancellationTokenSource(); | |
var ctoken = cts.Token; | |
try | |
{ | |
var millisDelay = TextChangedDelay > 0 ? TextChangedDelay : 650; | |
await Task.Delay(millisDelay, ctoken); | |
if (ctoken.IsCancellationRequested) | |
return; | |
if (TextChangedCommand.CanExecute(null)) | |
TextChangedCommand?.Execute(null); | |
} | |
catch (OperationCanceledException) | |
{ | |
// Expected | |
} | |
} | |
} | |
private void IconFilter_Tapped(object sender, EventArgs e) | |
{ | |
if (FilterCommand?.CanExecute(FilterCommandParameter) ?? false) | |
FilterCommand?.Execute(FilterCommandParameter); | |
} | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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:controls="clr-namespace:App.Core.Controls" | |
x:Class="App.Core.Pages.Other.SearchBarPage" | |
Title="{ Binding PageTitle }"> | |
<StackLayout> | |
<controls:ExtSearchBar | |
Placeholder="Search for an item" | |
Text="{ Binding SearchText }" | |
TextChangedDelay="550" | |
TextChangedCommand="{ Binding SearchCommand }" | |
SearchCommand="{ Binding SearchCommand }" | |
HasFilterIcon="True" | |
FilterCommand="{ Binding FilterTappedCommand }" /> | |
</StackLayout> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using App.Core.ViewModels.Other; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
namespace App.Core.Pages.Other | |
{ | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class SearchBarPage : ContentPage | |
{ | |
public SearchBarPage () | |
{ | |
InitializeComponent (); | |
BindingContext = new SearchBarPageViewModel(this.Navigation); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Diagnostics; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
namespace App.Core.ViewModels.Other | |
{ | |
public class SearchBarPageViewModel : BasePageViewModel | |
{ | |
private readonly INavigation Navigation; | |
public SearchBarPageViewModel(INavigation navigation) | |
{ | |
Navigation = navigation; | |
PageTitle = "Extended SearchBar"; | |
} | |
#region Properties | |
private string _SearchText; | |
public string SearchText | |
{ | |
get { return _SearchText; } | |
set { SetPropertyValue(ref _SearchText, value); } | |
} | |
#endregion | |
#region Commands | |
public ICommand SearchCommand => new Command(SearchAction); | |
public ICommand FilterTappedCommand => new Command(FilterTappedAction); | |
#endregion | |
#region Methods | |
void SearchAction() | |
{ | |
Debug.WriteLine("SearchAction"); | |
} | |
void FilterTappedAction() | |
{ | |
Debug.WriteLine("FilterTappedAction"); | |
} | |
#endregion | |
} | |
} |
necesito que mi searchbar busque x registro utilizando los datos de mysql, ya tengo las listview y se pintan los datos, utilice mvvm
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Excellent gist, just a couple of things I'd improve:
TextChangedDelay
autoproperty with default value, so you don't need the linevar millisDelay = TextChangedDelay > 0 ? TextChangedDelay : 650;
, just:CharactersThreshold
so that the search is done when a minimum number of characters is inputed:Fist, adding the property:
and then in the
SearchBar_TextChanged
instead ofif (TextChangedCommand != null)
, you can put this:HIH