MVVM How can I edit a ListView item in place when tapped?

Hi again,

I have a list of items as shown below, right now just three text cells.

<ListView ItemsSource="{Binding Items}" RowHeight="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*" />
                        <ColumnDefinition Width="1*" />
                        <ColumnDefinition Width="1*" />
                    </Grid.ColumnDefinitions>

                    <Label Grid.Column="0" Text="{Binding Col1}" />
                    <Label Grid.Column="1" Text="{Binding Col2}" />
                    <Label Grid.Column="2" Text="{Binding Col3}" />
                </Grid>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

What I want to be able to do is when the user taps on a list item, it replaces the labels on that item with either a picker or text box and provides a "Save" button. Once the user makes changes and clicks Save, the list updates and restores to the original format (all labels).

I'm using MVVM. Is this possible? Does the ListView have an Edit template or something similar?
Is there a more standard way of doing this (editing an item)?
With MVVM, how can I tell which item was tapped?

I'd prefer not to bring up a whole second screen. Thanks for your time.

Tagged:

Answers

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭
    Yes it’s possible. You should have in your ViewCell the Picker, the Button and the Entry with IsVisible property set to false. When you tap the row, you can set the IsVisible property to true.
  • EdDhalsimEdDhalsim USMember ✭✭

    @AlessandroCaliaro said:
    Yes it’s possible. You should have in your ViewCell the Picker, the Button and the Entry with IsVisible property set to false. When you tap the row, you can set the IsVisible property to true.

    Thanks for the suggestion. Sorry to ask, but how do I identify which controls I need to toggle? The ItemtappedEventArgs Item gives me the item tapped, but how to I find the controls? Also, I'm assuming I should be doing this in the form's code behind rather than the ViewModel.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    You should not know the "controls". You should know the "property". In your ViewModel you should have a property (for esample "IsTapped") that you set to "true" when your item is Tapped. If you bind "IsTapped" to your Button's "IsVisible" property, it becomes visible

  • NMackayNMackay GBInsider, University mod

    @EdDhalsim

    For an MVVM friendly approach, you should look at Data triggers, convertors and behaviors to accomplish this.

    In this scenario you just need to ensure your bound class has an edit model flag or something as a boolean that you can use to trigger the UI changes which you can do with either visibility.

    You can use behaviors to hook into the Listview's tapped event as suggested and pass the selected row to the viewmodelvia a command binding and set the boolean state there, you'll probably have to make a linq query to your OC to switch of any other rows in edit mode and if your class implements change tracking you may want to ask before wiping changes.

    I would suggest this is not not the usual behavior for an app, A Listview tap (especially if the listitem is highlighted) usually indicates a navigation event should take place.

  • NMackayNMackay GBInsider, University mod

    @EdDhalsim

    If your struggling to get my suggested approach working then let me know and I can assist with a sample or even better, knock together a sample and attach it or stick it on github, not your full app.

  • EdDhalsimEdDhalsim USMember ✭✭

    @AlessandroCaliaro said:
    You should not know the "controls". You should know the "property". In your ViewModel you should have a property (for esample "IsTapped") that you set to "true" when your item is Tapped. If you bind "IsTapped" to your Button's "IsVisible" property, it becomes visible

    Agreed. Controls should stay with the view. However, if all the hidden controls are bound to the same "IsTapped", wouldn't the controls in all rows be toggled? I'm only wanting the control in the row that was tapped to become visible. Perhaps if you provide a code sample that would help me understand.

  • EdDhalsimEdDhalsim USMember ✭✭

    @NMackay said:
    @EdDhalsim

    For an MVVM friendly approach, you should look at Data triggers, convertors and behaviors to accomplish this.

    In this scenario you just need to ensure your bound class has an edit model flag or something as a boolean that you can use to trigger the UI changes which you can do with either visibility.

    You can use behaviors to hook into the Listview's tapped event as suggested and pass the selected row to the viewmodelvia a command binding and set the boolean state there, you'll probably have to make a linq query to your OC to switch of any other rows in edit mode and if your class implements change tracking you may want to ask before wiping changes.

    I would suggest this is not not the usual behavior for an app, A Listview tap (especially if the listitem is highlighted) usually indicates a navigation event should take place.

    This approach sounds very complex. Converter? What would I be converting? Ideally I'd like to find a "simple" solution if possible.

    Another approach I saw was to trap the tapped event in the view's code behind, then use ContainerFromItem on the list view to find the specific container then update the controls. However, it looks like ContainerFromItem doesn't exist anymore (at least it's showing as undefined when I try it). I'm fine with doing everything in the view since swapping controls has no impact on the ViewModel until after they click save.

  • TaylorDTaylorD USMember ✭✭
    edited December 2018

    If you define your custom ViewCell in a separate xaml/xaml.cs file rather than in your ListView xaml, you can override the Tapped method in the xaml.cs file. From there you are in the particular ViewCell you are wanting and can show/hide controls or set a IsVisible property on your Model object that is the BindingContext to your ViewCell.

  • TaylorDTaylorD USMember ✭✭

    While the comment I posted above might work, it isn't good practice and doesn't comply with MVVM. I agree with @NMackay and suggest a different approach to this using data triggers and such to accomplish what you want. Try his suggestions and post example if able to.

  • NMackayNMackay GBInsider, University mod
    edited December 2018

    @EdDhalsim

    For an MVVM friendly approach, you should look at Data triggers, convertors and behaviors to accomplish this.

    In this scenario you just need to ensure your bound class has an edit model flag or something as a boolean that you can use to trigger the UI changes which you can do with either visibility of > @EdDhalsim said:

    @NMackay said:
    @EdDhalsim

    For an MVVM friendly approach, you should look at Data triggers, convertors and behaviors to accomplish this.

    In this scenario you just need to ensure your bound class has an edit model flag or something as a boolean that you can use to trigger the UI changes which you can do with either visibility.

    You can use behaviors to hook into the Listview's tapped event as suggested and pass the selected row to the viewmodelvia a command binding and set the boolean state there, you'll probably have to make a linq query to your OC to switch of any other rows in edit mode and if your class implements change tracking you may want to ask before wiping changes.

    I would suggest this is not not the usual behavior for an app, A Listview tap (especially if the listitem is highlighted) usually indicates a navigation event should take place.

    This approach sounds very complex. Converter? What would I be converting? Ideally I'd like to find a "simple" solution if possible.

    Another approach I saw was to trap the tapped event in the view's code behind, then use ContainerFromItem on the list view to find the specific container then update the controls. However, it looks like ContainerFromItem doesn't exist anymore (at least it's showing as undefined when I try it). I'm fine with doing everything in the view since swapping controls has no impact on the ViewModel until after they click save.

    Not complex but whatever works for you, you can write your own custom data template or subclass the listview control, data binding in Forms is powerful though and can save you a lot of effort.

Sign In or Register to comment.