UWP XAML ListView Nested UI on PointerOver

763 views Asked by At

I want to create a ListView where the ListViewItems have nested buttons next to the label, which only are shown on PointerOver. That's the DataTemplate I created for this. I tried it with VisualStateManager, but that doesn't work...

So the question is: What's the best pure-XAML way to do that? (or at least a working way would suffice ;) )

Thanks!

<DataTemplate x:Name="aUITodoListViewDataTemplate" x:DataType="a:aTodo">
    <Grid HorizontalAlignment="Stretch" PointerEntered="Grid_PointerEntered" PointerExited="Grid_PointerExited">
        <Grid.ColumnDefinitions>
            <!-- creates a grid with three columns, the first so wide as needed, and the second two in a 7/1 ratio-->
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="70*" MaxWidth="500" MinWidth="100"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <!-- First is the actual label -->
        <TextBlock Text="{x:Bind Name}" Grid.Column="0" VerticalAlignment="Center" />

        <!-- the middle column is for spacing -->

        <!-- the last column is for the buttons -->
        <StackPanel Grid.Column="2" Orientation="Horizontal" x:Name="buttonGroup">
            <Button>A</Button>
            <Button>B</Button>
            <Button>C</Button>
        </StackPanel>

        <!-- The visual states -->
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="buttonGroup" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                            <DiscreteObjectKeyFrame KeyTime="0:0:0.333" Value="Collapsed" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="PointerOver">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="buttonGroup" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
                            <DiscreteObjectKeyFrame KeyTime="0:0:0.333" Value="Visible" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>
</DataTemplate>
1

There are 1 answers

0
Petter Hesselberg On BEST ANSWER

You can make it work by providing a custom control template for ListViewItem. For example:

<ListView
    ItemsSource="{Binding Data, ElementName=self}"
    Grid.Row="1"
    SelectionChanged="OnSelectionChanged"
    x:Name="listView">
    <ListView.Resources>
        <Style TargetType="ListViewItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListViewItem">
                        <Grid Background="Pink" x:Name="grid" DataContext="{TemplateBinding DataContext}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="90" />
                            </Grid.ColumnDefinitions>
                            <ContentPresenter />
                            <StackPanel Grid.Column="1" Orientation="Horizontal" x:Name="buttonGroup" Visibility="Collapsed">
                                <Button>A</Button>
                                <Button>B</Button>
                                <Button>C</Button>
                            </StackPanel>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal" />
                                    <VisualState x:Name="Pressed" />
                                    <VisualState x:Name="PointerOver">
                                        <VisualState.Setters>
                                            <Setter Target="buttonGroup.Visibility" Value="Visible" />
                                            <Setter Target="grid.Background" Value="Orange" />
                                        </VisualState.Setters>
                                    </VisualState>
                                    <VisualState x:Name="Disabled" />
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.Resources>
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

For context, my toy model object looks like this:

public class Data
{
    public string Name { get; set; }
}

...and the page itself (named self) exposes the following collection property:

public Data[] Data { get; set; } =
    {
        new Data { Name = "One" },
        new Data { Name = "Two" },
        new Data { Name = "Three" },
    };

Note that you can't bind to your data model from within the ControlTemplate, hence the DataTemplate with a TextBlock bound to Name, and the ContentPresenter in the ControlTemplate. Conversely, if you want to use the VSM from the DataTemplate, you need to set states programatically.

Here is a related post.

The result is really ugly and looks as follows (with the mouse over "Two"):

enter image description here