Jihoon Yoon
Jihoon Yoon

Reputation: 37

WPF overriding ScrollVIewer in Listbox

I made a class named "AniScrollViewer," it can set the vertical scroll offset
to animate Scrollviewer code-behind.

public class AniScrollViewer : ScrollViewer {
    public static readonly DependencyProperty CurrentVerticalOffsetProperty =
        DependencyProperty.Register("CurrentVerticalOffset", typeof(double), typeof(AniScrollViewer),
        new PropertyMetadata(new PropertyChangedCallback(OnVerticalChanged)));

    public double CurrentVerticalOffset {
        get { return (double)GetValue(CurrentVerticalOffsetProperty); }
        set { SetValue(CurrentVerticalOffsetProperty, value); }
    }

    private static void OnVerticalChanged(DependencyObject property, DependencyPropertyChangedEventArgs e) {
        AniScrollViewer viewer = property as AniScrollViewer;
        viewer.ScrollToVerticalOffset((double)e.NewValue);
    }
}

I want to push this 'AniScrollViewer' into a Listbox

I meet couple of problems:

Listbox listbox (this is already definded in XAML)
AniScrollViewer scrollviewer = listbox.~~~~;

Please help.

Upvotes: 1

Views: 2191

Answers (1)

Viv
Viv

Reputation: 17380

  • To replace the default ScrollViewer with a custom one, Define a custom Style for the ListBox and in the ControlTemplate switch out the default ScrollViewer with your one(local:AniScrollViewer). Now any ListBox using this Style will have your ScrollViewer. Default ControlTemplate and Style can be found Here
  • To get the ScrollViewer in the code-behind, you can use this as a reference. In the function

GetScrollViewer(DependencyObject o)

check it to be type safe with your custom ScrollViewer such as

if (o is AniScrollViewer)
  return o;

Alternate

However in your case, am not sure why your opting to subclass the ScrollViewer for this. Instead of the hassle of this with defining your own ListBox ControlTemplate, maybe try using an attached property. Something like:

public class ScrollAnimator {
  public static readonly DependencyProperty ScrollToProperty =
    DependencyProperty.RegisterAttached(
      "ScrollTo",
      typeof(double),
      typeof(ScrollAnimator),
      new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.None, ScrollToChangedCallback));

  private static void ScrollToChangedCallback(
    DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) {
    ScrollViewer viewer = GetScrollViewer(dependencyObject) as ScrollViewer;
    if (viewer != null)
      viewer.ScrollToVerticalOffset((double)dependencyPropertyChangedEventArgs.NewValue);

    // Modify Above code to however you want to do the animation.
  }

  public static DependencyObject GetScrollViewer(DependencyObject o) {
    if (o is ScrollViewer)
      return o;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) {
      var child = VisualTreeHelper.GetChild(o, i);
      var result = GetScrollViewer(child);
      if (result == null)
        continue;
      return result;
    }
    return null;
  }

  public static void SetScrollTo(UIElement element, Orientation value) {
    element.SetValue(ScrollToProperty, value);
  }

  public static Orientation GetScrollTo(UIElement element) {
    return (Orientation)element.GetValue(ScrollToProperty);
  }
}

and usage:

<ListBox ItemsSource="{Binding Items}"
         local:ScrollAnimator.ScrollTo="{Binding ScrollTo}" />

This way you don't have the need to define custom ControlTemplate's and all the overhead that come with it.

Upvotes: 1

Related Questions