views:

52

answers:

4

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"  BorderThickness="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?

A: 

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.

abhishek
I tried that (both your method and my variation of it earlier today) and it's still null.
Brent
+1  A: 

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;
    }
}
karmicpuppet
Nice I didn't now about ApplyTemplate(). Claudiu's answer is working for me right now but I'm sure I'll be able to use this down the road.
Brent
+1  A: 

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);
}
Claudiu
You are correct sir. Thanks.
Brent
A: 

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.

Ragepotato