How to search items in CollectionView - xamarin.forms

I have an ObservableRangeCollection that consists of a few items. How can I conduct a search in the ViewModel of the Page?
Edit:
ObservableCollection in ViewModel:
private ObservableRangeCollection<Smetka> _smetki = new ObservableRangeCollection<Smetka>();
public ObservableRangeCollection<Smetka> Smetki
{
get { return _smetki; }
set { SetProperty(ref _smetki, value); }
}
And this is the CollectionView in the View:
<CollectionView ItemsSource="{Binding Smetki}" BackgroundColor="{DynamicResource SecondaryColor}"
VerticalScrollBarVisibility="Never" HorizontalScrollBarVisibility="Never"
SelectionMode="Single" SelectionChangedCommand="{Binding LaunchDetailPage}"
SelectedItem="{Binding SelectedSmetka}" Grid.Row="1">
</CollectionView>
I have been searching for a VM search example for a while now. I would really appreciate it if someone could provide me with one.
Edit 2:
This is what the app looks like normally.
When Search Criteria is given the ObservableRangleCollection should display only the Smetkas, who meet the Criteria.
After removing the Search Criteria it should display all the Smetkas, who meet the new criteria. If the String is empty or whitespace it should display all of them.

Do you mean you want to retrieve the item from your list ?
If yes,just use LINQ,for example:
public class PLU
{
public int ID { get; set; }
public string name { get; set; }
public double price { get; set; }
public int quantity {get;set;}
}
public static ObservableCollection<PLU> PLUList = new ObservableCollection<PLU>();
retrieve like:
PLU item = PLUList.Where(z => z.ID == 12).FirstOrDefault();
Update:
string filter = "9";//the filter you input
var searchItems = new ObservableCollection<Smetka>(Smetki.Where((smetka) => smetka.Id.Contains(filter))); // Assuming the property is Id.
then you could replace the Smetki with searchItems .

Related

Mapping list of checked InputCheckbox elements to list of string on the form model

