I really need a scroll event in the ScrollViewer to keep a few scrollviewer windows in synch. It doesn't appear that SL will ever support a Scroll event so now I'm stuck trying to write my own ScrollViewer. I figured I could do it similar to the ScrollViewer
by using a ScrollContentPresenter and a couple of ScrollBars. So far, this isn't doing the trick however. I can't get past getting the ScrollContentPresenter to do anything. Can't find any examples on how to do this either.
So could someone:
a) recommend a way to get a scroll event from the ScrollViewer?
b) Show me how to use the ScrollContentPresenter control to display something?
I use xaml like this for ScrollContentPresenter <ScrollContentPresenter Content="Some Content"/> I've also tried inserting content like <ScrollContentPresenter.Content> underneath the ScrollContentPresenter tag. Unfortunately I'm getting nowhere.
I created a custom ScrollViewer by wrapping the existing one with a custom control. I made a TemplatePart of type ScrollViewer, which my default style defined. Then I made my own corresponding "HorizontalOffset", "ScrollToVerticalOffset()", etc members
that passed on the call to the actual scroll viewer. If you did this then you could make a callback on your custom dependency properties that fires an event.
Of course, a scroll event would be much cleaner, so hopefully MSFT will implement it in a later version.
[Edit: On second thought, this probably won't work for you if you are still letting the user scroll using the scroll bars. In my case I implemented a different method of scrolling (dragging on the content to "pan") so the scroll viewer's properties
never changed except through my custom control.]
The _SkipLayoutEvent was necessary because any time I updated any other part of the UI it kicked off the ScrollViewer's LayoutUpdated event again, even if the updated UI wasn't inside the scroll viewer. I'm not sure why this happens, but if you don't account
for it you get layout error due to an infinite loop. The biggest downside to this implementation is that the event will get kicked off for a lot of things besides scrolling, but it does at least get fired off every time the user scrolls.
Very happy to say that I have got something that works well for me and I hope others will be able to use it too! Use a class called VisualTreeHelper.GetChild to get a reference to the first child in the ScrollViewer, cast it to a frameworkelement or whatever
to call FindName on it, then use FindName to do a search for "VerticalScrollBar" or "HorizontalScrollBar". From there, listen to the scroll event.
Make sure you do it from the LayoutUpdated event on the scrollviewer or after so that the VisualTreeHelper class picks up the children. I found that before the layout_udpated event it did not.
I think the cleanest way is to use the VisualTreeExtensions from the Toolkit's
System.Windows.Controls.Toolkit assembly along with some
LINQ.
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
// ...private void SomeMethod()
{
var myScrollBar = myControl.GetVisualDescendants()
.OfType<ScrollBar>()
.Where(foundScrollBar => foundScrollBar.Orientation == Orientation.Vertical /* or do we need Horizontal? */)
.FirstOrDefault();
if (myScrollBar != null)
{
myScrollBar.Scroll += this.myScrollBar_Scroll;
}
}
Look ma, no magic strings used, but we must be aware that the result can still be
null if the current style of our control has no scrollbars defined.
...: Between the iron gates of fate, the seeds of time were sown :...
Make sure you do it from the LayoutUpdated event on the scrollviewer or after so that the VisualTreeHelper class picks up the children. I found that before the layout_udpated event it did not.
Thanks for posting your solution. Now I find that LayoutUpdate is getting called frequently so I detach and reattach the event handlers like so:
void scrollViewer_LayoutUpdated(object sender, EventArgs e) { if (this.mouseIsOverScrollViewer) { layoutCounter++;
Debug.WriteLine("scrollViewer_LayoutUpdated fired this number of times (" + layoutCounter + ")"); FrameworkElement test = VisualTreeHelper.GetChild(this.scrollViewer, 0) as FrameworkElement;
System.Windows.Controls.Primitives.ScrollBar sbH = test.FindName("HorizontalScrollBar") as System.Windows.Controls.Primitives.ScrollBar; sbH.Scroll -= new System.Windows.Controls.Primitives.ScrollEventHandler(sbH_Scroll); sbH.Scroll += new System.Windows.Controls.Primitives.ScrollEventHandler(sbH_Scroll);
System.Windows.Controls.Primitives.ScrollBar sbV = test.FindName("VerticalScrollBar") as System.Windows.Controls.Primitives.ScrollBar; sbV.Scroll -= new System.Windows.Controls.Primitives.ScrollEventHandler(sbV_Scroll); sbV.Scroll += new System.Windows.Controls.Primitives.ScrollEventHandler(sbV_Scroll); } }
In an attempt to limit this, I at least check to see if the mouse is over the ScrollViewer. It would be that much cleaner if we could attach events directly to the scrollbars without having to depend on LayoutMethod.
jpsscott
Member
54 Points
97 Posts
How to get a Scroll event from a ScrollViewer
Jun 20, 2008 07:24 PM | LINK
Hey,
I really need a scroll event in the ScrollViewer to keep a few scrollviewer windows in synch. It doesn't appear that SL will ever support a Scroll event so now I'm stuck trying to write my own ScrollViewer. I figured I could do it similar to the ScrollViewer by using a ScrollContentPresenter and a couple of ScrollBars. So far, this isn't doing the trick however. I can't get past getting the ScrollContentPresenter to do anything. Can't find any examples on how to do this either.
So could someone:
a) recommend a way to get a scroll event from the ScrollViewer?
b) Show me how to use the ScrollContentPresenter control to display something?
I use xaml like this for ScrollContentPresenter <ScrollContentPresenter Content="Some Content"/> I've also tried inserting content like <ScrollContentPresenter.Content> underneath the ScrollContentPresenter tag. Unfortunately I'm getting nowhere.
Please advise...
James
dcstraw
Member
293 Points
103 Posts
Re: How to get a Scroll event from a ScrollViewer
Jun 20, 2008 08:38 PM | LINK
I created a custom ScrollViewer by wrapping the existing one with a custom control. I made a TemplatePart of type ScrollViewer, which my default style defined. Then I made my own corresponding "HorizontalOffset", "ScrollToVerticalOffset()", etc members that passed on the call to the actual scroll viewer. If you did this then you could make a callback on your custom dependency properties that fires an event.
Of course, a scroll event would be much cleaner, so hopefully MSFT will implement it in a later version.
[Edit: On second thought, this probably won't work for you if you are still letting the user scroll using the scroll bars. In my case I implemented a different method of scrolling (dragging on the content to "pan") so the scroll viewer's properties never changed except through my custom control.]
dcstraw
Member
293 Points
103 Posts
Re: How to get a Scroll event from a ScrollViewer
Jun 20, 2008 09:02 PM | LINK
OK I thought of a different way but it's kind of ugly. You can handle the LayoutUpdated event of the scroll viewer like this:
void ScrollViewer_LayoutUpdated(object sender, EventArgs e)
{
if (!_SkipLayoutEvent)
{
_SkipLayoutEvent = true;
OnScroll();
}
else
{
_SkipLayoutEvent = false;
}
}
The _SkipLayoutEvent was necessary because any time I updated any other part of the UI it kicked off the ScrollViewer's LayoutUpdated event again, even if the updated UI wasn't inside the scroll viewer. I'm not sure why this happens, but if you don't account for it you get layout error due to an infinite loop. The biggest downside to this implementation is that the event will get kicked off for a lot of things besides scrolling, but it does at least get fired off every time the user scrolls.
jpsscott
Member
54 Points
97 Posts
Re: How to get a Scroll event from a ScrollViewer
Jun 23, 2008 07:22 PM | LINK
Alright,
Very happy to say that I have got something that works well for me and I hope others will be able to use it too! Use a class called VisualTreeHelper.GetChild to get a reference to the first child in the ScrollViewer, cast it to a frameworkelement or whatever to call FindName on it, then use FindName to do a search for "VerticalScrollBar" or "HorizontalScrollBar". From there, listen to the scroll event.
Make sure you do it from the LayoutUpdated event on the scrollviewer or after so that the VisualTreeHelper class picks up the children. I found that before the layout_udpated event it did not.
James
jitendrachhajed
Member
2 Points
2 Posts
Re: How to get a Scroll event from a ScrollViewer
Sep 09, 2009 10:33 AM | LINK
Hi jpscott,
If possible please post the code snippet that your are using.
Thanks,
Jitendra Chhajed
mepfuso
Contributor
2055 Points
401 Posts
Re: How to get a Scroll event from a ScrollViewer
May 05, 2010 05:32 PM | LINK
I think the cleanest way is to use the VisualTreeExtensions from the Toolkit's System.Windows.Controls.Toolkit assembly along with some LINQ.
Look ma, no magic strings used, but we must be aware that the result can still be null if the current style of our control has no scrollbars defined.
beaudet
Member
17 Points
23 Posts
Re: How to get a Scroll event from a ScrollViewer
Jun 04, 2010 04:28 PM | LINK
Thanks for posting your solution. Now I find that LayoutUpdate is getting called frequently so I detach and reattach the event handlers like so:
In an attempt to limit this, I at least check to see if the mouse is over the ScrollViewer. It would be that much cleaner if we could attach events directly to the scrollbars without having to depend on LayoutMethod.
Thanks for the (indirect) help. ;)
kranthi.gull...
Member
491 Points
80 Posts
Re: How to get a Scroll event from a ScrollViewer
Aug 10, 2010 05:48 AM | LINK
Refer to the below blog post for a sample project containing the code for handling scroll event for scrollviewer
http://dotplusnet.blogspot.com/2010/05/scrollviewer-scroll-change-event-in.html
Please remember to mark the replies as answers if they help and unmark them if they provide no help.