Is it possible to open Expander from ViewModel? - c#

I have an Expander in my View that every time I touch it it opens and displays data. Is it possible to open the Expander or close it from ViewModel? What I want to do is that the Expander can open or close it by pressing a Button.
MainPage.xaml:
<StackLayout>
<StackLayout>
<CollectionView>
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView>
<Frame>
<StackLayout>
<Expander x:Name="expander"></Expander>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
<StackLayout>
<ImageButton Source="img1" Command="{Binding xxxCommand}" CommandParameter="{Binding Source={x:Reference expander}, Path=.}"/>
</StackLayout>
</StackLayout>
ViewModel:
xxxCommand = new Command((sender)=> {
var exp = sender as Expander;
exp.IsExpanded = !exp.IsExpanded;
//...
});
When I open the app, I get this exception:
Xamarin.Forms.Xaml.XamlParseException: Can not find the object
referenced by expander.

You could pass the Expander as parameter to the Command of the button .
Set the name of Expander
<Expander x:Name="exp">
<Button Text="XXX" Command="{Binding xxxCommand}" CommandParameter="{Binding Source={x:Reference exp}, Path=.}" />
In ViewModel
xxxCommand = new Command((sender)=> {
var exp = sender as Expander;
exp.IsExpanded = !exp.IsExpanded;
//...
});
Update
In your case you could use Data binding
<StackLayout>
<Expander IsExpanded="{Binding IsExpanded}"></Expander>
</StackLayout>
<ImageButton Source="img1" Command="{Binding xxxCommand}"/>
Add a new property in your model
public class YourModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
public bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
isExpanded= value;
OnPropertyChanged("IsExpanded");
}
}
//...other properties
}
In ViewModel
//list here is the ItemsSource of your listview
ObservableCollection<YourModel> list = new ObservableCollection<YourModel>();
//list.Add();
//list.Add();
//list.Add();
//list.Add();
Command xxxCommand = new Command(()=> {
//if you want to open the first Expander
list[0].IsExpanded = true;
});

Related

Xamarin ListView not updating with MVVM design pattern

I'm trying to add items to a listview from an Entry that is in a frame of its own though the listview won't update. The entry for reference, is added to the UI using the code behind. Here's the listview:
<ListView ItemsSource="{Binding TodoListItems}" x:Name="todoList" HeightRequest="400">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<FlexLayout JustifyContent="SpaceBetween" Padding="20,0">
<ContentView>
<FlexLayout AlignItems="Center" >
<input:CheckBox IsChecked="{Binding Complete}"
CheckChangedCommand="{Binding Path=BindingContext.CompleteTodoCommand, Source={x:Reference todoList}}"
CommandParameter="{Binding .}"
/>
<Label Text="{Binding TodoText}" Padding="10,0,0,0" FontSize="Large"/>
</FlexLayout>
</ContentView>
<ImageButton Source="trash_icon.png"
Command="{Binding Path=BindingContext.RemoveTodoCommand, Source={x:Reference todoList}}"
CommandParameter="{Binding .}"
Scale="1.2" BackgroundColor="White"
/>
</FlexLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The frame:
<Frame x:Name="bottomSheet" HasShadow="true" CornerRadius="8" Padding="1,4,1,0" BackgroundColor="DarkSlateGray"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=.85,Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
>
<StackLayout x:Name="bottomSheetStackLayout">
<Button Text="Add new Todo" x:Name="addNewTodoButton" Clicked="AddNewTodoButton_Clicked"/>
</StackLayout>
</Frame>
The ViewModel:
public ObservableCollection<TodoItem> TodoListItems { get; set; }
public ObservableCollection<TodoItem> CompletedTodoItems { get; set; }
public TodoListViewModel()
{
TodoListItems = new ObservableCollection<TodoItem>();
TodoListItems.Add(new TodoItem("Walk the duggo", false));
TodoListItems.Add(new TodoItem("Do the washing", false));
TodoListItems.Add(new TodoItem("Brush off Cheeto dust", false));
CompletedTodoItems = new ObservableCollection<TodoItem>();
//CompletedTodoItems.Add(new TodoItem("Do the dishes",true));
}
public ICommand AddTodoCommand => new Command(AddTodoItem);
public string NewTodoInputValue { get; set; }
void AddTodoItem()
{
TodoListItems.Add(new TodoItem(NewTodoInputValue, false));
}
And the code behind:
private void AddNewTodoButton_Clicked(object sender, System.EventArgs e)
{
Entry addTodoEntryField = new Entry();
bottomSheetStackLayout.Children.Remove(addNewTodoButton);
bottomSheetStackLayout.Children.Add(addTodoEntryField);
addTodoEntryField.Completed += AddTodoEntryField_Completed;
}
private void AddTodoEntryField_Completed(object sender, EventArgs e)
{
Entry addTodoEntryField = (Entry)sender;
Console.WriteLine("The text in the entry field: "+addTodoEntryField.Text);
addTodoEntryField.BindingContext = ViewModel;
ShowTodoItemsText(ViewModel.TodoListItems);
addTodoEntryField.SetBinding(Entry.TextProperty, "NewTodoInputValue");
Console.WriteLine(value: ViewModel.NewTodoInputValue==null);
ViewModel.AddTodoCommand.Execute(null);
Console.WriteLine(ViewModel.TodoListItems.Count);
//Console.WriteLine(ViewModel.TodoListItems.Last().TodoText==null);
ViewModel.TodoListItems.Add(new TodoItem("Added manually", false));
}
Edit: To clarify, I have a frame with a button, which when clicked is replaced with an Entry, the completed text of which, I want to bind to the TodoListItems observable collection, which is displayed by the listView.
I've checked that there are objects being put into the observable collection. I'm not implementing INotifyPropertyChanged on the ViewModel yet though as the collection has that. (Also, the Entry at the top of the page does work fine.) Any help appreciated.

