How to get SelectedItem data using MVVM Toolkit library? - c#

I am trying to fire an event or do any action after clicking any of the items in the list:
I am using the library CommunityToolkit.Mvvm but I do not know how to get its data after clicking or selecting any item. For example show an alert and showing its information after clicking.
I have this so far:
<ListView ItemsSource="{Binding Almacenes}" HasUnevenRows="True" SelectedItem="{Binding SelectedItem}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="empresita.svg"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1" Padding="15,0,0,0"
Text="{Binding nombre}"
FontAttributes="Bold"
FontSize="16"/>
<Label Grid.Row="1" Padding="15,0,0,0"
Grid.Column="1"
Text="descripcion"
FontAttributes="Italic"
VerticalOptions="End"
FontSize="16"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
backend:
[ObservableProperty]
List<Almacenes> _almacenes;
[ObservableProperty]
Almacenes _selectedItem;
public SeleccionAlmacenViewModel()
{
Almacenes = new List<Almacenes>()
{
new Almacenes()
{
nombre = "Tomato"
},
new Almacenes()
{
nombre = "Pizza"
},
new Almacenes()
{
nombre = "Romaine"
},
};
}
// aqui se debe cargar la lista
public SeleccionAlmacenViewModel(List<Almacenes> almacenes)
{
Almacenes = new List<Almacenes>();
CargarAlmacenes(almacenes);
}
private void CargarAlmacenes(List<Almacenes> almacenes)
{
Almacenes = almacenes;
}
There is no error but I cannot make this work. How do I get selected item data using this toolkit?
Like I am trying to fire this in a method because I will be coding myself inside the fired method.
EDIT: actually I do not know what I am doing but I tried this and did not work either :(
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GetAlmacenCommand))]
private Almacenes _selectedItem;
[RelayCommand]
public async Task GetAlmacen()
{
await AppShell.Current.DisplayAlert("Error", "TESTING", "OK");
}

You can try to use the CommunityToolkit.Maui package to convert the ListView's ItemSelected event to command.
In the xaml:
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
<ListView ItemsSource="{Binding Almacenes}" HasUnevenRows="True" SelectedItem="{Binding SelectedItem Mode=TwoWay}" >
.....
<ListView.Behaviors>
<toolkit:EventToCommandBehavior
EventName="ItemSelected"
Command="{Binding GetAlmacen}" />
</ListView.Behaviors>
</ListView>
And you can get the SelectedItem data from the _selectedItem;, because you set the SelectedItem="{Binding SelectedItem Mode=TwoWay}".

Related

Listview - Textblock - Button - FolderBrowserDialog - ObservableCollection Binding

I am trying to wrap my head around C#-wpf-MVVM.
I am attempting to put it to use with a simple idea I want to implement
.csv files stores LocationName, Location for default file locations.
.csv is read into a datatable for reference
ObservableCollection is created from datatable
Listview is bound to ObservableCollection
Textblock is bound to SelectedItem.LocationName
Button activates a FileDialogBrowser, the result of which I would like to use to update the location of the SelectedItem.LocationName
I have bound the Listview to the ObservableCollection and bound the Textblock to the SelectedItem.
Where I am running into problems is how do I get the result of the FolderDialogBrowser to update the ObservableCollection SelectedItem.LocationName?
If I change the text manually in the Textblock everything works fine...However, if I use codebehind to update the Textblock...no changes are happening.
private ObservableCollection<FileLocation > _filelocation;
private FileLocation _selectedLocation;
public ObservableCollection<FileLocation > FileLocations
{
get { return _filelocation; }
set
{
_filelocation = value;
OnPropertyChanged("Files");
}
}
public FileLocation SelectedFile
{
get { return _selectedLocation; }
set
{
_selectedLocation = value;
OnPropertyChanged("SelectedFile");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
FileLocations = new ObservableCollection<FileLocation>();
foreach (DataRow row in DtLocations.Rows)
{
string LocationDescription = (string)row.ItemArray[0];
string LocationPath = (string)row.ItemArray[1];
FileLocations.Add(new FileLocation() { LocationName = LocationDescription, Location = LocationPath });
}
<TextBox x:Name="tbxFileLocation" Tag="FileLocation" Grid.Row="1" Grid.Column="4" Width="200" Height="23" Foreground="Black" VerticalContentAlignment="Center" Text ="{Binding SelectedFile.Location ,Mode=TwoWay }" />
<Button x:Name="btnFolderLocator" Content="..." Grid.Row="1" Grid.Column="5" Width="25" Height=" 23" Background="White" HorizontalAlignment="Left" Click="BtnFolderLocator_Click" />
<ListView x:Name="lvwFileLocations" Grid.Row="9" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding FileLocations}" SelectedItem="{Binding SelectedFile }" Margin="0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path= LocationName, Mode=TwoWay}"></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path= Location , Mode=TwoWay}"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Xamarin Forms Collectionview not showing anything on UI even when binding is set and data is added to observable collection

