WPF - Animate ListBox.ScrollViewer.HorizontalOffset?
Solution 1
I don't think you will be able to use a WPF storyboard for the animation because storyboards animate WPF dependency properties. You will need to call ScrollViewer.ScrollToHorizontalOffset(double)
to scroll.
You could try creating a custom dependency property that calls SetHorizontalOffset in the OnDependencyPropertyChanged() function. Then you could animate this property.
public static readonly DependencyProperty ScrollOffsetProperty =
DependencyProperty.Register("ScrollOffset", typeof(double), typeof(YOUR_TYPE),
new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnScrollOffsetChanged)));
public double ScrollOffset
{
get { return (double)GetValue(ScrollOffsetProperty); }
set { SetValue(ScrollOffsetProperty, value); }
}
private static void OnScrollOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
YOUR_TYPE myObj = obj as YOUR_TYPE;
if (myObj != null)
myObj.SCROLL_VIEWER.ScrollToHorizontalOffset(myObj.ScrollOffset);
}
To get the scroll viewer you can use the VisualTreeHelper to search the visual children of the ListBox. Save a reference to the ScrollViewer because you will need it later. Try this:
public static childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
// Iterate through all immediate children
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 function returns the first visual child of the parameter type. Call FindVisualChild<ScrollViewer>(ListBox)
to get the ScrollViewer.
Finally, try using UIElement.TranslatePoint(Point, UIElement)
to get the X position of the item. Call this function on the item, pass in 0,0 for the point, and pass in the ScrollViewer.
Hope this helps.
Solution 2
I'm not sure if my method is good practice but for the limited time I had it seemed to work okay. Instead of using a story board I just used a DispatcherTimer instead.
ScrollLeftButtonCommand = new DelegateCommand(
o =>
{
var scrollViewer = (ScrollViewer)o;
scrollTimer = new DispatcherTimer();
scrollTimer.Start();
scrollTimer.Interval = TimeSpan.FromMilliseconds(30);
scrollTimer.Tick += (s, e) =>
{
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - 50);
if (scrollViewer.HorizontalOffset <= 0)
{
scrollTimer.Stop();
}
};
});
Make sure it's a DispatchTimer so the thread is able to take control of the UI element
Also remember to bind to your object in your view!
<Button CommandParameter="{Binding ElementName=MyScrollViewer }"
Command="{Binding ScrollLeftButtonCommand }"/>
Related videos on Youtube
EightyOne Unite
Updated on March 12, 2020Comments
-
EightyOne Unite about 4 years
I have a collection of
Visual
s in aListBox
. I need to find theXPosition
of an element inside it and then animate theHorizontalOffset
of theListBox
'sScrollViewer
. Essentially I want to created an animatedScrollIntoView
method.This gives me a couple of problems. Firstly, how can I get a reference to the
ListBox
s scrollviewer? Secondly, how can i get the relativeXPosition
orHozintalOfffset
of an arbitrary element in theListBox
?I'm not reponding to any input on the
ListBox
itself so I can't useMouse
related properties. -
EightyOne Unite about 15 yearsMy Lord, that's quite a job! Thanks for the help Josh, it's pointed me in the right direction at least.
-
romu over 14 yearsDo you mean ScrollToHorizontalOffset instead of SetHorizontalOffset?
-
babatto over 14 yearsGreat idea. It helped me out!
-
Josh G over 14 yearsIt's possible that this property could be an attached DependencyProperty. That would allow this code to be reused on any control that has a ScrollView in it's visual tree.
-
Flatliner DOA almost 14 yearsYou actually want to find the ItemsPresenter when translating the position of the item. e.g. var itemContainer = listBox.ItemContainerGenerator.ContainerFromItem(listBox.SelectedItem); itemContainer.TranslatePoint(new Point(0, 0), FindVisualChild<ItemsPresenter>(scrollViewer));