How to delete item from CollectionView with SwipeView in Xamarin Forms?

I am currently making a note-taking app. I am new so excuse my lack of knowledge.
I am using the default flyout menu template given with changes made to cater for my needs. I am using a SwipeView in my CollectionView so when you swipe on a 'note' the item will delete on execute.
I have the swipe working but I cannot get the item to delete once swiped.
This is my ItemsPage.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="xamarinMobileTest.Views.ItemsPage"
Title="{Binding Title}"
xmlns:local="clr-namespace:xamarinMobileTest.ViewModels"
xmlns:model="clr-namespace:xamarinMobileTest.Models"
x:Name="BrowseItemsPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add Note" Command="{Binding AddItemCommand}" />
</ContentPage.ToolbarItems>
<ContentPage.BindingContext>
<local:ItemsViewModel/>
</ContentPage.BindingContext>
<RefreshView x:DataType="local:ItemsViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<CollectionView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<SwipeView>
<SwipeView.RightItems>
<SwipeItems Mode="Execute">
<SwipeItem Text="Delete"
BackgroundColor="Red"
Command="{Binding DeleteItemCommand}"
CommandParameter="{Binding .}"/>
</SwipeItems>
</SwipeView.RightItems>
<StackLayout Padding="10" x:DataType="model:Item">
<Label Text="{Binding Text}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16"
FontAttributes="Bold"
TextColor="Black"/>
<Label Text="{Binding Description}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13"
TextColor="Black"/>
<Label Text="{Binding DueDate}"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13"
TextColor="Black"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemsViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</ContentPage>
ItemsViewModel:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using xamarinMobileTest.Models;
using xamarinMobileTest.Views;
namespace xamarinMobileTest.ViewModels
{
public class ItemsViewModel : BaseViewModel
{
private Item _selectedItem;
public ObservableCollection<Item> Items { get; }
public Command LoadItemsCommand { get; }
public Command AddItemCommand { get; }
public Command<Item> ItemTapped { get; }
public Command<Item> DeleteItemCommand { get; }
public ItemsViewModel()
{
Title = "Notes";
Items = new ObservableCollection<Item>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
ItemTapped = new Command<Item>(OnItemSelected);
AddItemCommand = new Command(OnAddItem);
DeleteItemCommand = new Command<Item>(OnDeleteItem);
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Items.Clear();
var items = await DataStore.GetItemsAsync(true);
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
SelectedItem = null;
}
public Item SelectedItem
{
get => _selectedItem;
set
{
SetProperty(ref _selectedItem, value);
OnItemSelected(value);
}
}
private async void OnAddItem(object obj)
{
await Shell.Current.GoToAsync(nameof(NewItemPage));
}
async void OnItemSelected(Item item)
{
if (item == null)
return;
// This will push the ItemDetailPage onto the navigation stack
await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
}
private void OnDeleteItem(Item item)
{
Items.Remove(item);
}
}
}
*Other code needed can be found by opening up default flyout menu template
How do I delete an Item on a swipe in the observable collection so that when I delete the Item it automatically is seen that the item is deleted?
Items.Remove(item); does not seem to work (no error, just does not remove the item from the CollectionView) Why is this?
await DataStore.DeleteItemAsync(item); has the same issue of nothing happening when the item is swiped.
Any help is appreciated.
Change your parameter to this
CommandParameter="{Binding Source={RelativeSource Self}, Path=BindingContext}"
the code you have in OnDeleteItem is nonsensical. It should look something like this
private void OnDeleteItem(Item item)
{
Items.Remove(item);
}
Trying same thing with Tabbed page from default project.
Noticed that Command is not running at all not matter what I do. (using Debug)
Try putting delete button in ItemDetailPage.
In ItemDetailPageViewModel put this code.
public Command DeleteCommand { get; set; }
public ItemDetailViewModel()
{
DeleteCommand = new Command(OnDelete);
}
private async void OnDelete()
{
await DataStore.DeleteItemAsync(Id);
await Shell.Current.GoToAsync("..");
}
with ItemDetailPageViewModel you have the Id to delete
ItemDetailPage code here
<?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:XamarinDeleteItem.ViewModels"
x:Class="XamarinDeleteItem.Views.ItemDetailPage"
Title="{Binding Title}">
<ContentPage.BindingContext>
<local:ItemDetailViewModel />
</ContentPage.BindingContext>
<StackLayout Spacing="20" Padding="15">
<Label Text="Text:" FontSize="Medium" />
<Label Text="{Binding Text}" FontSize="Small"/>
<Label Text="Description:" FontSize="Medium" />
<Label Text="{Binding Description}" FontSize="Small"/>
<Button Text="Delete" Command="{Binding DeleteCommand}" />
</StackLayout>
Is SwipeView even supported by Command?
Goto page 221 in this PDF
found this in Xamarin.Forms Documentation. download here
https://opdhsblobprod03.blob.core.windows.net/contents/332e36c8b2484d748610a3dd6b6e98d6/190868cccec033e90406d3cafdb40d3d?skoid=29100048-1fa1-4ada-b0e0-e2aa294fc66a&sktid=975f013f-7f24-47e8-a7d3-abc4752bf346&skt=2022-09-18T13%3A52%3A55Z&ske=2022-09-25T13%3A57%3A55Z&sks=b&skv=2020-10-02&sv=2020-08-04&se=2022-09-20T23%3A36%3A35Z&sr=b&sp=r&sig=0myHYJIb3aEzMUIuCtmXn4VN%2F0MC8gzG1k7%2BQCMZVno%3D
To allow ViewModels to be moreindependent of particular user interface objects but still allow methods to be
called within the ViewModel,a command interfaceexists.This command interface is supported by the following
elements in Xamarin.Forms:
Button
MenuItem
ToolbarItem
SearchBar
TextCell (and hencealso ImageCell )
ListView
TapGestureRecognizer
With theexception of the SearchBar and ListView element, theseelements definetwo properties:
Command of type System.Windows.Input.ICommand
CommandParameter of type Object
The SearchBar defines SearchCommand and SearchCommandParameter properties, whilethe ListView defines a
RefreshCommand property of type ICommand .
The ICommand interface defines two methods and oneevent:
void Execute(object arg)
bool CanExecute(object arg)
event EventHandler CanExecuteChange
Instead use CollectionView Selection with multiple.
use OnCollectionViewSelectionChanged event in codebehind to delete items
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Multiple"
SelectionChanged="OnCollectionViewSelectionChanged">
...
</CollectionView>
CollectionView collectionView = new CollectionView
{
SelectionMode = SelectionMode.Multiple
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
collectionView.SelectionChanged +=
OnCollectionViewSelectionChanged;
void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var previous = e.PreviousSelection;
var current = e.CurrentSelection;
...
}

