Xamarin Forms - ListviewSelectedItem MVVM - c#

Im unable to set the selectedItem in a listview control. Ive set the binding context and can debug the code without issue. Onload of the form I can see the get and set method being called but when I select an item in the listview the set is not called. I cannot understand why
private NavigationItems _SelectedNavigationItem;
public NavigationItems SelectedNavigationItem
{
get { return _SelectedNavigationItem; }
set
{
if (value != null)
{
_SelectedNavigationItem = value;
NavigateToPage(value);
}
}
}
public async void NavigateToPage(NavigationItems selectedItem )
{
switch (selectedItem.Id)
{
case "Appointments":
await Navigation.PushAsync(new MyAppointments());
break;
case "Companies":
await Navigation.PushAsync(new Companies());
break;
case "Messages":
await Navigation.PushAsync(new Messages());
break;
default:
break;
}
}
View
<!-- Navigation -->
<ListView Grid.ColumnSpan="2" Margin="20,30,20,10" Grid.Row="1"
ItemsSource="{Binding NavigationCollection}"
SelectedItem="{Binding SelectedNavigationItem}"
HasUnevenRows="false"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Grid Padding="5,5,5,5" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<!--Icon-->
<Image Aspect="AspectFit" Grid.Column="0" HeightRequest="18" WidthRequest="18" Source="{Binding Icon}"/>
<!--Navigation Name-->
<Label Margin="10,0,30,0" Grid.Column="1" Grid.ColumnSpan="2" Style="{StaticResource SecondaryLabelStyle}" Text="{Binding Name}" FontSize="Medium"/>
<!--Navigation Totals-->
<Frame VerticalOptions="Center" Grid.Column="3" Style="{StaticResource CircleFrameStyle}">
<Label TextColor="#d2d2d2" HorizontalTextAlignment="Center" VerticalOptions="Center" Text="{Binding Total}" FontSize="Small">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ForgotPassword}" />
</Label.GestureRecognizers>
</Label>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Everything appears to be in order but something strange is happening. Ive also tried setting mode=TwoWay explicitly in the xaml but I know listview mode is twoway by default.
Kevin

I think doing your setter code this way will solve your problems
private NavigationItems _SelectedNavigationItem;
public NavigationItems SelectedNavigationItem
{
get { return _SelectedNavigationItem; }
set
{
if (_SelectedNavigationItem != value)
{
_SelectedNavigationItem = value;
NavigateToPage(value);
}
}
}

Related

data from firebase to listView in xamarin forms

I have this Task which bring data from firebase :
public async Task<List<Player>> GetPlayersData()
{
var playersData = (await client.Child("Players")
.OnceAsync<Player>())
.Select(f => new Player
{
PlayerName = f.Object.PlayerName,
PlayerTime = f.Object.PlayerTime,
PlayerRank = f.Object.PlayerRank
}).ToList();
return playersData;
}
it worked perfectly , but i want to get the (playersData) to put it as "ItemSource" in "Listview" in XAML, so i call the task in the page constructor :
public RankTable()
{
InitializeComponent();
BindingContext = this;
client = new FirebaseClient("https://numbergame-*****firebaseio.com/");
GetPlayersData();
}
Now it will run the task and brings the players data but I don't know how to control it and put it as ItemSource in the Following XAML code :
<StackLayout>
<CollectionView Grid.ColumnSpan="2" Grid.RowSpan="2" SelectionMode="None" ItemsSource="{Binding ??}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True" BackgroundColor="Beige" CornerRadius="15"
IsClippedToBounds="True" Visual="Material" InputTransparent="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Text="{Binding PlayerRank}" FontSize="15" Grid.Column="1"/>
<Label Text="{Binding PlayerName}" FontSize="15" Grid.Column="2"/>
<Label Text="{Binding PlayerTime}" FontSize="15" Grid.Column="3"/>
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
any help please and sorry for my bad english..
<CollectionView x:Name="Players" ... /
// GetPlayersData is async so you can't call it from the constructor
protected override void OnAppearing()
{
Players.ItemsSource = await GetPlayersData();
}

List view selected item value changed is not reflecting UI on time

