I did something like this in my Xamarin.Forms project, which was 100% shared by Android and iOS - no custom renderers. This example uses the MVVM pattern with some use of the code-behind. My view model contains an IList of object which the ListView binds to. It is updated when the user presses three or more characters with a query expression.
private IList<myObject> results;
public IList<myObject> QueryResults
{
get
{
return results;
}
set
{
SetProperty(ref results, value);
}
}
Within the OnTextChanged handler I run the query expression and populate QueryResults on every key stroke. This might not be that efficient, but it performs well.
So the view mark up and code-behind is something like this:
<StackLayout>
<Label Style="{StaticResource formLabel}" Text="My Searchable List" />
<!-- from poc -->
<Grid WidthRequest="550">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="AUTO" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
</Grid.ColumnDefinitions>
<Frame Grid.Row="0" Style="{StaticResource InputFrame}" HeightRequest="49">
<Entry x:Name="SearchText" Style="{StaticResource formLabel}" WidthRequest="550" HeightRequest="45" Text="{Binding SearchString, Mode=TwoWay}" TextChanged="Handle_TextChanged" />
</Frame>
<StackLayout Grid.Row="1" Orientation="Horizontal">
<Frame x:Name="MyObjectFrame" Style="{StaticResource InputFrame}" HeightRequest="{Binding ListHeight}" HasShadow="true">
<ListView x:Name="MyObjectList" ItemsSource="{Binding ResultantMyObjects}" ItemSelected="OnItemSelected" HeightRequest="{Binding ListHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
</StackLayout>
with the Handle_TextChanged in the code-behind:
void Handle_TextChanged(object sender, Xamarin.Forms.TextChangedEventArgs e)
{
if (e.NewTextValue.Length > 2)
{
(BindingContext as SampleDetailsViewModel).MySIteSearchResults(e.NewTextValue);
this.ForceLayout();// force the change in heightrequest for ListView
MyObjectList.IsVisible = true;
MyObjectFrame.IsVisible = true;
}
else
{
MyObjectList.IsVisible = false;
MyObjectFrame.IsVisible = false;
}
}
In the view model, I update the property above, note the return value really isn't used in this not-yet-perfect example:
public List<MyObjectDTO> MyObjectSearchResults(string keystrokes)
{
//TODO: encapsulate in a View
List<MyObjectDTO> searchResults = null;
IEnumerable<MyObjectDTO> queryResults;
queryResults = from site in MyObjects
where site.Name.ToLower().Contains(keystrokes.ToLower())
select site;
if (string.IsNullOrEmpty(SearchString))
{
QueryResults = MyObjects;
}
else
{
QueryResults = queryResults.ToList<MyObjectDTO>();
ListHeight = QueryResults.Count * 45; //TODO: detect size. magic number of 45 limits the height.
}
return searchResults;
}
NOTE: There might be some errors, as I had to scrub some code.