I am quite a beginner so take it easy on me. I have a collection view which is bound to an observable collection. The observable collection receives data and has the items but the Collectionview doesn't display anything at all. Could someone please help me with this. Thanks.
XAML
<CollectionView Grid.Row="1" ItemsSource="{Binding Fav}" x:Name="CVWatchItems" SelectionMode="Single" SelectionChanged="CVWatchItems_SelectionChangedAsync">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="8, 8, 8, 0">
<Frame BorderColor="LightGray" CornerRadius="0" HasShadow="True" Padding="5">
<Grid Padding="0" ColumnSpacing="0" RowSpacing="0" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ffimageloading:CachedImage Source="{Binding SingleimageUrl}" Aspect="AspectFill" WidthRequest="120" HeightRequest="100" Grid.Row="0" Grid.Column="0"/>
<Grid Grid.Row="0" Grid.Column="1" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Padding="0, 0, 0, 3" Text="{Binding Title}" LineBreakMode="TailTruncation" TextColor="Black" FontSize="Medium" Grid.Row="0"/>
<Label Text="{Binding Price, StringFormat='Nu.{0}'}" FontSize="Small" TextColor="Black" Grid.Row="1" HorizontalOptions="StartAndExpand" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding location}" FontSize="Small" TextColor="Gray" Grid.Column="0" HorizontalOptions="StartAndExpand" VerticalOptions="EndAndExpand"/>
<!--Watchlist Icon-->
<Image Source="{Binding Favorite}" Grid.Column="1" Aspect="AspectFill" HeightRequest="28" Margin="2, 0" HorizontalOptions="EndAndExpand" VerticalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
<!--Give ID to each ad and stored data is retrieved through id.-->
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</Grid>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code behind
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
public Watchlist ()
{
InitializeComponent ();
NavigationPage.SetHasNavigationBar(this, false);
NavigationPage.SetHasBackButton(this, false);
CVWatchItems.SetBinding(CollectionView.ItemsSourceProperty, nameof(Fav));
GetFavItems();
}
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
}
Thanks guys.
First your observable collection is static and you never set BindingContext to your page.
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
Remove static keyword from the definition of Fav, change binding in your XAML (`assign a Name to your page) like:
<ContentPage x:Name="Root" ..../>
<CollectionView Grid.Row="1" ItemsSource="{Binding Source={x:Reference Name=Root}, Path=Fav}" ..../>
Also remove binding from your code behind. Last thing, your page has to implement INotifyPropertyChanged interface because you have to tell the page that this collection changed (your collection is filled after your async task is finished) So you have to raise PropertyChanged after your collection is filled.
But maybe in your case is better and easier to set ItemsSource without binding because you are not using MVVM.
If you are in code behind of your page it is not necessary to use binding. You can set collectionview itemsource like:
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
CVWatchItems.ItemsSource = Fav;
}
If you will use MVVM in your application you should use bindings.

xamarin forms - how to get product from listview upon 'delete' button click

A 'delete' button is added to each item on the listview, when the user clicks the delete button it will call RemoveItemFromShoppingList() and remove the product from the shopping cart list.
I have tested calling RemoveItemFromShoppingList() and removing the first item from the shopping list. This all works fine and is the new shopping cart list is updated.
But instead of removing the first item, i want to remove the product the user clicks on. I have tried capturing the ProductId but cant get it...please advise
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding ShoppingListItemSource}" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.Footer>
<Label x:Name="Total" HorizontalTextAlignment="End" VerticalTextAlignment="Start" Margin="20,20" FontAttributes="Bold"/>
</ListView.Footer>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding ProductId}" VerticalOptions="End" IsVisible="False"/>
<controls:CircleImage Grid.Column="1" Grid.Row="1" HeightRequest="60" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Aspect="AspectFill" WidthRequest="66" Grid.RowSpan="2" Source="{Binding Img}"/>
<Label Grid.Column="2" Grid.Row="1" Text="{Binding Name}" VerticalOptions="End"/>
<Label Grid.Column="2" Grid.Row="2" VerticalOptions="Start" Text="{Binding Detail}"/>
<Label Grid.Column="3" Grid.Row="2" VerticalOptions="Start" Text="{Binding TotalPrice, StringFormat='£{0:0.00}'}"/>
<Label Grid.Column="4" Grid.Row="2" VerticalOptions="Start" Text="{Binding QuantityOfProduct}"/>
<Label Grid.Column="5" Grid.Row="2" VerticalOptions="Start" Text="{Binding SubTotal, StringFormat='£{0:0.00}'}"/>
<Button Grid.Column="6" Grid.Row="2" VerticalOptions="Start" Text="Delete" Clicked="RemoveItemFromShoppingList" Grid.RowSpan="2" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
none of these are working can someone please tell me how to get the product id, when delete is clicked? thanks
void RemoveItemFromShoppingList(object sender, EventArgs e)
{
//How can I get Id of product the user wishes to delete
//have tried many things...
// var productToRemove = (Button)sender;
// Product product = new Product();
//var p_Id = int.Parse(productToRemove.ProductId.ToString());
//product.ProductId = productId;
//var product = ((Button)sender).BindingContext;
// var productId = product.id;
//Button button = (Button)sender;
//var imt = (Grid)button.Parent;
//var c = (Label)imt.Children[0];
}
use the BindingContext - this assumes that your ItemsSource is a collection of type Product
void RemoveItemFromShoppingList(object sender, EventArgs e)
{
var button = (Button)sender;
var item = (Product)button.BindingContext;
...
}

How can I manage the visibility of a button in a listview row using mvvm?

I have a listview that has a ViewCell, and inside the view cell I have two buttons,when one button is clicked it hides its visibility and triggers the visibility of the other button, the problem I'm facing is that when I click a button of a row it updates the visibility of buttons in other rows too. How can I update the visibility of only the clicked button? Here is what I have done:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="{Binding Notes}" Grid.Column="0" Grid.Row="0"/>
<Button Visual="Material" Text="Start" TextColor="White"
BackgroundColor="#28a745"
IsVisible="{Binding Source={x:Reference TimesheetDetailsPage},
Path=BindingContext.IsStartVisible}"
Grid.Column="2" Grid.Row="0"
Command="{Binding Source={x:Reference TimesheetDetailsPage},
Path=BindingContext.StartCommand}"
CommandParameter="{Binding}"/>
<Button Visual="Material" Text="Stop" TextColor="White"
BackgroundColor="#dc3545"
IsVisible="{Binding Source={x:Reference TimesheetDetailsPage},
Path=BindingContext.IsStopVisible}"
Grid.Column="2" Grid.Row="0"
Command="{Binding Source={x:Reference TimesheetDetailsPage},
Path=BindingContext.StopCommand}"
CommandParameter="{Binding}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
Here is the code in my viewModel:
public ICommand StartCommand => new Command( async (object item) => {
TimeSheetTrack myItem = item as TimeSheetTrack;
IsStartVisible = false;
IsStopVisible = true;
UserDialogs.Instance.ShowLoading("Updating timer...");
await Start(myItem);
UserDialogs.Instance.HideLoading();
});
public ICommand StopCommand => new Command( async (object item) => {
TimeSheetTrack myItem = item as TimeSheetTrack;
IsStartVisible = true;
IsStopVisible = false;
UserDialogs.Instance.ShowLoading("Stopping timer...");
await Stop(myItem);
UserDialogs.Instance.HideLoading();
});
You have referenced the visibility of all the buttons to the Page using
IsVisible="{Binding Source={x:Reference TimesheetDetailsPage},
Path=BindingContext.IsStartVisible}"
You need to have the visibility set in the Model of the Items, so each of the Items has its own Start and Stop Button Visiblity. So if you do it that way then the code will change to this:-
IsVisible="{Binding IsStartVisible}"
Let me know if you face difficulties.

ComboBox in WPF Datagrid c#

I have a DataGrid and fill it with a DataTable.
dgMitarbeiter.ItemsSource = mainController.loadDataTableMitarbeiter().DefaultView;
this is the function:
public DataTable loadDataTableMitarbeiter()
{
loadMitarbeiterList();
dtMitarbeiter.Clear();
foreach (Mitarbeiter mitarbeiter in mitarbeiterList)
{
drMitarbeiter = dtMitarbeiter.NewRow();
drMitarbeiter["ID"] = mitarbeiter.ID;
drMitarbeiter["Vorname"] = mitarbeiter.vorname;
drMitarbeiter["Nachname"] = mitarbeiter.nachname;
drMitarbeiter["Kostenstelle"] = mitarbeiter.kostenstelle.id;
drMitarbeiter["Größe Hose"] = mitarbeiter.gr_hose;
drMitarbeiter["Größe Oberteil"] = mitarbeiter.gr_oberteil;
drMitarbeiter["Gröse Schuhe"] = mitarbeiter.gr_schuhe;
drMitarbeiter["Ferial"] = mitarbeiter.ferial;
drMitarbeiter["Werk"] = mitarbeiter.werk;
drMitarbeiter["Datum"] = mitarbeiter.creationDate.ToString("dd.MM.yyyy");
dtMitarbeiter.Rows.Add(drMitarbeiter);
}
return dtMitarbeiter;
}
The Xaml:
<DataGrid x:Name="dgMitarbeiter" AlternatingRowBackground="Gainsboro" AlternationCount="2" ColumnWidth="*" HorizontalAlignment="Left" SelectedItem="{Binding SelectedItem}" Margin="10,22,0,0" VerticalAlignment="Top" Height="357" Width="731" CanUserAddRows="False" CanUserDeleteRows="False" RowEditEnding="dgMitarbeiter_RowEditEnding" Background="White" HeadersVisibility="Column"/>
I need a ComboBox for the column "Kostenstelle" but have no idea how to achieve this. Any ideas?
My answer implements an ObservableCollection. And adds this as a databinding to the ComboBox
You need a new class:
public class Kostenstellen: ObservableCollection<Kostenstelle>
{
}
And a fill Method with the following lines of code:
var kostenstellen = new Kostenstellen();
foreach mitarbeiter in mitarbeiterList
{
kostenstellen.Add(mitarbeiter.kostenstelle);
}
var cvsCombobox = new CollectionViewSource() { Source = this.operationList };
this.myCombobox.SetBinding(ItemsControl.ItemsSourceProperty, new Binding() { Source = cvsCombobox });
Now there will be only "(Kostenstelle)" as a string in the Combobox.
So you need to override the ToString() method of your Kostenstelle class
public partial class Kostenstelle
{
public override string ToString()
{
return this.ID.ToString();
}
}
HINT:
Use english variable and class names next time :)
You need to define a DataGridComboBoxColumn in your DataGrid columns, you can then bind the ItemsSource to wherever your combo box's options are located.
See here.
You can do a lot in your xaml file :) For myself I used this one lately...
In the xaml file you can now define the placement of the combobox on your page.
you can set the content by code later.
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Items In Group"
TabIndex="1"
Grid.RowSpan="2"
Padding="120,126,120,50"
ItemsSource="{Binding}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
>
<GridView.ItemTemplate >
<DataTemplate >
<Grid Height="150" Width="480" Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock Text="{Binding title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" FontSize="25"/>
<Line/>
<TextBlock Text="{Binding subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" FontSize="20" Margin="0,10,0,0" />
<Line/>
<TextBlock Text="{Binding description}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" FontSize="15" Margin="0,10,0,0"/>
<Button Tag="{Binding title}" Click="ItemButtonClicked" Content="Details" FontSize="15" Margin="0,10,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="FrameworkElement" >
<Setter Property="Margin" Value="52,0,0,2"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>

Categories

Resources