locked
SilverLight ScrollViewer Scroll Event RRS feed

  • Question

  • I am attempting to port a WPF application to SilverLight 2 release version.

    In the WPF version, the ScrollViewer will fire Scroll events. This allowed me to sync other parts of the UI that were not part of the ScrollViewer content.

    I have googled for a day to find a way on how to hook up the Scroll event. Almost all solutions and workarounds I found were either WPF people answering and were not aware of SilverLight limitations or were plain misleading or wrong.

    Can someone point me to code or a working sample that actually works ?

    Thanks 

     

     

    Saturday, November 29, 2008 9:29 PM

Answers

  • There is no ScrollChanged in Silverlight (as you have discovered), but you can work around it by hooking to the mousemove event on the ScrollView and then looking at the HorizontalOffset and VerticalOffset properties.  Unfortunately the ScrollViewer class is sealed so you can't inherit from it and add this functionality, but the following will give you a possible solution to work with:

    Xaml (example):

     

        <StackPanel x:Name="LayoutRoot" Background="White">
            <ScrollViewer   Width="200" MouseMove="ScrollViewer_MouseMove"  Height="200" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
                <Rectangle Width="500" Height="500" Stroke="Red" StrokeThickness="5" >
                    <Rectangle.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="Red" Offset="0.0"/>
                            <GradientStop Color="Blue" Offset="0.3"/>
                            <GradientStop Color="Green" Offset="0.8"/>
                        </RadialGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
            </ScrollViewer>
            <TextBlock x:Name="TextBlock1" Text="text" Grid.Row="1"></TextBlock>
    
        </StackPanel>
     

     

    Code:

        public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();
            }
    
            double lastVerticalOffset;
            double lastHorizontalOffset;
            private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
            {
                double verticalOffset = ((ScrollViewer)sender).VerticalOffset;
                double horizontalOffset = ((ScrollViewer)sender).HorizontalOffset;
                if  (verticalOffset != lastVerticalOffset || horizontalOffset != lastHorizontalOffset)
                {
                    ScrollChangedEventArgs args = new ScrollChangedEventArgs();
                    args.HorizontalChange = horizontalOffset-lastHorizontalOffset;
                    args.HorizontalOffset = horizontalOffset;
                    args.VerticalChange = verticalOffset - lastVerticalOffset;
                    args.VerticalOffset = verticalOffset;
                    ScrollChanged(args);
                    lastHorizontalOffset = horizontalOffset;
                    lastVerticalOffset = verticalOffset;
                }
            }
    
            private void ScrollChanged(ScrollChangedEventArgs e)
            {
                TextBlock1.Text = e.VerticalOffset + "," + e.HorizontalOffset;
            }
    
    
        }
    
        public class ScrollChangedEventArgs
        {
    
            public double HorizontalOffset { get; set; }
            public double VerticalOffset { get; set; }
            public double HorizontalChange { get; set; }
            public double VerticalChange { get; set; }
        }
     HTH
    Sunday, November 30, 2008 10:47 AM

