Binding ViewModel Property with ListView in View in Xamarin - c#

I am just new to Xamarin. I am trying to bind the properties values with the ListView but not getting any success. Tried searching on the internet but it didnt worked. Also the property changed event is null always. This is my ViewModel
namespace DemoApp.ViewModels
{
class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<ShippingDetail> ShippingDetailList { get; set; }
public ObservableCollection<ShippingDetail> ShippingDetails
{
get { return ShippingDetailList; }
set
{
ShippingDetailList = value;
OnPropertyChanged("Changed");
}
}
public async Task GetShippingDataAsync(string TrackID)
{
GenericRestClient<ShippingDetail> client = new GenericRestClient<ShippingDetail>();
ShippingDetails = await client.GetAsyncByID(TrackID);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
var changed = PropertyChanged;
if (changed == null)
return;
changed(this, new PropertyChangedEventArgs(property));
}
}
}
This is my View Xaml Code
<?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="DemoApp.TrackByID"
Title="Mex World Wide"
xmlns:local="clr-DemoApp"
xmlns:ViewModels="clr-DemoApp.ViewModels">
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<ScrollView>
<StackLayout>
<StackLayout Padding="30" Spacing="2" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
<Entry x:Name="TrackIDText" HorizontalTextAlignment="Center" Placeholder="Enter Your Shipment Tracking ID" TextChanged="TrackID_TextChanged"></Entry>
</StackLayout>
<StackLayout Padding="30" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
<Button x:Name="TrackBtn" Text="Track" IsEnabled="False" BackgroundColor="Olive" Clicked="TrackBtn_Clicked"/>
<Button x:Name="ScanBtn" Text="Scan Barcode" IsEnabled="True" BackgroundColor="Azure" Clicked="ScanBtn_Clicked"/>
</StackLayout>
</StackLayout>
</ScrollView>
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
<ListView x:Name="ShippingLV"
RowHeight="60"
ItemsSource="{Binding ShippingDetails}"/>
</StackLayout>
</StackLayout>
<StackLayout x:Name="ActivityIndsL" IsVisible="False" Padding="12"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator x:Name="TrackingActivity" Color ="#FF4081"/>
<Label Text="Please Wait while Details are being fetched..." HorizontalOptions="Center" TextColor="#FF4081"/>
</StackLayout>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
I am trying to bind the ShippingDetails with ListView as its ItemSource, which is called on button click event. Following is the code of XAML View
private async void TrackBtn_Clicked(object sender, EventArgs e)
{
MainViewModel obj = new MainViewModel();
ActivityIndsL.IsVisible = true;
TrackingActivity.IsRunning = true;
TrackingActivity.IsVisible = true;
TrackBtn.IsEnabled = false;
ScanBtn.IsEnabled = false;
await obj.GetShippingDataAsync(TrackIDText.Text);
ActivityIndsL.IsVisible = false;
TrackingActivity.IsRunning = false;
TrackingActivity.IsVisible = false;
TrackBtn.IsEnabled = true;
ScanBtn.IsEnabled = true;
}
Please Correct me where i am doing wrong.
Thanks

A couple of things wrong with your code. First of all, you're calling OnPropertyChanged with a wrong value. It's supposed to be the name of the property that has changed, like this:
public ObservableCollection<ShippingDetail> ShippingDetails
{
get { return ShippingDetailList; }
set
{
ShippingDetailList = value;
OnPropertyChanged("ShippingDetails");
}
}
Also, you already set MainViewModel as your BindingContext in the XAML:
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
There's no need to do it again in the button's clicked event. Instead of creating a new instance every time the button is clicked, I'd reference the already existing view model like this:
private async void TrackBtn_Clicked(object sender, EventArgs e)
{
MainViewModel vm = this.BindingContext as MainViewModel;
await vm.GetShippingDataAsync(TrackIDText.Text);
}
Edit: There's one more thing I'd fix in your code. I'd define ShippingDetailList as a private instance field since ShippingDetails property is used to expose it to the outside world. This won't really affect how your code works but it's closer to a proper C# way.
private ObservableCollection<ShippingDetail> shippingDetailList;
Here's some good reading material about fields, if you're interested.

Related

Why won't my Xamarin.Forms ListView Data Binding reflect in other Views?