In Xamarin forms with MVVM ; I'm using observable collection for listing my items in the listview. It worked fine. In my listview i have a Label and i need to change the Label value when the user click on that selected list item. It's also working fine. But the problem is, Selected list item Label value changed only after i scroll the listview.
I need to change the selected list item's Label value On time.
Here is my code:
Xaml
<ListView x:Name="Listitem" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding States,Mode=TwoWay}" SelectedItem="{Binding YourSelectedItem,Mode=TwoWay}" VerticalOptions="FillAndExpand" HasUnevenRows="true" CachingStrategy="RecycleElement" BackgroundColor="White" >
<ListView.Header>
<Grid BackgroundColor="{StaticResource Accent}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="Country Code" HorizontalOptions="Center" VerticalOptions="Center" TextColor="{StaticResource Primary}" />
<Label Grid.Column="1" Text="Name" HorizontalOptions="Center" TextColor="{StaticResource Primary}" />
<Label Grid.Column="2" Text="Capital" HorizontalOptions="Center" TextColor="{StaticResource Primary}" />
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid VerticalOptions="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding abbr}" Margin="5" HorizontalOptions="Center" VerticalOptions="Center" FontSize="18" TextColor="{StaticResource Primary}" />
<Label Grid.Column="1" Text="{Binding name}" Margin="5" HorizontalTextAlignment="Start" VerticalOptions="Center" FontSize="18" LineBreakMode="WordWrap" TextColor="{StaticResource Primary}"/>
<Label Grid.Column="2" Text="{Binding capital}" Margin="5" HorizontalTextAlignment="Start" VerticalOptions="Center" FontSize="18" LineBreakMode="WordWrap" TextColor="{StaticResource Primary}"/>
<controls:CheckBox Grid.Column="3" Margin="0, 0, 0, 0" CheckedChanged="Handle_CheckedChanged" BindingContext="{Binding .}" BackgroundColor="Transparent" WidthRequest="40" HeightRequest="30" IsVisible="True" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And this is my View model for selected list item;
public TodoItem YourSelectedItem
{
get
{
return _yourSelectedItem;
}
set
{
_yourSelectedItem = value;
_yourSelectedItem.abbr = "Tapped";
OnPropertyChanged("YourSelectedItem");
}
}
Item source property;
private ObservableCollection<TodoItem> _States;
public ObservableCollection<TodoItem> States
{
get { return _States; }
set { _States = value; OnPropertyChanged("States"); }
}
The class TodoItem needs to implement the interface INotifyPropertyChanged and the property abbr needs to be implemented as something like blow:
public string abbr {
get
{
return _abbr;
}
set
{
if (_abbr != value) {
_abbr = value;
OnPropertyChanged("abbr");
}
}
}

Images disapearing in ListView when deleting an Item

I am currently working on a Xamarin CrossPlatform project and have implemented a Listview bound to an ObservableCollection. Everything works out fine until I remove an Item from the ListView. The images in the follow up items within the ListView disappear randomly - not all of them and a different amount of them every time. I guess it has something to do with the MemoryStream, but what do I have to change? Here´s the relevant part of my Model that is bound to the ListView:
public string ImageBase64
{
get
{
return imagebase64;
}
set
{
if (imagebase64 != value)
{
imagebase64 = value;
OnPropertyChanged(nameof(ImageBase64));
OnPropertyChanged(nameof(ImageSource));
}
}
}
public ImageSource ImageSource
{
get
{
if (!string.IsNullOrEmpty(imagebase64))
{
return ImageSource.FromStream(() => new MemoryStream(Convert.FromBase64String(imagebase64)));
}
else
{
return null;
}
}
}
Here´s the relevant XAML:
<ListView x:Name="listView" Margin="20" ItemsSource="{Binding}" ItemSelected="OnListItemSelected" HasUnevenRows="True" SeparatorColor="{StaticResource primaryGreen}" SeparatorVisibility="Default">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Margin="0,5,0,5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="45" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.RowSpan="3" Margin="-2,-2,-2,-2" Source="{Binding ImageSource}" HorizontalOptions="Start" VerticalOptions="Center" Aspect="AspectFill"/> <!-- This is the displayed Image -->
<Label Margin="10,0,0,0" Grid.Column="1" Grid.Row="0" FontAttributes="Bold" FontSize="18" TextColor="{StaticResource primaryGreen}" Text="{Binding VorNachname}" VerticalTextAlignment="Start" HorizontalTextAlignment="Start"/>
<Label Margin="10,0,0,0" Grid.Column="1" Grid.Row="1" Text="{Binding MediumSelected.Wert, StringFormat='via {0}'}" HorizontalOptions="FillAndExpand" VerticalTextAlignment="Start" HorizontalTextAlignment="Start"/>
<StackLayout Margin="10,0,0,0" Grid.Column="1" Grid.Row="2" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="{Binding Alter,StringFormat='Alter: {0}'}" VerticalTextAlignment="Start" HorizontalTextAlignment="Start" HorizontalOptions="Start"/>
</StackLayout>
<StackLayout Margin="0,0,0,-5" Grid.Column="2" Grid.RowSpan="3" Orientation="Vertical" HorizontalOptions="End" VerticalOptions="End">
<Button WidthRequest="40" HeightRequest="40" BackgroundColor="White" BorderWidth="0" BorderColor="White" Image="socialmedia_18.png" Clicked="OnChangeClicked" CommandParameter ="{Binding}" VerticalOptions="EndAndExpand" />
<Button Margin="0,-15,0,0" WidthRequest="40" HeightRequest="40" BackgroundColor="White" BorderColor="White" Image="cancel_18.png" Clicked="OnDeleteClicked" CommandParameter ="{Binding}" VerticalOptions="End" />
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...and the Code behind:
async void OnDeleteClicked(object sender, EventArgs e)
{
Helper.TrackEvent("PeopleList_OnDeleteClicked");
//Get selected Person
Person person = (Person)((Button)sender).CommandParameter;
//Remove from Model
DBHelper.DBModel.People.Remove(person);
//Update database
App.Database.UpdateWithChildren(DBHelper.DBModel);
}
EDIT:
Resizing the images does not help, same problem. I tried it by binding a testvariable ImageSourceThumb to the ListViewItemImage:
public ImageSource ImageSourceThumb
{
get
{
if (!string.IsNullOrEmpty(imagebase64))
{
return ImageSource.FromStream(() => new MemoryStream(ImageResizer.ResizeImage(Convert.FromBase64String(imagebase64), 64, 64)));
}
else
{
return null;
}
}
}
I had a similar problem. When I loaded or updated my listview not all the images showed up.
I fixed my problem resizing the images. Huge images gived me a outofmemory exception. Resizing those images to a smaller resolution fixed these problems.

