[Solved] How can I change the contents of the ListView item(UWP) that is clicked? Like Mail App does


Possible Solution 1

You can have two data templates which share some UI, and one has some more elements than the other. The issue you will have is how to update to a different template on click on the fly.

I recently answered a question here which solves this issue by subscribing to the IsSelected property and manually reset the ContentTemplateSelector to force updating the template.

But swapping the full template to me is an overkill, and I lean towards your second solution too.

Possible Solution 2

Looks like you are trying to update the Height via a property in your ViewModel, this could work. But to me, this kind of UI logic is better put in the code-behind, where you can easily obtain your Grid inside the ItemClick callback and just assign a different Height to it.

But then what if you needed a similar ListView elsewhere with similar requirements? You’d end up with a lot of duplicate code-behind code. This is why I’d actually extend the ListViewItem functionality to cater for expanding some parts of UI inside its ItemTemplate. So here is how –

First, let’s create an ExtendedListViewItem that inherits from the ListViewItem. This extended control does two things –

  1. Create an attached property called IsExpandable which will later
    be attached to some UI element inside the ListView‘s data
    template.
  2. Since every time when a ListViewItem is clicked, the IsSelected property gets updated, we can monitor it to show and hide the IsExpandable element(s) accordingly.

public class ExtendedListViewItem : ListViewItem
{
    public ExtendedListViewItem()
    {
        // This could be set in its default style in Generic.xaml instead.
        HorizontalContentAlignment = HorizontalAlignment.Stretch;

        RegisterPropertyChangedCallback(IsSelectedProperty, (s, e) =>
        {
            // Children() is available at
            // https://github.com/JustinXinLiu/Continuity/blob/0cc3d7556c747a060d40bae089b80eb845da84fa/Continuity/Extensions/UtilExtensions.cs#L25
            foreach (var child in this.Children())
            {
                if (GetIsExpandable(child))
                {
                    child.Visibility = IsSelected ? Visibility.Visible : Visibility.Collapsed;
                }
            }
        });
    }

    public static void SetIsExpandable(DependencyObject element, bool value) =>
        element.SetValue(IsExpandableProperty, value);
    public static bool GetIsExpandable(DependencyObject element) =>
        (bool)element.GetValue(IsExpandableProperty);
    public static readonly DependencyProperty IsExpandableProperty = DependencyProperty.RegisterAttached(
        "IsExpandable",
        typeof(bool),
        typeof(ExtendedListViewItem),
        new PropertyMetadata(default(bool), (s, e) =>
            {
                var element = (UIElement)s;
                element.Visibility = (bool)e.NewValue ? Visibility.Collapsed : Visibility.Visible;
            }));
}

Next, we also need to extend the ListView control to take our ExtendedListViewItem control as its child instead of the default ListViewItem.


public class ExtendedListView : ListView
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ExtendedListViewItem();
    }
}

Finally, it’s time to attach the IsExpandable flag to the right element. For example, I only want the texts to show when the item is clicked –


<DataTemplate x:Key="MyItemTemplate">
    <StackPanel Margin="24">
        <Image HorizontalAlignment="Left" Source="{Binding Property3}" Width="48" Height="48" />

        <StackPanel Margin="12" local:ExtendedListViewItem.IsExpandable="True">
            <TextBlock Text="{Binding Property1}" Style="{StaticResource TitleTextBlockStyle}" />
            <TextBlock Text="{Binding Property2}" Style="{StaticResource CaptionTextBlockStyle}" />
        </StackPanel>
    </StackPanel>
</DataTemplate>

<local:ExtendedListView ContainerContentChanging="OnMyListViewContainerContentChanging" 
                        ItemTemplate="{StaticResource MyItemTemplate}" 
                        ItemsSource="{Binding Groups}" />

Bonus

Noticed the ExtendedListView control is subscribing to an event called ContainerContentChanging? This is the place where you can create implicit offset animations for your items to make layout changes more interesting.


    private void OnMyListViewContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
    {
        var containerVisual = ElementCompositionPreview.GetElementVisual(args.ItemContainer);

        if (args.InRecycleQueue)
        {
            containerVisual.ImplicitAnimations = null;
        }
        else
        {
            // EnableImplicitAnimation() is available at
            // https://github.com/JustinXinLiu/Continuity/blob/0015a96897c138e09d8604267df46da936b66838/Continuity/Extensions/CompositionExtensions.Implicit.cs#L144
            containerVisual.EnableImplicitAnimation(VisualPropertyType.Offset, 400.0f);
        }
    }

Outcome
enter image description here

Hope this helps!

1

solved How can I change the contents of the ListView item(UWP) that is clicked? Like Mail App does