My MainPage.xaml page is bound to ClientsViewModel.cs. This page has a ListView bound to an ObservableCollection property.
The NewClient.xaml page and entry fields are also bound to the ClientsViewModel.cs.
When I save a new client using the NewClient.xaml form and navigate back to MainPage.xaml (using the navigation back arrow) I expect to see the newly added client in the MainPage.xaml ListView however I do not see this change.
How come the ListView in MainPage.xaml isn't showing the newly updated record? Where am I going wrong?
It may be worthwhile mentioning that my actual project will be using SQLite, so the ObseravbleCollection will eventually be obtaining records directly from an SQLite database, so any help or advice around this would be greatly appreciated also.
Refer below code, or clone from my GitHub repository https://github.com/minlopalis/XamarinForms-ListView-DataBinding.git
(Model) Client.cs
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
(ViewModel) BaseViewModel.cs
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
(View Model) ClientViewModel.cs
public class ClientViewModel : BaseViewModel
{
private ObservableCollection<Client> clients;
public ObservableCollection<Client> Clients
{
get { return clients; }
set
{
clients = value;
OnPropertyChanged();
}
}
public Command SaveClientCommand { get; }
public ClientViewModel()
{
this.Clients = new ObservableCollection<Client>();
SaveClientCommand = new Command(()=> {
Client client = new Client()
{
Name = Name,
Phone = Phone
};
Clients.Add(client);
OnPropertyChanged(nameof(Clients));
});
}
private int id;
public int Id
{
get { return id; }
set
{
id = value;
OnPropertyChanged();
}
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
private string phone;
public string Phone
{
get { return phone; }
set
{
phone = value;
OnPropertyChanged();
}
}
}
(View) MainPage.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"
xmlns:viewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.MainPage">
<ContentPage.BindingContext>
<viewModels:ClientViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="Client List"></Label>
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Add Client"
Clicked="AddClientButton_Clicked"/>
</StackLayout>
</ContentPage>
(View) NewClient.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"
xmlns:viewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.Views.NewClient">
<ContentPage.BindingContext>
<viewModels:ClientViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Label Text="Add New Client" />
<Label Text="Name"/>
<Entry Text="{Binding Name}"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"/>
<Button Text="Save"
Command="{Binding SaveClientCommand}"/>
<!-- Added ListView -->
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I've downloaded your code from the repo and I think there is one big flaw in it causing this. You're setting your BindingContext in XAML on both pages. If you set a breakpoint in the constructor of the ClientViewModel, you will notice it gets called twice: once when the app boots, once when you click "Add Client".
This means you are looking at two separate instances of this class so your Client is in the wrong instance. You want to make sure that you are looking at the same view model.
Even more so, you might even want to make the separation of concerns even better by creating an extra, i.e.: CreateClientViewModel which is only responsible for creating the client and returning that object to the ClientViewModel which then in its turn adds that to the collection.
Hope this helps!
According to your description, you want to pass data when navigate between pages, I suggest you can use MessagingCenter.
MainPage:
<StackLayout>
<Label Text="Client List" />
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding Phone}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Command="{Binding SaveClientCommand}" Text="Add Client" />
</StackLayout>
public partial class Page9 : ContentPage
{
private ClientViewModel _clientmodel;
public ClientViewModel clientmodel
{
get { return _clientmodel; }
set
{
_clientmodel = value;
}
}
public Page9()
{
InitializeComponent();
this.BindingContext = new ClientViewModel(this.Navigation);
}
}
public class ClientViewModel
{
public ObservableCollection<Client> Clients { get; set; }
public Command SaveClientCommand { get; }
private INavigation _navigation;
public ClientViewModel(INavigation navitation)
{
Clients = new ObservableCollection<Client>();
Clients.Add(new Client() { Name = "client1", Phone = "123" });
_navigation = navitation;
SaveClientCommand = new Command(async() => {
await _navigation.PushAsync(new NewClient());
});
MessagingCenter.Subscribe<string, string[]>("test", "Add", (sender, values) =>
{
Client client = new Client() { Name=values[0],Phone=values[1]};
Clients.Add(client);
});
}
}
NewClient.xaml:
public partial class NewClient : ContentPage
{
public NewClient()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
string name = entry1.Text;
string phone = entry2.Text;
string[] values = { name,phone};
MessagingCenter.Send<string, string[]>("test", "Add", values);
Navigation.PopAsync();
}
}
By the way, you don't need to call PropertyChanged for ObservableCollection, because ObservableCollection Class Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
Thanks for everyone's help, I have solved my issue.
There were two problems with my code.
1. Two ViewModel Instances
As pointed out by Gerald Versluis I had two instances of my ViewModel. I fixed this issue by creating an instance of my view model in Application.Resources in my App.xaml page.
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBinding.App"
xmlns:ClientViewModel="clr-namespace:DataBinding.ViewModels">
<Application.Resources>
<ClientViewModel:ClientViewModel x:Key="ClientViewModel" />
</Application.Resources>
</Application>
And binding each page to the Static Resource (as below)
<?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:ViewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.Views.NewClient">
<ContentPage.BindingContext>
<StaticResource Key="ClientViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Label Text="Add New Client" />
<Label Text="Name"/>
<Entry Text="{Binding Name}"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"/>
<Button Text="Save"
Command="{Binding SaveClientCommand}"/>
<!-- Added ListView -->
<ListView ItemsSource="{Binding ClientList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Thanks Gerald Versluis for your help. Check out his YouTube channel here.
2. Missing ViewCell
My MainPage.xaml was missing a ViewCell in the ListView. This was a simple typing oversight but was throwing a "'Specified cast is not valid" error. Big thanks to Alexander Fauland for his reply to this thread which helped me solve my missing ViewCell problem.