I have a list of strings that I'd like to list in a form. Users should be able to check the boxes for the strings they want and I'd like to bind the list values back to the form model.
Let's take a look at the code I've figured out so far:
//mypage.razor
//...
<EditForm Model="MyModel">
#foreach(var opt in AvailableOptions)
{
<label for="option-#opt">#opt</label>
<InputCheckbox id="option-#opt" name="option-#opt" type="checkbox" #bind="#(/* bind to what?!? */)"/>
}
</EditForm>
#code
{
private MyPageModel MyModel = new ();
private List<string> AvailableOptions = new List<string>
{
"Apple",
"Banana",
"Cherry"
}
public class MyPageModel
{
public List<string> SelectedValues { get; set; } = new();
}
}
So this is where I started at. Of course, while I can show the value of the label as part of the loop, it's not clear how I'd bind the boolean value of the Checked property to the form model, especially not when I'm wanting to save the opt string value to the form model's list (for checked values) and not a collection of boolean values.
Looking at an answer to a similar question, the thought is that I'd create a "carrier" class with the name and a boolean like:
public class MyCheckedOption
{
public string Name {get; set;}
public bool IsChecked {get; set;}
}
Now, when I bind in the InputCheckbox, I can now set AvailableOptions to a list of MyPageModel and do #bind="opt.IsChecked", but this now binds to MyPageModel and doesn't bind back to my form model.
Now, in my OnValidSubmit, I could harvest the values of these and populate my form model, but that doesn't seem ideal.
Rather, is there some way to map the boolean of the checked properties (as populated by something in my code behind) to a list of string (wherein the value I want to use is another property of the "carrier" class I'm looping through a list of) that I can store directly on the form model?
You can use Linq's Select method to convert your list to something else-- in the following case, a list of MyCheckableOption objects. Later, when you want to do something with the list, you can do the same in reverse to get back to a List<string>
#foreach (var opt in AvailableOptions)
{
<label>#opt.Name </label>
<input type="checkbox" #bind="opt.IsChecked" />
<br/>
}
<hr />
#foreach (var item in AvailableOptions.Where(option => option.IsChecked))
{
<div>#item.Name has been selected. </div>
}
#code
{
private List<MyCheckableOption> AvailableOptions = new List<string>
{
"Apple",
"Banana",
"Cherry"
}
.Select(option => new MyCheckableOption {Name = option }).ToList();
public class MyCheckableOption
{
public string Name { get; set; }
public bool IsChecked { get; set; }
}
}

Data not displaying in columns in grid UWP

I have a database linked to my grid. When adding data a new checkbox appears, so it is registering that something has been entered, but, the columns are all blank.
Im pretty sure the issue is that I have the bindings set up for the class Person. Could I either still use this class with some additional code, or, find a way to separate the data from the database into columns?
Relevant Code:
Database Class method:
public static List<string> Grab_Entries()
{
List<string> entries = new List<string>();
using (SqliteConnection db = new SqliteConnection("Filename=sqliteSample.db"))
{
db.Open();
SqliteCommand selectCommand = new SqliteCommand("SELECT * from EmployeeTable", db);
SqliteDataReader query;
try
{
query = selectCommand.ExecuteReader();
}
catch (SqliteException)
{
//Handle error
return entries;
}
while (query.Read())
{
entries.Add(query.GetString(0));
}
db.Close();
}
return entries;
MainPage method:
public void EmployeeGrid_Loaded(object sender, RoutedEventArgs e)
{
EmployeeGrid.ItemsSource = DB.Grab_Entries();
}
Main Page XAML:
<controls:DataGrid x:Name="EmployeeGrid" Margin="170,55,35,35"
ItemsSource="{x:Bind persons}"
CanUserSortColumns="True"
AutoGenerateColumns="False" Background="Black" Loaded="EmployeeGrid_Loaded">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn Header="Employee ID"
Binding="{Binding PersonId}"/>
<controls:DataGridTextColumn Header="First Name"
Binding="{Binding FirstName}"/>
<controls:DataGridTextColumn Header="Last Name"
Binding="{Binding LastName}"/>
<controls:DataGridTextColumn Header="Address"
Binding="{Binding Address}"/>
<controls:DataGridTextColumn Header="Position"
Binding="{Binding Position}"/>
<controls:DataGridTextColumn Header="Pay Rate (ph)"
Binding="{Binding PayratePH}"/>
<controls:DataGridTextColumn Header="Sex"
Binding="{Binding Sex}"/>
<controls:DataGridTextColumn Header="TaxCode"
Binding="{Binding TaxCode}"/>
<controls:DataGridTextColumn Header="Email"
Binding="{Binding Email}"/>
<controls:DataGridTextColumn Header="Emergency Contact"
Binding="{Binding EmergencyDetails}"/>
<controls:DataGridCheckBoxColumn Header="Selected"
/>
</controls:DataGrid.Columns>
</controls:DataGrid>
Ill add my Person class too:
public class Person
{
public int PersonId { get; set; }
public int DepartmentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Position { get; set; }
public string Address { get; set; }
public double PayratePH { get; set; }
public string Sex { get; set; }
public string TaxCode { get; set; }
public string EmergencyDetails { get; set; }
public string Email { get; set; }
I can spot two things here. First, the main page XAML binds the items source to persons, which gets overwritten with the return value of DB.Grab_Entries() whenever the control loads.
In EmployeeGrid_Loaded(), you should be populating the collection that the ItemsSource is bound to (persons), instead of assigning a new collection.
The loaded event should look something like this:
public void EmployeeGrid_Loaded(object sender, RoutedEventArgs e)
{
this.persons.Clear();
this.persons.AddRange(DB.Grab_Entries());
}
P.S. make sure that the ItemsSource collection is of type ObservableCollection, because otherwise it will not update the binding when the elements change.
Second, the method DB.Grab_Entries() returns a list of strings, but by the looks of it, the data grid and the persons collection is expecting a list of person objects with certain properties.
When you're reading the data from the sqlite query, you're getting a single column of data at a time (query.GetString(0)). You'll need to construct an object with all column values and put the object into a list.
As #Darius S. mentioned, the ItemsSource of DataGrid you bound with is the lists of Person class, however, the DB.Grab_Entries() method returns the lists of string type, so the DataGrid can't display well. In your Grab_Entries method, you could get the value each property and then convert them into the Person class. After that, add the Person class into entries lists.
In addition, it is recommended to use ObservableCollection class, when you insert or remove data from this class, it will automatically update the UI. So it's better to return ObservableCollection type from your Grab_Entries method directly. I take the FirstName property as an example:
public async static Task<ObservableCollection<Person>> Grab_Entries()
{
ObservableCollection<Person> entries = new ObservableCollection<Person>();
string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path, "sqliteSample.db");
using (SqliteConnection db = new SqliteConnection($"Filename={dbpath}"))
{
db.Open();
SqliteCommand selectCommand = new SqliteCommand("SELECT * from EmployeeTable", db);
using (var reader = await selectCommand.ExecuteReaderAsync())
{
var nameOrdinal = reader.GetOrdinal("First_Name");
//The same method to get other properties
while (await reader.ReadAsync())
{
entries.Add(new Person() { FirstName = reader.GetString(nameOrdinal) });
}
}
db.Close();
}
return entries;
}
MainPage.cs:
ObservableCollection<Person> persons;
public async void EmployeeGrid_Loaded(object sender, RoutedEventArgs e)
{
persons = await Person.Grab_Entries();
EmployeeGrid.ItemsSource = persons;
}

How to save piker's value to Realm db?

i am working with Realm and my example is similar to this one by #BenBishop . I added a Yes / No picker to my PersonPage and im trying to save the selected value of the picker to my db. Could anyone help me with that please? Thanks!
This is not really a Realm question as that sample just uses Realm internally for storage. Most of its code is generic C# Xamarin code.
As a quick summary, you need to
Add a property in AddEditPersonViewModel.cs
Update AddEditPersonViewModel.Init to load the new property
Add a picker mapped to that property, in PersonPage.xaml
Add a matching property in Person.cs to store that value
Update IDBService.SavePerson to allow passing the new property
Update RealmDBService.SavePerson to copy the new property back to Realm
In detail:
// step 1 & 2
public class AddEditPersonViewModel : INotifyPropertyChanged
{
...
// added property
private int superPower;
public int SuperPower {
get {
return superPower;
}
set {
superPower = value;
PropertyChanged(this, new PropertyChangedEventArgs("SuperPower"));
}
}
...
public void Init (string id)
{
...
SuperPower = Model.SuperPower;
// step 3 - in PersonPage.xaml
Text="{Binding LastName}" />
<Picker SelectedIndex="{Binding SuperPower}">
<Picker.Items>
<x:String>Flight</x:String>
<x:String>Super Strength</x:String>
<x:String>Ordinariness</x:String>
</Picker.Items>
</Picker>
</StackLayout>
// step 4 - in Person.cs
public class Person : RealmObject
{
...
public int SuperPower {
get;
set;
}
// step 5 in IDBService.cs
public interface IDBService
{
bool SavePerson (string id, string firstName, string lastName, int SuperPower);
// step 6 in RealmDBService.cs
public class RealmDBService : IDBService
{
...
public bool SavePerson (string id, string firstName, string lastName, int superPower)
{
try {
RealmInstance.Write (() => {
var person = RealmInstance.CreateObject<Person> ();
person.ID = id;
person.FirstName = firstName;
person.LastName = lastName;
person.SuperPower = superPower;
});

xamarin.forms complex DataBinding

i have a problem with DataBinding on Xamarin.Forms and i hope you can help me out.
I have two classes calles angebot (quotation) and adresse (address) looks like this:
public class angebot
{
public string ADRESSE { get; set; }
public string ANGEBOT { get; set; }
// ... and more
}
public class adresse
{
public string ADRESSE { get; set; }
public string NAME { get; set; }
// ...and more
}
These classes are used on another class for data holding:
private IEnumerable<recDataClass> recData = null;
private class recDataClass
{
public Database.Tabellen.angebot angebotData = null;
public Database.Tabellen.adresse adresseData = null;
}
After filled the data classes with data i joined them togeteher in the recData class.
recData =angData.Join (adrData,
angebot => angebot.ADRESSE,
adresse => (adresse.ADRESSE),
(angebot, adresse) => new recDataClass {
angebotData = angebot,
adresseData = adresse
});
Till this step everything is working really fine. recData holds the correct data.
Now i want to set the databinding for my xaml
recDataPage.BindingContext = recData.ElementAt(index);
Now to my question. How do i set the databinding in my xaml correctly to show my data?
<Entry Text="{Binding ANGEBOT}" x:Name="enAngebot" Keyboard="Text" WidthRequest="350" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
Text="{Binding ANGEBOT}"
Text="{Binding recData.angebotData.ANGEBOT}"
Text="{Binding angebotData.ANGEBOT}"
are not working
What i'm doing wrong?
Thx forward
DataBinding only works on property, so you should change the fileds on recDataClass to property like:
private class recDataClass
{
public Database.Tabellen.angebot angebotData {get;set;}
public Database.Tabellen.adresse adresseData {get;set;}
}

Windows Phone 7 - Performance issues with "Filter as you write"-feature

i have some performance problems implementing a feature, where a listbox is realtime-filtered while user is typing the filter-string to a textbox. Feature i'm trying to create is similar to the call history search in WP7.
I created a simple project to test this and copy-pasted the important bits below. Basically i have a TextBox, where user is supposed to write a string that will be used to filter the data bound to a listbox. This filtering should happen in realtime, not after tapping any sort of filter-button etc.
ListBox is bound to a CollectionViewSource which uses a ObservableCollection as a source. When something is entered to the textbox, that value is instantly databound to a property in view model. View model property's setter fires up the filtering of CollectionViewSource, which updates the ListBox's contents.
In the actual project i'm doing, the ListBox can contain a hundred or so items.
Here's the related XAML:
<TextBox TextChanged="TextBox_TextChanged" Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=Explicit}"></TextBox>
<ListBox ItemsSource="{Binding ItemsListCVS.View, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Prop1, Mode=TwoWay}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to trigger instant binding to ViewModel-property:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression(TextBox.TextProperty);
bindingExpr.UpdateSource();
}
ViewModel:
private ObservableCollection<AnItem> _itemsList = new ObservableCollection<AnItem>();
private CollectionViewSource _itemsListCvs = new CollectionViewSource();
public ObservableCollection<AnItem> ItemsList
{
get
{
return _itemsList;
}
set
{
_itemsList = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemsListPropertyName);
}
}
public string FilterString
{
get
{
return _filterString;
}
set
{
if (_filterString == value)
{
return;
}
_filterString = value;
// Update bindings, no broadcast
RaisePropertyChanged(FilterStringPropertyName);
this.Filter();
}
}
public CollectionViewSource ItemsListCVS
{
get
{
return _itemsListCvs;
}
set
{
if (_itemsListCvs == value)
{
return;
}
_itemsListCvs = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemListPropertyName);
}
}
public MainViewModel()
{
var items = Builder<AnItem>.CreateListOfSize(100).Build();
this.ItemsList = new ObservableCollection<AnItem>(items);
this.ItemsListCVS.Source = this.ItemsList;
}
private void Filter()
{
this.ItemsListCVS.View.Filter = r =>
{
if (r == null) return true;
return ((AnItem)r).Prop1.ToString().ToLowerInvariant().Contains(FilterString);
};
}
AnItem-class which is databound to the list:
public class AnItem
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public string Prop4 { get; set; }
public string Prop5 { get; set; }
}
Question:
Everything is working okay, but there is a horrific lag between writing to the TextBox and updating of the ListBox.
Am i simply doing it wrong? If so, then how should i change my approach? I think that this is quite common requirement, so there's probably some nice solution for it.
Rather than rolling your own filter, could you use the AutoCompleteBox from the toolkit?
As an alternative, could you categorise the data and make it searchable via a LongListSelector?
Ultimately, if you've got poorly performing code you should use the Profiler to see where the actual bottleneck is. http://msdn.microsoft.com/en-us/library/hh202934(v=vs.92).aspx

Resources