Xamarin Forms TapGestureRecognizer Is not issuing command

So I have a xamarin forms app that currently only implemented for android. I am attempting to implement a tap event. When tapped though this never hits the command in the ViewModel. I'm not sure if I have something the matter with my code or I am just implementing it wrong.
ViewModel Code:
private RelayCommand<object> _OnClickableLabel;
public RelayCommand<object> OnClickableLabel
{
get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
}
private void Test(object currentObject)
{
Application.Current.MainPage.DisplayAlert("Alert", "were going down cap", "OK");
}
Page Xaml:
<ListView Grid.Row="1" ItemsSource="{Binding Notifications}" RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" BackgroundColor="{Binding BackgroundColor}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnClickableLabel }" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}"></Label>
<Label FontSize="Small" Text="{Binding Text}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tested it using a method in the page's cs code and that works fine but it has to be implemented in the ViewModel because it affects that data.
From your description, you want to add a tap gesture recognizer in ListView, and want to pass ListView current row data to TapGestureRecognizer event, am I right?
If yes, as Jason's opinion, you need to take a look Xamarin.Forms Relative Bindings firstly,name ListView as listview1, then take a look the following code:
<ListView
x:Name="listview1"
Grid.Row="1"
ItemsSource="{Binding Notifications}"
RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="{Binding BackgroundColor}" Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.OnClickableLabel, Source={x:Reference listview1}}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}" />
<Label FontSize="Small" Text="{Binding Text}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public partial class Page16 : ContentPage
{
public ObservableCollection<Notclass> Notifications { get; set; }
public ICommand OnClickableLabel { get; set; }
public Page16()
{
InitializeComponent();
Notifications = new ObservableCollection<Notclass>()
{
new Notclass(){Title="title 1",Text="notification 1"},
new Notclass(){Title="title 2",Text="notification 2"},
new Notclass(){Title="title 3",Text="notification 3"},
new Notclass(){Title="title 4",Text="notification 4"},
new Notclass(){Title="title 5",Text="notification 5"}
};
OnClickableLabel = new Command(n=> {
var vm = (Notclass)n;
Application.Current.MainPage.DisplayAlert("Alert",vm.Title , "OK");
});
this.BindingContext = this;
}
}
public class Notclass
{
public string Title { get; set; }
public string Text { get; set; }
public Color BackgroundColor { get; set; } = Color.White;
}