Can not pass binding context through Navigation.pushasync with collectionview in xamarin

I am new to Xamarin and am making a small prototype application. Since this is a pilot project, I am keeping it simple and not using MVVM.
Expected output: I have a collection view and when I select an item from that view, I would like to bind the data from that Item, and navigate to a new page. I want the new page to be binded with the data I selected from the collection view as there will be a couple buttons on that page with different options.
Problem: When the item is selected from the collection view, I use Navigation.pushasync to open a new page. Inside of that routing action, I set the binding context to the data from the selected item. When I navigate to the page, none of the page is populated with data from the binding context I set in the previous page.
Comment: I had this working with a list view. But I wanted more flexibility in my styles for my list, so I am trying a collection view. I am having trouble trying to understand the way to bind data with a collection list vs a listview.
Main Page View:
MainPage.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="Notes.MainPage"
Title="My Numbers">
<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNumberAddedClicked" />
<ToolbarItem Text="Q"
Clicked="GetCount"/>
<ToolbarItem Text="L"
Clicked="GetLast"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<CollectionView x:Name="listView"
SelectionMode="Single"
ItemsSource="{Binding listView}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Orientation="Vertical"
Grid.Column="1">
<Label Text="{Binding MyNumber}" FontSize="Title"/>
<Label Text="{Binding DateAdded}" FontSize="Subtitle"/>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage.Content>
</ContentPage>
MainPage.xaml.cs:
using System;
using Xamarin.Forms;
using Notes.Models;
namespace Notes
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
listView.SelectionChanged += ListView_SelectionChanged;
}
async void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var num = e.CurrentSelection;
if (num != null)
{
await Navigation.PushAsync(new ActionsPage
{
BindingContext = num as NUM
});
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
listView.ItemsSource = await App.Database.GetNumbersAsync();
}
}
}
Action Page View:
The circle is where the label is supposed to be, but the data won't bind.
ActionsPage.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="Notes.ActionsPage"
Title="NUM Actions">
<StackLayout>
<Label Text="{Binding myNumber}"
FontSize="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center" />
<Button Text="Delete NUM"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Clicked="OnDeleteButtonClicked" />
</StackLayout>
</ContentPage>
ActionsPage.xaml.cs:
using Notes.Models;
using System;
using Xamarin.Forms;
namespace Notes
{
public partial class ActionsPage : ContentPage
{
public ActionsPage()
{
InitializeComponent();
}
async void OnDeleteButtonClicked(object sender, EventArgs e)
{
var num = (NUM)BindingContext;
await App.Database.DeleteNumAsync(num);
await Navigation.PopAsync();
}
}
}
Any help is appreciated
The simple way to pass value on navigation is just to pass an argument in the constructor.
MainPage SelectionChanged event:
private async void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var num = e.CurrentSelection[0] as NUM;
if (num != null)
{
await Navigation.PushAsync(new ActionsPage(num));
}
}
ActionPage:
public partial class ActionsPage : ContentPage
{
public int myNumber { get; set; }
public ActionsPage(NUM num)
{
InitializeComponent();
myNumber = num.MyNumber;
this.BindingContext = this;
}
async void OnDeleteButtonClicked(System.Object sender, System.EventArgs e)
{
//var num = (NUM)BindingContext;
//await App.Database.DeleteNumAsync(num);
//await Navigation.PopAsync();
}
}

What is the best way to start a function in a ViewMomdel just by clicking on the item in the ListView

