I am trying to get the selected item in the tap event of a button in a list view item, but I can't seem to figure it out. Anyone know how to accomplish this?
This is one of the situations in which you really need to understand how MVVM works. A ListView's ItemsSource property refers to a list of view models for each item, and the cell should use bindings to the properties in that item view model. A button within the cell should also use a binding to a command. A command is a way of handling an event in a view model by using a binding to inject the handler into the view. The way it works is this: in the view model you define a new property of type ICommand, which might look like this:
private Command _clickCommand;
public ICommand ClickCommand
{
get
{
if (_clickCommand == null)
{
_clickCommand = new Command(() => ClickCount++);
}
return _clickCommand;
}
}
Then in your view, instead of using the Clicked event for the button you use the Command property, and you use a binding like this:
When that button is clicked it will call handler for the command specific to that particular instance of the item view model type, and you can directly modify the state of that item from the handler.
A complete example is attached, and you can read more here.
@adamkemp I'm looking into doing it with a command as you said. Is there no way to pass a binding context to the button or something along those lines though?
So say I have a list of users and I want to access the selected user when the user clicks a button on that list item. How would I access the selected user (item) in the tap event?
It seems like you didn't look at the example I attached. Please look at that.
You don't use an event for this. You use the command, which is itself a binding to the view model. In the context of your user example, the handler for the command would be in the UserViewModel class. If you're trying to put an event handler in your view or page class then you're going down the wrong path.
I looked through the example, but it's very different than the code i've been using so I was trying to avoid going that route. Most of my code has events in the code behind like all the Xamarin examples etc. What i'm confused about is that I need to have an Xlabs popup open when the button is clicked and it seems odd to do it with a command in the view model, but i'll give it a shot.
Thanks for getting back to me so quick btw and sorry for being a little slow at this. I'm new to Xamarin and C# and the whole view model command thing is odd and not documented well in the Xamarin examples.
Most of my code has events in the code behind like all the Xamarin examples etc
Yeah, it's a shame most of the examples avoid MVVM (because it's complicated) because in a case like this you really need to use MVVM, and a lot of people get stuck in the exact same situation you're in now.
Yes, MVVM is better. But, to answer your question, if you are using the ListView.ItemTapped event, the ItemTappedEventArgs has an Item property that passes along the visual item that was tapped. Cast it to the correct type.
private void ListViewItemTapped(object sender, ItemTappedEventArgs e)
{
var user = e.Item as User;
}
If you are instead using a Button.Clicked event for a button in a cell in the ListView, you'll have to cast the sender to a Button and get the user from the buttons BindingContext.
private void ButtonClicked(object sender, EventArgs e)
{
var button = sender as Button;
var user = button.BindingContext as User;
//Perform actions on user
}
@LeeOlsen.3738 That's exactly what I was trying to do. Thanks!!
@adamkemp I went with Lee's suggestion for now. MVVM is a bit odd for me since I come from a Ruby/Rails background. I'm use to MVC and the whole view/view model thing seems a bit redundant to me when you have a code behind file. Thanks for all your help though. I am going to look through your code more and start trying to convert things to be more MVVM as I get a better grasp on it.
@LeeOlsen.5615 said:
Yes, MVVM is better. But, to answer your question, if you are using the ListView.ItemTapped event, the ItemTappedEventArgs has an Item property that passes along the visual item that was tapped. Cast it to the correct type.
private void ListViewItemTapped(object sender, ItemTappedEventArgs e)
{
var user = e.Item as User;
}
If you are instead using a Button.Clicked event for a button in a cell in the ListView, you'll have to cast the sender to a Button and get the user from the buttons BindingContext.
private void ButtonClicked(object sender, EventArgs e)
{
var button = sender as Button;
var user = button.BindingContext as User;
//Perform actions on user
}
I am using the Button.clicked event and do like above. But getting "System.NullReferenceException: Object reference not set to an instance of an object" on the user actions lines.
Most likely this expression is returning null: `button.BindingContext as UserProfileTO`. That either means the `BindingContext` is null or it isn’t the expected type. You’ll have to debug it to determine which.
`Yes, MVVM is better. But, to answer your question, if you are using the ListView.ItemTapped event, the ItemTappedEventArgs has an Item property that passes along the visual item that was tapped. Cast it to the correct type.
private void ListViewItemTapped(object sender, ItemTappedEventArgs e)
{
var user = e.Item as User;
}
If you are instead using a Button.Clicked event for a button in a cell in the ListView, you'll have to cast the sender to a Button and get the user from the buttons BindingContext.
private void ButtonClicked(object sender, EventArgs e)
{
var button = sender as Button;
var user = button.BindingContext as User;
//Perform actions on user
}
`
Posts
This is one of the situations in which you really need to understand how MVVM works. A ListView's
ItemsSource
property refers to a list of view models for each item, and the cell should use bindings to the properties in that item view model. A button within the cell should also use a binding to a command. A command is a way of handling an event in a view model by using a binding to inject the handler into the view. The way it works is this: in the view model you define a new property of typeICommand
, which might look like this:Then in your view, instead of using the
Clicked
event for the button you use theCommand
property, and you use a binding like this:When that button is clicked it will call handler for the command specific to that particular instance of the item view model type, and you can directly modify the state of that item from the handler.
A complete example is attached, and you can read more here.
@adamkemp I'm looking into doing it with a command as you said. Is there no way to pass a binding context to the button or something along those lines though?
The button has a binding context, which it inherits from its parent. Ultimately the binding context comes from the item in
ItemsSource
for that cell.So say I have a list of users and I want to access the selected user when the user clicks a button on that list item. How would I access the selected user (item) in the tap event?
I keep trying
this.BindingContext
in the tap event, but it saysObject reference not set to an instance of object
.It seems like you didn't look at the example I attached. Please look at that.
You don't use an event for this. You use the command, which is itself a binding to the view model. In the context of your user example, the handler for the command would be in the UserViewModel class. If you're trying to put an event handler in your view or page class then you're going down the wrong path.
I looked through the example, but it's very different than the code i've been using so I was trying to avoid going that route. Most of my code has events in the code behind like all the Xamarin examples etc. What i'm confused about is that I need to have an Xlabs popup open when the button is clicked and it seems odd to do it with a command in the view model, but i'll give it a shot.
Thanks for getting back to me so quick btw and sorry for being a little slow at this. I'm new to Xamarin and C# and the whole view model command thing is odd and not documented well in the Xamarin examples.
Yeah, it's a shame most of the examples avoid MVVM (because it's complicated) because in a case like this you really need to use MVVM, and a lot of people get stuck in the exact same situation you're in now.
Yes, MVVM is better. But, to answer your question, if you are using the ListView.ItemTapped event, the ItemTappedEventArgs has an Item property that passes along the visual item that was tapped. Cast it to the correct type.
If you are instead using a Button.Clicked event for a button in a cell in the ListView, you'll have to cast the sender to a Button and get the user from the buttons BindingContext.
@LeeOlsen.3738 That's exactly what I was trying to do. Thanks!!
@adamkemp I went with Lee's suggestion for now. MVVM is a bit odd for me since I come from a Ruby/Rails background. I'm use to MVC and the whole view/view model thing seems a bit redundant to me when you have a code behind file. Thanks for all your help though. I am going to look through your code more and start trying to convert things to be more MVVM as I get a better grasp on it.
I am using the Button.clicked event and do like above. But getting "System.NullReferenceException: Object reference not set to an instance of an object" on the user actions lines.
My code:
Button inside listview-viewcell
Button clicked event:
Any suggestions?
Hello @LeeOlsen.5615,
Your solution work for me
`Yes, MVVM is better. But, to answer your question, if you are using the ListView.ItemTapped event, the ItemTappedEventArgs has an Item property that passes along the visual item that was tapped. Cast it to the correct type.
private void ListViewItemTapped(object sender, ItemTappedEventArgs e)
{
var user = e.Item as User;
}
If you are instead using a Button.Clicked event for a button in a cell in the ListView, you'll have to cast the sender to a Button and get the user from the buttons BindingContext.
private void ButtonClicked(object sender, EventArgs e)
{
var button = sender as Button;
var user = button.BindingContext as User;
//Perform actions on user
}
`