Мое решение состояло в том, чтобы использовать многозначный преобразователь, который принимает в качестве входных данных ListBox
, ScrollViewer
и ListBoxItem
в иерархии, а также окно списка ActualHeight
, средство просмотра прокрутки VerticalOffset
, а элемент списка ActualHeight
и возвращает видимость. Последние (двойные) значения дерева приведены здесь только для того, чтобы гарантировать, что метод Convert
преобразователя будет вызываться при изменении любого значимого значения. По сути, возвращаемое значение Visibility
равно Hidden
, если нижняя часть элемента больше нижней части средства просмотра с прокруткой, а Visible
else.
Вот код конвертера:
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
public class ListBoxItemToVisibilityConverter : IMultiValueConverter
{
public object Convert(
object[] values,
Type targetType,
object parameter,
CultureInfo culture)
{
if ((values?.Length ?? 0) != 6)
return Visibility.Collapsed;
var listBox = values.OfType<ListBox>().FirstOrDefault();
var scrollViewer = values.OfType<ScrollViewer>().FirstOrDefault();
var listBoxItem = values.OfType<ListBoxItem>().FirstOrDefault();
var heights = values.OfType<double>().ToArray();
if (new object[] { listBox, scrollViewer, listBoxItem }.Any(item => item == null) || heights.Length != 3)
return Visibility.Collapsed;
var scrollViewerBottom = scrollViewer.PointToScreen(new Point(0, scrollViewer.ActualHeight)).Y;
var listBoxItemBottom = listBoxItem.PointToScreen(new Point(0, listBoxItem.ActualHeight)).Y;
return listBoxItemBottom > scrollViewerBottom ? Visibility.Hidden : Visibility.Visible;
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
CultureInfo culture) =>
throw new NotSupportedException();
}
его декларация:
<local:ListBoxItemToVisibilityConverter x:Key="ListBoxItemToVisibility"/>
его использование в шаблоне предмета:
<DataTemplate>
<Button Content="{Binding Text}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ListBoxItemToVisibility}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBoxItem}"/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ScrollViewer}"/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}"/>
<Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBoxItem}"/>
<Binding Path="VerticalOffset" RelativeSource="{RelativeSource FindAncestor, AncestorType=ScrollViewer}"/>
<Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}"/>
</MultiBinding>
</Button.Visibility>
</Button>
</DataTemplate>