I want to add the data from my selected item in the ListView to a few entries immediately when clicking on the item.
My XAML Code:
<?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:local="clr-namespace:WishListProject.ViewModels"
x:Class="WishListProject.Views.UpdateGames">
<ContentPage.BindingContext>
<local:GameListViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding Games}" SelectedItem="{Binding SelectedGame}" ItemSelected="{Binding InsertGame }">
</ListView>
<Entry Placeholder="ID" IsVisible="False" Text="{Binding IdEntry}"></Entry>
<Entry Placeholder="GameName" Text="{Binding GameNaamEntry}"></Entry>
<Entry Placeholder="GameGenre" Text="{Binding GameGenreEntry}"></Entry>
<Entry Placeholder="GameRelease" Text="{Binding GameReleaseEntry}"></Entry>
<Button Text="Update Game" Command="{Binding UpdateGameCommand}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
And this is the function I want to start for displaying the selected item data in to a few entries USING THE VIEWMODEL:
public void InsertGame(object sender, SelectedItemChangedEventArgs e)
{
Game game = new Game();
game = SelectedGame;
IdEntry.Text = game.Id.ToString();
GameNaamEntry = game.GameNaam;
GameGenreEntry = game.GameGenre;
GameReleaseEntry = game.GameRelease;
}
What is the best way to start a function in a VIEWMODEL just by clicking on the item in the ListView?
You can call your method at the time SelectedItem is set. Since you are using bindings, I assume you have something like this in your ViewModel:
Game _selectedGame;
public Game SelectedGame
{
get { return _selectedGame; }
set {
SetProperty(ref _selectedGame, value);
// code to add in the setter
if (value != null)
InsertGame();
//----------------------------
}
}
Just make sure the method is called after the property is changed.
Then you will also need to simplify your method signature to:
public void InsertGame(){
...
}
And you should be all set.
Happy coding!
ItemSelected is an event and you can't bind it to a method. I will give you a solution about how to start a function in a ViewMomdel just by clicking on the item in the ListView:
In xaml:
<ListView ItemsSource="{Binding Games}" SelectedItem="{Binding SelectedGame}" ItemSelected="ListView_ItemSelected">
And in code behind, get the current ViewModel in the ListView_ItemSelected method and then call InsertGame of ViewModel, you can pass the current selectedGame also:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
Game selectedGame = e.SelectedItem as Game;
var currentVm = this.BindingContext as GameListViewModel;
currentVm.InsertGame(selectedGame);
}
}
And your ViewModel:
public class GameListViewModel : INotifyPropertyChanged
{
public void InsertGame(Game currentGame)
{
GameNaamEntry = currentGame.GameNaam;
}
...
}

Error CS1929 when i try to make a searchbar in a listview can someone check my code for errors?

This is my code, i get error CS1929. i am trying to make a searchbar in a listview. Can someone check my code for fixes and post what code i need to use, or if there is another way to make a searchbar? i neeeeeeed Help!
this is my Xaml.cs code:
namespace App2
{
public partial class MainPage : ContentPage
{
List<Kontakter> kontakter = new List<Kontakter>
{
new Kontakter
{
Fuldenavn = "Anja Birkelund (ANBI)",
Tlfnr = 24212504
},
new Kontakter
{
Fuldenavn = "Morten Jensen (MOJ)",
Tlfnr = 24838149
},
new Kontakter
{
Fuldenavn = "Thomas Duvall Pedersen (TPD)",
Tlfnr = 61706767
},
new Kontakter
{
Fuldenavn = "Svend-Erik Dejbjerg (SD)",
Tlfnr = 20116644
}
};
public MainPage()
{
InitializeComponent();
NameslistView.ItemsSource = kontakter;
}
private void MainSearchBar_SearchButtonPressed(object sender, EventArgs e)
{
var keyword = MainSearchBar.Text;
NameslistView.ItemsSource =
kontakter.Where(name => name.Contains(keyword));
}
}
}
this is my Xmal code:
<?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:local="clr-namespace:App2"
x:Class="App2.MainPage">
<StackLayout>
<SearchBar x:Name="MainSearchBar" SearchButtonPressed="MainSearchBar_SearchButtonPressed" />
<ListView x:Name="NameslistView" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Fuldenavn}" />
<Label Text="{Binding Tlfnr}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
The correct way:
private void MainSearchBar_SearchButtonPressed(object sender, EventArgs e)
{
var keyword = MainSearchBar.Text;
NameslistView.ItemsSource = kontakter.Where(obj =>(obj.Fuldenavn.Contains(keyword) || obj.Tlfnr.ToString().Contains(keyword)));
}
PS:
Modify your code in XMAL:
<StackLayout>
<SearchBar x:Name="MainSearchBar" SearchButtonPressed="MainSearchBar_SearchButtonPressed" />
<ListView x:Name="NameslistView" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Fuldenavn}" />
<Label Text="{Binding Tlfnr}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Otherwise, the two label will overlap.
My test:
Update
If you want to show all the item after click the cancel button , assign the Event TextChanged
<SearchBar x:Name="MainSearchBar" SearchButtonPressed="MainSearchBar_SearchButtonPressed" TextChanged="MainSearchBar_TextChanged"/>
Code behind:
private void MainSearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue == string.Empty)
{
NameslistView.ItemsSource = kontakter.Where(name => (name.Fuldenavn.Contains("")));
}
}
Update2
if you want to search verbatim ,just modify the event TextChanged to observe the e.NewTextValue.
private void MainSearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
NameslistView.ItemsSource = kontakter.Where(obj => (obj.Fuldenavn.Contains(e.NewTextValue)|| obj.Tlfnr.ToString().Contains(e.NewTextValue)));
}
LINQ query must be like
NameslistView.ItemsSource =
kontakter.Where(obj => obj.Fuldenavn.Contains(keyword));

