I want to adjust the width or visibility of a Border or Margin that is placed above a separate ListBox if the vertical scroll bar of the ListBox is collapsed.
I am trying something like this, but cannot figure out how to get to the ListBox ScrollViewer. Obviously, the Path in the DataTrigger is not correct.
<Border Width={Binding Source={x:Static SystemParameters.ScrollWidth}}">
<Border.Style>
<Style>
<Setter Property="Border.Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=listBox,
Path=***ScrollViewer.ComputedVerticalScrollBarVisibility***}"
Value="Collapsed">
<Setter Property="Border.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<ListBox Name="listBox" ItemsSource="{Binding MyItems}"/>
Is there a way to get to that ListBox property? If not, is there a better way to solve this problem?
Thanks much!
The workaround of wrapping the ListBox in another ScrollViewer allows an all-XAML solution at the expense of a redundant ScrollViewer control (see comment in the question). In the end, my team-mate decided to go with a code-behind solution as follows.
Here are the relevant attributes for the ListBox that has the ScrollViewer we need to access.
<ListBox Name="_listBox" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Loaded="InitializeListBoxScrollViewerProperty"/>
Here is the code-behind to expose the scroll viewer for use by external controls.
private static readonly DependencyPropertyKey ListBoxScrollViewerPropertyKey =
DependencyProperty.RegisterReadOnly("ListBoxScrollViewer", typeof(ScrollViewer),
typeof(MyEditView), new PropertyMetadata());
protected static readonly DependencyProperty ListBoxScrollViewerProperty =
ListBoxScrollViewerPropertyKey.DependencyProperty;
protected ScrollViewer ListBoxScrollViewer
{
get { return (ScrollViewer)GetValue(ListBoxScrollViewerProperty); }
private set { SetValue(ListBoxScrollViewerPropertyKey, value); }
}
private void InitializeListBoxScrollViewerProperty(object sender, RoutedEventArgs args)
{
if (ReferenceEquals(args.OriginalSource, _listBox))
{
var scrollViewer = _listBox.GetFirstDescendantBreadthFirst<ScrollViewer>();
if (scrollViewer != null)
{
ListBoxScrollViewer = scrollViewer;
}
}
}
Here are the extension methods used:
public static class DependencyObjectExtensions
{
public static TDescendant GetFirstDescendantBreadthFirst<TDescendant>
(this DependencyObject dependencyObject) where TDescendant : DependencyObject
{
if (dependencyObject == null) { throw new ArgumentNullException(); }
return GetFirstDescendantBreadthFirst<TDescendant>(GetAllChildren(dependencyObject));
}
private static TDescendant GetFirstDescendantBreadthFirst<TDescendant>
(IEnumerable<DependencyObject> descendants) where TDescendant : DependencyObject
{
if (!descendants.Any()) return null;
var descendant = descendants.OfType<TDescendant>().FirstOrDefault();
if (descendant != null) return descendant;
return GetFirstDescendantBreadthFirst<TDescendant>(descendants.SelectMany(GetAllChildren));
}
private static IEnumerable<DependencyObject> GetAllChildren(DependencyObject dependencyObject)
{
return Enumerable
.Range(0, VisualTreeHelper.GetChildrenCount(dependencyObject))
.Select(i => VisualTreeHelper.GetChild(dependencyObject, i));
}
}
And finally, the scroll viewer can be accessed in another part of the XAML.
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding ListBoxScrollViewer.ComputedVerticalScrollBarVisibility,
RelativeSource={RelativeSource AncestorType=l:MyEditView}}" Value="Visible">
<Setter Property="Margin" Value="{StaticResource myWidenedMargin}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>