SilverLight Get control Value inside Combo Box

I am working on silverlight and I am new in it, I have a combox box,inside which I have a checkbox and textbox. I want to get the value of these controls on button click, How can I do this in SilverLight?
This is my ComboBox
<ComboBox x:Name="Type" VerticalAlignment="Top" Margin="2,8,-2,0" Grid.ColumnSpan="3" Height="28" Padding="3">
<ComboBoxItem>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*" MinWidth="105" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="true" VerticalAlignment="Center" Grid.Column="0"/>
<TextBlock Text="All" VerticalAlignment="Center" Grid.Column="1" Style="{x:Null}" FontSize="11"/>
</Grid>
</ComboBoxItem>
<ComboBoxItem>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*" MinWidth="105" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="true" VerticalAlignment="Center" Grid.Column="0"/>
<TextBlock Text="General" VerticalAlignment="Center" Grid.Column="1" Style="{x:Null}" FontSize="11"/>
<TextBox Text="25" VerticalAlignment="Center" Grid.Column="2" FontSize="11" Padding="2" HorizontalContentAlignment="Right"/>
</Grid>
</ComboBoxItem>
</ComboBox>
User have option to select multiple values
I am using MVVM pattern
Since all of your ComboBox Items are unchanging, you can give Names to those items and reference them directly.
<ComboBoxItem x:Name="ComboItemAll">
...
</ComboBoxItem>
<ComboBoxItem x:Name="ComboItemGeneral">
...
</ComboBoxItem>
and the in the Button Click event:
if (ComboItemAll.IsSelected)
{
...
}
else if (ComboItemGeneral.IsSelected)
{
...
}
alternatively, you could also get this information from your ComboBox "Type":
var selectedItem = Type.SelectedItem;
if (selectedItem.Name.StartsWith("ComboItemAll"))
{
...
}
else if (selectedItem.Name.StartsWith("ComboItemGeneral"))
{
...
}
For MVVM (Edit):
Xaml (View):
<ComboBox SelectedItem="{Binding SelectedCustomItem, Mode=TwoWay}" ItemsSource="{CustomItems}" ItemTemplate={StaticResource CustomItemsTemplate} />
Xaml (Resource):
<DataTemplate x:Key="CustomItemsTemplate">
<StackPanel
<CheckBox IsChecked="{Binding IsSelected}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding CustomPropertyFromCustomClass}"/>
</StackPanel>
</DataTemplate>
VM:
public class ViewModel
{
public ViewModel()
{
CustomItems = new ObservableCollection<CustomClass>();
CustomItems.Add(new CustomClass() { "All" });
CustomItems.Add(new CustomClass() { "General" });
}
private ObservableCollection<CustomClass> customItems = null;
public ObservableCollection<CustomClass> CustomItems
{
get { return customItems; }
set
{
if (object.Equals(value, customItems) == false)
{
customItems = value;
OnPropertyChanged(() => CustomItems);
}
}
}
private CustomClass selectedCustomItem = null;
public CustomClass SelectedCustomItem
{
get { return selectedCustomItem; }
set
{
if (object.Equals(value, selectedCustomItem) == false)
{
selectedCustomItem= value;
OnPropertyChanged(() => SelectedCustomItem);
}
}
}
}
You should never reference your combobox directly from the ViewModel. Everything in your ViewModel should be related to data manipulation and know nothing of the View (aka ComboBox).
Everything else should be done in the view. If you need to access the ComboBox, you need you ask yourself why, and can I do that logic in the XAML through templates and bindings?

