Accessing the ScrollViewer of a ListBox from C#
I'd like to change the properties of a ScrollViewer
of a ListBox
from C#.
I found this question here on Stackoverflow. I took the accepted answer's advice and exposed the ScrollViewer
as a property of a subclass. However, this doesn't appear to be working in an example shown below. Some of the comments in that question also state that this technique didn't work.
XAML:
<Window x:Class="StackoverflowListBoxScrollViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace StackoverflowListBoxScrollViewer
{
public class MyListBox : ListBox
{
public ScrollViewer ScrollViewer
{ get { return (ScrollViewer)GetTemplateChild("ScrollViewer"); } }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var myListBox = new MyListBox();
Content = myListBox;
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
var button = new Button() { Content = "Check ScrollViewer" };
button.Click += (s, e) =>
{
if (myListBox.ScrollViewer == null)
Console.WriteLine("null");
};
myListBox.Items.Add(button);
}
}
}
When I click the "Check ScrollViewer" button, it prints "null". I.e., the ScrollViewer
wasn't retrieved.
How do I get to that darn ScrollViewer
? :-)
Solution 1:
you can try this little helper function
usage
var scrollViewer = GetDescendantByType(yourListBox, typeof(ScrollViewer)) as ScrollViewer;
helper function
public static Visual GetDescendantByType(Visual element, Type type)
{
if (element == null) {
return null;
}
if (element.GetType() == type) {
return element;
}
Visual foundElement = null;
if (element is FrameworkElement) {
(element as FrameworkElement).ApplyTemplate();
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type);
if (foundElement != null) {
break;
}
}
return foundElement;
}
Hope this helps
Solution 2:
If you will use standard ListBox, so you can change yours getter to this one:
public class MyListBox : ListBox
{
public ScrollViewer ScrollViewer
{
get
{
Border border = (Border)VisualTreeHelper.GetChild(this, 0);
return (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
}
}
}
Solution 3:
I've modified the great answer of @punker76 to create an extension method for Visual and provide explicit return type:
public static class Extensions
{
public static T GetDescendantByType<T>(this Visual element) where T:class
{
if (element == null)
{
return default(T);
}
if (element.GetType() == typeof(T))
{
return element as T;
}
T foundElement = null;
if (element is FrameworkElement)
{
(element as FrameworkElement).ApplyTemplate();
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = visual.GetDescendantByType<T>();
if (foundElement != null)
{
break;
}
}
return foundElement;
}
}
You can now call it by SomeVisual.GetDescendantByType and it returns either already a correct typed ScrollViewer or null (which is default(T))