Detecting tap on label, inside ViewCell, inside ListView

I'm trying to handle something similar (from UI perspective), to:
in order to invoke two different business logics for:
tapping at ViewCell element itself (inside ListView) - in example navigate to different page
tapping at Label element (Clickable Label), which is inside given ViewCell element - in example delete given object or smth else
I would like to have whole "tapping" logic inside page ViewModel.
Based on Xamarin forum proposes, I'm able to invoke some logic of "tapping" my delete action from cell, however directly inside my data model - which in my PoV is not good solution, as I would like to manipulate my List collection (so the most preferable way, would be to have this logic at page ViewModel).
What I have right now:
My page View XAML code looks like:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App.Views.TestView">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<ListView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding MyItemsCollection}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<!-- Name Label -->
<Label Text="{Binding Name}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<!-- Delete "Icon" -->
<Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnClickableLabel}" CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
My page View C# code looks like (not specific code there, except binding **BindingContext* to page ViewModel):
public partial class TestView : ContentPage
{
public TestView()
{
InitializeComponent();
BindingContext = ServiceLocator.Current.GetInstance<TestViewModel>();
}
}
My page ViewModel C# code looks like:
public class TestViewModel : ViewModelBase
{
public TestViewModel()
{
MyItemsCollection = GetMyItemsCollection();
}
private List<MyItem> GetMyItemsCollection()
{
return new List<MyItem>
{
new MyItem
{
ID = 1L,
Name = "Item 1 Name"
},
new MyItem
{
ID = 2L,
Name = "Item 2 Name"
},
new MyItem
{
ID = 3L,
Name = "Item 3 Name"
}
};
}
private List<MyItem> _myItemsCollection { get; set; }
public List<MyItem> MyItemsCollection
{
get
{
return _myItemsCollection;
}
set
{
_myItemsCollection = value;
RaisePropertyChanged();
}
}
private MyItem _SelectedItem { get; set; }
public MyItem SelectedItem
{
get
{
return _SelectedItem;
}
set
{
if (_SelectedItem != value)
{
_SelectedItem = value;
RaisePropertyChanged();
Debug.WriteLine("SelectedItem: " + _SelectedItem.Name);
}
}
}
private RelayCommand<object> _OnClickableLabel;
public RelayCommand<object> OnClickableLabel
{
get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
}
private void Test(object currentObject)
{
Debug.WriteLine("This should work... but it's not working :(");
}
}
My data model code looks like:
public class MyItem
{
public long ID { get; set; }
public string Name { get; set; }
private RelayCommand<object> _OnClickableLabel;
public RelayCommand<object> OnClickableLabel
{
get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
}
private void Test(object currentObject)
{
Debug.WriteLine("This works... but it's not good idea, to have it here...");
}
}
Any idea what needs to be changed, in order to invoke OnClickableLabel directly inside my page ViewModel ?
I know, that it's something wrong at:
<TapGestureRecognizer Command="{Binding OnClickableLabel}" CommandParameter="{Binding .}" />
but don't know what :/.
Help! Thanks a lot.
Ok, I found solution, by extending XAML code:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App.Views.TestView" x:Name="Page">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<ListView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding MyItemsCollection}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<!-- Name Label -->
<Label Text="{Binding Name}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<!-- Delete "Icon" -->
<Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.OnClickableLabel, Source={x:Reference Page}}" CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
After that, I got OnClickableLabel command invoked inside my page ViewModel, as expected :).
If someone know "better" solution (better from XAML code point of view), I would like to see it ;).
Thanks a lot everyone!
continuing with what #Namek said i would suggest to get the object of list view item first and then call the command or viewmodel method.
for more you can refer my blog post about interactive Listview at https://adityadeshpandeadi.wordpress.com/2018/07/15/the-more-interactive-listview/
feel free to drop by. :)

Categories

Resources