Как фиксировать IsMouseOver над элементом ListBox?

Исходные данные

Есть ListBox. Структура его элементов задаётся ItemTemplate. При выборе одного из элементов совершается действие (меняется содержимое окна). Среди прочего, у каждой строки списка есть кнопки, изначально скрытые.

Задача

При наведении курсора на элемент списка, его кнопки должны становиться видимыми. Если возможно, не выходя за пределы паттерна MVVM, не прибегая к code-behind.

Решения и проблемы

1

С помощью триггера у меня получилось связать кнопку со свойством IsMouseOver родительского контейнера (Border или DockPanel).

<ListBox HorizontalContentAlignment="Stretch"
         ItemsSource="{Binding Tasks}"
         SelectedItem="{Binding SelectedTask}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Border Name="Item" BorderBrush="Blue" BorderThickness="1">
                    <DockPanel LastChildFill="True">
                        <Label Content="{Binding Path=Name}"/>
                        
                        <Button Content="Btn"
                                Height="30" FontSize="20"
                                BorderThickness="0" Background="Transparent"
                                HorizontalAlignment="Center">
                            <Button.Style>
                                <Style TargetType="Button">
                                    <Setter Property="Visibility" Value="Hidden"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding IsMouseOver, ElementName=Item}" Value="True">
                                            <Setter Property="Visibility" Value="Visible"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Button.Style>
                        </Button>
                    </DockPanel>
                </Border>
            </DataTemplate>
        </ListBox.ItemTemplate>
</ListBox>

Всё работает как надо, но только пока курсор касается текста или рамки, пустое пространство внутри того же Border не активирует триггер.

Border Name="Item"

2

Эту проблему решает помещение Border в ListBoxItem и обращение триггера кнопки к нему.

<ListBox HorizontalContentAlignment="Stretch"
         ItemsSource="{Binding Tasks}"
         SelectedItem="{Binding SelectedTask}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <ListBoxItem Name="Item">
                    <Border BorderBrush="Blue" BorderThickness="1">
                        //Всё то же самое
                    </Border>
                </ListBoxItem>
            </DataTemplate>
        </ListBox.ItemTemplate>
</ListBox>

Я ожидал тем самым обратиться к контейнеру самого верхнего уровня для элемента списка, но вместо этого WPf создаёт прозрачный прямоугольник, перекрывающий вложенные элементы. При наведении на него курсора триггер кнопки работает достаточно хорошо, но клик по нему не приводит к выбору элемента списка.

ListBoxItem Name="Item"

Обратите внимание, что размер созданного ListBoxItem прямоугольника не совпадает с фактическим размером элемента списка.

Вопросы

Как заставить свойсто IsMouseOver контейнеров считывать курсор над вложенным пустым пространством?

Как обратиться к контейнеру верхнего уровня, используя ItemTemplate?


Ответы (1 шт):

Автор решения: aepot

Как-то так получится. Убрал лишнее, немного поправил разметку. Не используйте Label в WPF, это устаревшее наследие. Используйте TextBlock для отображения текста. Не используйте размеры, используйте отступы.

<ListBox HorizontalContentAlignment="Stretch"
         ItemsSource="{Binding Tasks}"
         SelectedItem="{Binding SelectedTask}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border BorderBrush="Blue" BorderThickness="1">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" Padding="5" VerticalAlignment="Center" FontSize="20"/>
                    <Button Grid.Column="1" Content="Btn" Padding="15,5"
                            FontSize="20"
                            BorderThickness="0"
                            Background="Transparent">
                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="Visibility" Value="Hidden"/>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
                                        <Setter Property="Visibility" Value="Visible"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>
                </Grid>
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Как обратиться к контейнеру верхнего уровня, используя ItemTemplate?

См. RelativeSource

→ Ссылка