Using A checkbox inside a ListBox

I have this List box:
Here is the Code for my ListBox:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="List" Grid.Row="0" ItemsSource="{Binding ShoppingItems}" FontSize="42" FontWeight="Light" FontFamily="Segoe WP Light" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="0,-15,0,22">
<Checkbox x:Name="MyCheckBox" IsChecked="{Binding IsChecked,Mode=TwoWay}"/>
</Grid>
<Grid x:Name="MyGrid" Margin="0,-15,0,22" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding DisplayingItem}" FontSize="46" Grid.Column="0" Grid.Row="0" TextWrapping="Wrap" />
<TextBlock Text="quantity" FontSize="32" Grid.Row="1" Margin="6,0,0,0" Grid.Column="0" TextWrapping="Wrap"/>
<TextBlock Text="{Binding DisplayingQuantity}" FontSize="32" Grid.Column="1" Grid.Row="1" Margin="32,0,12,12" TextWrapping="Wrap"/>
<TextBlock Grid.Column="2" Grid.Row="1" FontSize="32" Margin="32,0,12,12" Text="{Binding DisplayingPackaging}" TextWrapping="Wrap"/>
<TextBlock Text="price" Margin="6,0,0,0" FontSize="32" Grid.Row="2" Grid.Column="0" TextWrapping="Wrap"/>
<TextBlock Text="$" FontSize="32" Grid.Row="2" Grid.Column="1" Margin="32,0,12,12" TextWrapping="Wrap"/>
<TextBlock Grid.Column="3" FontSize="32" Grid.Row="2" Margin="32,0,12,12" Text="{Binding DisplayingPrice}" TextWrapping="Wrap"/>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
it has a checkbox inside, the ListBox is populated by data from a SQLite db. I want when I check the Checkbox to receive a message showing me the name of the SelectedItem of the ListBox.
Here is the Property for my CheckBox
private bool isChecked;
public bool IsChecked
{
get
{
if(this.SelectedItem != null)
{
return this.selectedItem.IsChecked;
}
return false;
}
set
{
this.isChecked = value;
}
}
When I check my Checkbox and Click This Button:
private void CheckedItem(object p)
{
if (this.IsChecked != false)
{
MessageBox.Show("Checked");
}
}
Nothing Happens.
I have included my Checkbox in my SelectedItem Like this:
private ShowingModel selectedItem;
public ShowingModel SelectedItem
{
get
{
return selectedItem;
}
set
{
if(this.selectedItem != value)
{
this.selectedItem = value;
if (this.selectedItem != null)
{
this.product = selectedItem.DisplayingItem;
this.price = selectedItem.DisplayingPrice;
this.qty = selectedItem.DisplayingQuantity;
this.package = selectedItem.DisplayingPackaging;
this.isChecked = selectedItem.IsChecked;
}
RaisePropertyChanged("MyPackage");
RaisePropertyChanged("MyPrice");
RaisePropertyChanged("MyProduct");
RaisePropertyChanged("MyQty");
RaisePropertyChanged("IsChecked");
}
}
}
So Where is the Problem.
Not an answer but too much for a comment
This is just messed up
You need to read up on data binding and start over
The following get and set don't refer to the same value
private bool isChecked;
public bool IsChecked
{
get
{
if(this.SelectedItem != null)
{
return this.selectedItem.IsChecked;
}
return false;
}
set
{
this.isChecked = value;
}
}
No indication SelectedItem is bound to anything
On CheckedItem that is not the signature of an event handler
Like #Blam said your immediate problem is resolveable by actually setting an event handler for the CheckBox "MyCheckBox."
<CheckBox x:Name="MyCheckBox" IsChecked="{Binding IsChecked,Mode=TwoWay}"
Checked="MyCheckBox_Checked"/>
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
MessageBox.Show("Checked");
}
2 other things:
If you keep the property public ShowingModel SelectedItem, then I would move the RaisePropertyChanged inside of the if statement. Better yet, make each of the variables you're setting their own property like:
double MyPrice
{
get {return price;}
set { if(price >= 0.0)/* Don't want a negative price. \m/*/
{ price = value; RaisePropertyChanged("MyPrice"); } }
}
If you have it this way for deleting an item, then I would suggest making that clearer for future reference.
I would highly suggest you take a look at few other resources:
Databinding a listbox to an ObservableCollection in code. <-- The question was correct, and has the best way to do this.
Databinding a listbox to an ObservableCollection in XAML.
WPF Tutorial | DataTemplates.
Making the list item check the checkbox.

Categories