All replies

  • The way I handled this was to set the scrollbar visibility to hidden and then add my own scrollbar. Then use that scrollbar to scroll the scrollviewer and the other content that needed to be scrolled.

    If you need some sample code still let me know.

    Sunday, November 30, 2008 10:40 AM
  • There is no ScrollChanged in Silverlight (as you have discovered), but you can work around it by hooking to the mousemove event on the ScrollView and then looking at the HorizontalOffset and VerticalOffset properties.  Unfortunately the ScrollViewer class is sealed so you can't inherit from it and add this functionality, but the following will give you a possible solution to work with:

    Xaml (example):

     

        <StackPanel x:Name="LayoutRoot" Background="White">
            <ScrollViewer   Width="200" MouseMove="ScrollViewer_MouseMove"  Height="200" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
                <Rectangle Width="500" Height="500" Stroke="Red" StrokeThickness="5" >
                    <Rectangle.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="Red" Offset="0.0"/>
                            <GradientStop Color="Blue" Offset="0.3"/>
                            <GradientStop Color="Green" Offset="0.8"/>
                        </RadialGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
            </ScrollViewer>
            <TextBlock x:Name="TextBlock1" Text="text" Grid.Row="1"></TextBlock>
    
        </StackPanel>
     

     

    Code:

        public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();
            }
    
            double lastVerticalOffset;
            double lastHorizontalOffset;
            private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
            {
                double verticalOffset = ((ScrollViewer)sender).VerticalOffset;
                double horizontalOffset = ((ScrollViewer)sender).HorizontalOffset;
                if  (verticalOffset != lastVerticalOffset || horizontalOffset != lastHorizontalOffset)
                {
                    ScrollChangedEventArgs args = new ScrollChangedEventArgs();
                    args.HorizontalChange = horizontalOffset-lastHorizontalOffset;
                    args.HorizontalOffset = horizontalOffset;
                    args.VerticalChange = verticalOffset - lastVerticalOffset;
                    args.VerticalOffset = verticalOffset;
                    ScrollChanged(args);
                    lastHorizontalOffset = horizontalOffset;
                    lastVerticalOffset = verticalOffset;
                }
            }
    
            private void ScrollChanged(ScrollChangedEventArgs e)
            {
                TextBlock1.Text = e.VerticalOffset + "," + e.HorizontalOffset;
            }
    
    
        }
    
        public class ScrollChangedEventArgs
        {
    
            public double HorizontalOffset { get; set; }
            public double VerticalOffset { get; set; }
            public double HorizontalChange { get; set; }
            public double VerticalChange { get; set; }
        }
     HTH
    Sunday, November 30, 2008 10:47 AM
  • Thanks IanBlackburn. I downloaded your zip file and it works just fine. I then incorporated your code in my project, and it works fine too.

    Thanks agains.Big Smile

     

    Sunday, November 30, 2008 12:42 PM
  • You can get scroll event from ScrollViewer scrollbar. Try this

     hBar = ((FrameworkElement)VisualTreeHelper.GetChild(ScrollViewerHorizontalGraph, 0)).FindName("VerticalScrollBar") as System.Windows.Controls.Primitives.ScrollBar;

    hBar.Scroll += new ScrollEventHandler(HorizontalScrollBarScrolled);

    Then you get a scroll event

    Monday, December 1, 2008 2:23 AM
  • Thanks Olav. The code line you mentioned will not even compile.

    I also saw a previous post that mentions something like this:

    ScrollBar vBar = ((FrameworkElement)VisualTreeHelper.GetChild(SurfaceScrollViewer, 0)).FindName("VerticalScrollBar") as ScrollBar;

    This code will throw an exception of index out of range.

    I wish someone will look at this as this will provide a comphensive solution to this SilverLight ScrollViewer limitation.

    Thanks

    Monday, December 1, 2008 9:17 AM
  • Thanks to all for your help. After a goodnight sleep, I have finally been able to piece together the code that takes care of the thumbs moving as well as when buttons are pressed. The only area that I have a kludge is when SilverLight has loaded all elements. I added a handler to a button on my screen that when pressed, makes the call to GetScrollBars with my ScrollViewer reference. It is then able to find the ScrollBars.

    Hooking into the Loaded event or making a call to UpdateLayout() does not work.

    I faced a similar problem with WPF also. I still don't know which event to hook that will tell that SilverLight is done for sure.

    ----------------

    void GetScrollBars(DependencyObject parent)

    {

    int count = VisualTreeHelper.GetChildrenCount(parent);for (int i = 0; i < count; i++)

    {

    DependencyObject child = VisualTreeHelper.GetChild(parent, i);

    ScrollBar scrollBar = child as ScrollBar;if (scrollBar == null)

    {

    GetScrollBars(child);

    }

    else

    {

    string name = scrollBar.Name;if (name == "VerticalScrollBar")

    {

    scrollBar.Scroll +=
    new ScrollEventHandler(VerticalScrollBar_Scroll);

    }

    else if (name == "HorizontalScrollBar")

    {

    scrollBar.Scroll +=
    new ScrollEventHandler(HorizontalScrollBar_Scroll);

    }

    }

    }

    }

    private void VerticalScrollBar_Scroll(Object sender, EventArgs e)

    {

    ScrollBar scrollBar = sender as ScrollBar;Message_Diagnostics.Text = "VerticalScrollBar:" + scrollBar.Value.ToString();

    }

    private void HorizontalScrollBar_Scroll(Object sender, EventArgs e)

    {

    ScrollBar scrollBar = sender as ScrollBar;

    Message_Diagnostics.Text = "HorizonatalScrollBar:" + scrollBar.Value.ToString();

    }

     

    Monday, December 1, 2008 10:37 AM
  • This link discusses when SilverLight is done loading etc. http://blogs.msdn.com/silverlight_sdk/archive/2008/10/24/loaded-event-timing-in-silverlight.aspx

    After adding the following I removed the kludge that I mentioned in my previous email.

    ......... 

    bool
    result = SurfaceScrollViewer.ApplyTemplate(); Loaded += new RoutedEventHandler(DesignerSurface_Loaded);

    }

    void DesignerSurface_Loaded(object sender, RoutedEventArgs e)

    {

    GetScrollBars(SurfaceScrollViewer);

    }

    Monday, December 1, 2008 10:53 AM
  • I also had a problem with the index out of range exception

     This next lines has to be moved, I am running them in

    private void StackPanel_SizeChanged(Object sender, SizeChangedEventArgs e)

    {

    hBar = ((FrameworkElement)VisualTreeHelper.GetChild(ScrollViewerHorizontalGraph, 0)).FindName("VerticalScrollBar") as System.Windows.Controls.Primitives.ScrollBar;

    hBar.Scroll += new ScrollEventHandler(HorizontalScrollBarScrolled);

    }

     Then you will not get the index out of range exception

     In another sample i run them in a

    System.Windows.Threading.DispatcherTimer

    Olav

     

    Tuesday, December 2, 2008 12:51 AM
  • You can get scroll event from ScrollViewer scrollbar. Try this

     hBar = ((FrameworkElement)VisualTreeHelper.GetChild(ScrollViewerHorizontalGraph, 0)).FindName("VerticalScrollBar") as System.Windows.Controls.Primitives.ScrollBar;

    hBar.Scroll += new ScrollEventHandler(HorizontalScrollBarScrolled);

    Then you get a scroll event

    Sadly, this method no longer works in the release version of Silverlight.  That's because they encapsulate the Scroll Viewer class, and you can no longer get a reference to any of its consituent controls.

    Saturday, February 7, 2009 1:13 PM
  • Hi,

    Use SizeChanged event. VisualTreeHelper can only get reference to a child element when layout rendering is completed.

    Wednesday, October 14, 2009 6:23 AM