How to assign HeighRequest of a ListView depending on the length of the text of the labels inside it?

I have a ListView which in its DataTemplate has a ViewCell with 2 Labels. What I want to do is that the size of the ListView is proportional to the text that these two Labels have. If they don't contain text, make the ListView very small, if it contains a lot of text, make the size large.
Thats my actual code:
<ListView HasUnevenRows="True" HeightRequest="{Binding heightList}" ItemsSource="{Binding sourceList}" SelectionMode="Single" SeparatorColor="Transparent" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label FontSize="14" LineBreakMode="NoWrap" Text="{Binding text1}" />
<Label FontSize="14" LineBreakMode="NoWrap" Text="{Binding text2}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Can you just not bind isVisible to if text1/text2 is not ""? like
bool IsTxt1Vis { get { return text1 != ""; } }
What I want to do is that the size of the ListView is proportional to the text that these two Labels have. If they don't contain text, make the ListView very small, if it contains a lot of text, make the size large.
According to your description, you want to adjust height of listview to be minimum size to fit children? If yes, you can use binding ListView HeightRequestto do.
firstly, please set ListView VerticalOptions="StartAndExpand",otherwise the listview's heightrequest in StackLayout will be ignored.
<StackLayout>
<ListView
BackgroundColor="AliceBlue"
HasUnevenRows="True"
HeightRequest="{Binding rowHeigth}"
ItemsSource="{Binding sourceList}"
MinimumHeightRequest="50"
VerticalOptions="StartAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label
FontSize="14"
LineBreakMode="NoWrap"
Text="{Binding text1}" />
<Label
FontSize="14"
LineBreakMode="NoWrap"
Text="{Binding text2}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
x:Name="addbtn"
Command="{Binding AddCommand}"
Text=" add data in listview" />
</StackLayout>
public partial class Page5 : ContentPage
{
public Page5()
{
InitializeComponent();
this.BindingContext = new listviewmodel();
}
}
public class listviewmodel:ViewModelBase
{
public ObservableCollection<listmodel> sourceList { get; set; }
private int _rowHeigth;
public int rowHeigth
{
get { return _rowHeigth; }
set
{
_rowHeigth = value;
RaisePropertyChanged("rowHeigth");
}
}
public ICommand AddCommand { protected set; get; }
public listviewmodel()
{
sourceList = new ObservableCollection<listmodel>()
{
new listmodel(){text1="test 1",text2="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
new listmodel(){text1="test 1",text2="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
new listmodel(){text1="test 1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",text2="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccc"},
new listmodel(){text1="test 1",text2="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
new listmodel(){text1="test 1",text2="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
};
rowHeigth = sourceList.Count * 40;
AddCommand = new Command<listmodel>(async (key) => {
sourceList.Add(new listmodel() { text1 = "test 1", text2 = "aaaaaaaaaa" });
rowHeigth = sourceList.Count * 40;
});
}
}
public class listmodel
{
public string text1 { get; set; }
public string text2 { get; set; }
}
The ViewModelBase is class that implementing INotifyPropertyChanged interface.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

How to get indexes of Xamarin CollectionView Items?

I have a CollectionView:
<CollectionView Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="1" x:Name="SomeCollection"
ItemsLayout="VerticalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" Padding="3" Spacing="0">
<Label WidthRequest="90" HeightRequest="20" Text="{Binding SomeItemText}" BackgroundColor="Black" TextColor="White"
VerticalTextAlignment="Center" HorizontalTextAlignment="Center" />
<Button WidthRequest="36" HeightRequest="36" CornerRadius="0" ImageSource="plus_thick.xml" BackgroundColor="Green"
Clicked="Duplicate_Clicked" />
<Button WidthRequest="36" HeightRequest="36" CornerRadius="0" ImageSource="close_thick.xml" BackgroundColor="Red"
Clicked="Remove_Clicked" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Every item in the collection has a duplicate and a remove button. By clicking the button I would like to duplicate/remove the item, but CollectionView doesn't have indexes on items.
Could you help me with a custom renderer or even something shorter and simpler?
About getting current collectionview item index by Button command, I do one sample that you can take a look:
<StackLayout>
<CollectionView
x:Name="SomeCollection"
ItemsLayout="VerticalList"
ItemsSource="{Binding items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout
Padding="3"
Orientation="Horizontal"
Spacing="0">
<Label
BackgroundColor="Black"
HeightRequest="20"
HorizontalTextAlignment="Center"
Text="{Binding SomeItemText}"
TextColor="White"
VerticalTextAlignment="Center"
WidthRequest="90" />
<Button
BackgroundColor="Green"
Command="{Binding BindingContext.duplicatecommand, Source={x:Reference SomeCollection}}"
CommandParameter="{Binding}"
CornerRadius="0"
HeightRequest="36"
Text="duplicate item"
WidthRequest="36" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
public partial class Page13 : ContentPage
{
public ObservableCollection<someitems> items { get; set; }
public ICommand duplicatecommand { get; set; }
public Page13()
{
InitializeComponent();
items = new ObservableCollection<someitems>()
{
new someitems(){SomeItemText="test 1"},
new someitems(){SomeItemText="test 2"},
new someitems(){SomeItemText="test 3"},
new someitems(){SomeItemText="test 4"},
new someitems(){SomeItemText="test 5"},
new someitems(){SomeItemText="test 6"}
};
duplicatecommand = new Command<someitems>(getindexfun);
this.BindingContext = this;
}
private void getindexfun(someitems item)
{
int index = items.IndexOf(item);
}
}
public class someitems
{
public string SomeItemText { get; set; }
}
Instead of using click events, I would bind the buttons to a Command<T>.
This way in the ViewModel where you have your ItemsSource, you can just check what the index is of the Item ViewModel you are passing into the command as parameter. So in the ViewModel add something like this:
public Command<ItemViewModel> RemoveItemCommand { get; }
ctor() // this is the ViewModel constructor
{
RemoveItemCommand = new Command<ItemViewModel>(DoRemoveItemCommand);
}
private void DoRemoveItemCommand(ItemViewModel model)
{
// do stuff to remove
// probably something like:
Items.Remove(model);
// or get the index like:
var index = Items.IndexOf(model);
}
Then you can bind this command like so:
<Button
WidthRequest="36"
HeightRequest="36"
CornerRadius="0"
ImageSource="close_thick.xml"
BackgroundColor="Red"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemViewModel}}, Path=RemoveItemCommand}"
CommandParameter="{Binding}" />

Categories