I think this should be easy but I'm having a tough time with it.
How can I get a reference to my ListBox's scrollviewer in C#? I've tried pretty much everything I can think of. The ListBox is in a WPF Custom Control so we use Template.FindName to get references to all our controls. My ListBox looks like this:
<ListBox x:Name="PART_SoundList"
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Hidden" Focusable="False" FocusVisualStyle="{x:Null}"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Borde开发者_开发问答rThickness="0"
ItemContainerStyleSelector="{StaticResource ListBoxItemAlternatingStyleSelector}"
ItemsSource="{Binding}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" Height="850" Focusable="False" Panel.ZIndex="999" >
<WrapPanel.RenderTransform>
<TransformGroup>
<ScaleTransform CenterX="0" CenterY="0" ScaleX=".75" ScaleY=".57" />
</TransformGroup>
</WrapPanel.RenderTransform>
</WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Template>
<ControlTemplate>
<ScrollViewer x:Name="Scroller" VerticalAlignment="Bottom" Focusable="False" Style="{StaticResource HorizontalScroller}" >
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Focusable="False" Panel.ZIndex="999" />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
</ListBox>
Template.FindName("Scroller",this) as ScrollViewer results in null.
Any ideas?
You probably try to get a reference to the ScrollViewer too soon. Try to move your code in the loaded event and check if it still returns null:
in your customControl/form constructor:
this.Loaded += MainWindow_Loaded;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var x = PART_SoundList.Template.FindName("Scroller", PART_SoundList);
}
I'm assuming that the XAML you have above is part of the ControlTemplate for your CustomControl, right? I would also assume that you're getting the control parts on the OnApplyTemplate() method, right? If this is the case, then, I think what you need to do is to force a call to PART_SoundList.ApplyTemplate() before finding the ScrollViewer. So, the code for your Custom Control should look something like this:
public class MyControl : Control
{
private ListBox lb;
private ScrollViewer scroller;
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
lb = this.Template.FindName("PART_SoundList", this) as ListBox;
lb.ApplyTemplate();
scroller = lb.Template.FindName("Scroller", lb) as ScrollViewer;
}
}
Use recursive call to Visual Tree to grab any Visual from the tree.
public static ChildItem FindVisualChild<childItem>(DependencyObject obj) where ChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is ChildItem)
return (ChildItem)child;
else
{
ChildItem childOfChild = FindVisualChild<ChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
This gives you a generic method to get Visual element of type mentioned from the Visual tree.
For those who got here seeking an answer to the original question:
In C#
ScrollViewer sv = FindVisualChild<ScrollViewer>(MyListBox);
or in VB
Dim sv As ScrollViewer = FindVisualChild(Of ScrollViewer)(MyListBox)
Where in XAML
<ListBox x:Name="MyListBox">
</ListBox>
If you are going to use the reference to scroll/check viewport size the IScrollProvider should be sufficient for you.
You can access it like this in your code behind (Note Claudiu point of waiting til the loaded event):
ListBoxAutomationPeer svAutomation = (ListBoxAutomationPeer)ScrollViewerAutomationPeer.CreatePeerForElement(PART_SoundList);
// not feeling creative with my var names today...
IScrollProvider scrollInterface = (IScrollProvider)svAutomation.GetPattern(PatternInterface.Scroll);
Then scroll horizontally and vertically whenever you want and to your hearts content.
Use this to access scroll viewer in WPF.
var scrollViewer = ((Border)PlaybackDeviceList.Template.FindName("Bd", PlaybackDeviceList)).Child;
精彩评论