列表框列表轮换

时间:2009-09-22 21:14:45

标签: wpf listbox

我想创建一个基本上显示循环列表的ListBox。例如,包含三个项目的列表:

1
2
3 <- (Selector on item 3)
在传递第3项后,

将旋转列表并使选择器超过项目1:

2
3
1 <- (Selector on item 1)

这类似于Windows Media Center的菜单范例。

我应该调整ListBox的哪一部分?

1 个答案:

答案 0 :(得分:1)

好吧,我更新了这些项目...不确定这是正确的方法,但是,无论如何,如果你想看一下我的实现......这里是:

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using ScrollLimit;
using System.Windows.Controls.Primitives;
using System.Text.RegularExpressions;


namespace ScrollLimit
{
  public enum ScrollLimitBehaviour { Springs, Circular };
}



namespace SmoothScroll
{

[TemplatePart(Name = "PART_Border", Type = typeof(Border))]
[TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))]
[TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))]
public class SmoothScrollViewer : ListBox
{
    private double move_init, scroll_init, scroll_offset, scroll_t0, scroll_span, scroll_v0, scroll_offset0, backOffset, adjustLimit, stopWindowCoord, hitOffset;
    private bool scroll_mouseDown = false, scroll_direction = false, scroll_out = false, scrollLessThanZero = false, scrollMoreThanEnd = false, selectionAllowed = false, canUpdate = true, external = true;
    private DispatcherTimer scroll_timerClock = new DispatcherTimer();
    private EventHandler decelerateEventHandler, adjustEventHandler;
    private int selectedIndex;
    private ScrollViewer sv;
    private Rectangle stopWindow;
    private StackPanel sp;
    private ListBoxItem lbi;
    private Grid g;
    private TextBox filterEdit;








    //proprietatea de orientare a listei
    public static readonly DependencyProperty OrientationProperty =
        DependencyProperty.Register("Orientation",
                                    typeof(Orientation),
                                    typeof(SmoothScrollViewer),
                                    new UIPropertyMetadata(Orientation.Vertical,
                                        new PropertyChangedCallback(OnOrientationChanged),
                                        new CoerceValueCallback(OnCoerceOrientation))
                                   );



    private static object OnCoerceOrientation(DependencyObject o, Object value)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) return ssw.OnCoerceOrientation((Orientation)value);
        else return value;
    }



    private static void OnOrientationChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) ssw.OnOrientationChanged((Orientation)e.OldValue, (Orientation)e.NewValue);
    }



    protected virtual Orientation OnCoerceOrientation(Orientation value)
    {
        return value;
    }



    protected virtual void OnOrientationChanged(Orientation oldValue, Orientation newValue)
    {
    }



    public Orientation Orientation
    {
        get
        {
            return (Orientation)GetValue(OrientationProperty);
        }
        set
        {
            SetValue(OrientationProperty, value);
        }
    }













    //proprietatea de multiplicator de deplasament a chenarului de oprire a elementului evidentiat
    public static readonly DependencyProperty StopWindowOffsetProperty =
        DependencyProperty.Register("StopWindowOffset",
                                    typeof(double),
                                    typeof(SmoothScrollViewer),
                                    new UIPropertyMetadata(-1.0,
                                        new PropertyChangedCallback(OnStopWindowOffsetChanged),
                                        new CoerceValueCallback(OnCoerceStopWindowOffset))
                                   );



    private static object OnCoerceStopWindowOffset(DependencyObject o, Object value)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) return ssw.OnCoerceStopWindowOffset((double)value);
        else return value;
    }



    private static void OnStopWindowOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) ssw.OnStopWindowOffsetChanged((double)e.OldValue, (double)e.NewValue);
    }



    protected virtual double OnCoerceStopWindowOffset(double value)
    {
        return value;
    }



    protected virtual void OnStopWindowOffsetChanged(double oldValue, double newValue)
    {
    }



    public double StopWindowOffset
    {
        get
        {
            return (double)GetValue(StopWindowOffsetProperty);
        }
        set
        {
            SetValue(StopWindowOffsetProperty, value);
        }
    }






















    //proprietatea de comportament la capete
    public static readonly DependencyProperty ScrollLimitBehaviourProperty =
        DependencyProperty.Register("ScrollLimitBehaviour",
                                    typeof(ScrollLimitBehaviour),
                                    typeof(SmoothScrollViewer),
                                    new UIPropertyMetadata(ScrollLimitBehaviour.Springs,
                                        new PropertyChangedCallback(OnScrollLimitBehaviourChanged),
                                        new CoerceValueCallback(OnCoerceScrollLimitBehaviour))
                                   );



    private static object OnCoerceScrollLimitBehaviour(DependencyObject o, Object value)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) return ssw.OnCoerceScrollLimitBehaviour((ScrollLimitBehaviour)value);
        else return value;
    }



    private static void OnScrollLimitBehaviourChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) ssw.OnScrollLimitBehaviourChanged((ScrollLimitBehaviour)e.OldValue, (ScrollLimitBehaviour)e.NewValue);
    }



    protected virtual ScrollLimitBehaviour OnCoerceScrollLimitBehaviour(ScrollLimitBehaviour value)
    {
        return value;
    }



    protected virtual void OnScrollLimitBehaviourChanged(ScrollLimitBehaviour oldValue, ScrollLimitBehaviour newValue)
    {
    }



    public ScrollLimitBehaviour ScrollLimitBehaviour
    {
        get
        {
            return (ScrollLimitBehaviour)GetValue(ScrollLimitBehaviourProperty);
        }
        set
        {
            SetValue(ScrollLimitBehaviourProperty, value);
        }
    }






















    //proprietatea de existenta a filtrarii
    public static readonly DependencyProperty CanFilterProperty =
        DependencyProperty.Register("CanFilter",
                                    typeof(bool),
                                    typeof(SmoothScrollViewer),
                                    new UIPropertyMetadata(false,
                                        new PropertyChangedCallback(OnCanFilterChanged),
                                        new CoerceValueCallback(OnCoerceCanFilter))
                                   );



    private static object OnCoerceCanFilter(DependencyObject o, Object value)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) return ssw.OnCoerceCanFilter((bool)value);
        else return value;
    }



    private static void OnCanFilterChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SmoothScrollViewer ssw = o as SmoothScrollViewer;

        if (ssw != null) ssw.OnCanFilterChanged((bool)e.OldValue, (bool)e.NewValue);
    }



    protected virtual bool OnCoerceCanFilter(bool value)
    {
        return value;
    }



    protected virtual void OnCanFilterChanged(bool oldValue, bool newValue)
    {
    }



    public bool CanFilter
    {
        get
        {
            return (bool)GetValue(CanFilterProperty);
        }
        set
        {
            SetValue(CanFilterProperty, value);
        }
    }



















    //previne scroll-ul prin drag in afara listei
    protected override void OnMouseMove(MouseEventArgs e)
    {
    }


    protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e)
    {
    }










    //copie ItemsSource in Items
    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);

        if (ItemsSource != null)
        {
            ObservableCollection<object> temp = new ObservableCollection<object>();
            foreach (object o in ItemsSource) temp.Add(o);
            ItemsSource = null;
            for (int i = 0; i < temp.Count; i++) Items.Add(temp[i]);
        }
    }










    //returneaza elementul primit ca parametru ca ListBoxItem
    private ListBoxItem getListBoxItem(UIElement element)
    {
        if (element is ListBoxItem) return element as ListBoxItem;

        while (element != this && element != null)
        {
            element = VisualTreeHelper.GetParent(element) as UIElement;
            if (element is ListBoxItem) return element as ListBoxItem;
        }

        return null;
    }









    //la aplicarea sablonului vizual au loc asocierile de evenimente si initializarile
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        sv = GetTemplateChild("PART_ScrollViewer") as ScrollViewer;
        sp = GetTemplateChild("PART_StackPanel") as StackPanel;
        g = GetTemplateChild("PART_Grid") as Grid;

        if ((ScrollBarVisibility)GetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty) == ScrollBarVisibility.Disabled) sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
        if ((ScrollBarVisibility)GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty) == ScrollBarVisibility.Disabled) sv.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;


        sp.PreviewMouseDown += new MouseButtonEventHandler(l_MouseDown);
        sp.PreviewMouseMove += new MouseEventHandler(l_MouseMove);
        sp.PreviewMouseUp += new MouseButtonEventHandler(l_MouseUp);
        sp.MouseLeave += new MouseEventHandler(l_MouseLeave);
        sp.PreviewMouseWheel += new MouseWheelEventHandler(l_PreviewMouseWheel);
        sp.PreviewKeyDown += new KeyEventHandler(sp_PreviewKeyDown);


        if (Orientation == Orientation.Vertical)
        {
            sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
            HorizontalContentAlignment = HorizontalAlignment.Stretch;
        }
        else
        {
            sv.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
            VerticalContentAlignment = VerticalAlignment.Stretch;
        }




        scroll_timerClock.Interval = new TimeSpan(0, 0, 0, 0, 1);
        decelerateEventHandler = new EventHandler(scroll_timerClock_Tick);
        adjustEventHandler = new EventHandler(adjust_timerClock_Tick);
        scroll_timerClock.Tick += decelerateEventHandler;


        LayoutUpdated += new EventHandler(l_LayoutUpdated);

        PreviewMouseDown += new MouseButtonEventHandler(SmoothScrollViewer_MouseDown);
    }



    //prin interactiunea cu mouse-ul asupra listei, se initiaza o actiune de
    //selectie non-externa
    void SmoothScrollViewer_MouseDown(object sender, MouseButtonEventArgs e)
    {
        external = false;
    }













    //functie de scroll la element specificat
    public void ScrollTo(object item)
    {
        if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
        {
            int indexOfItem = Items.IndexOf(item);

            int nrItemsToMove = 0,
                i = 0;
            double cummulativeSize = 0;
            do
            {
                lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(i));
                cummulativeSize += (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
                i++;
                nrItemsToMove++;
            }
            while (cummulativeSize <= ((Orientation == Orientation.Vertical) ? sv.ViewportHeight : sv.ViewportWidth));


            if (indexOfItem > Items.Count - nrItemsToMove || indexOfItem == 0)
            {
                for (i = 0; i <= nrItemsToMove; i++)
                {
                    object elt = Items[0];
                    Items.RemoveAt(0);
                    Items.Add(elt);
                }
                indexOfItem = Items.IndexOf(item);
            }


            double scrollAmount = 0;
            for (i = 0; i < indexOfItem - 1; i++)
            {
                lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(i));
                scrollAmount += (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
            }

            if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scrollAmount);
            else sv.ScrollToHorizontalOffset(scrollAmount);


            selectionAllowed = true;
            SelectedItem = item;
            selectionAllowed = false;
            scroll_out = false;
        }
    }











    //Manipularea listei din tastele sageti
    void sp_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (((e.Key == Key.Right || e.Key == Key.Left) && Orientation == Orientation.Horizontal) || ((e.Key == Key.Up || e.Key == Key.Down) && Orientation == Orientation.Vertical))
        {
            adjustLimit = 0;
            scrollLessThanZero = false;
            scrollMoreThanEnd = false;
            SelectedIndex = -1;
            backOffset = ((Orientation == Orientation.Vertical) ? (sv.ExtentHeight - sv.ViewportHeight) : (sv.ExtentWidth - sv.ViewportWidth));
            scroll_mouseDown = false;
            scroll_v0 = (e.Key == Key.Right || e.Key == Key.Up ? -1 : 1);
            scroll_t0 = Environment.TickCount;
            scroll_direction = (scroll_v0 >= 0);
            scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);
            scroll_timerClock.IsEnabled = true;
        }
        else
            if (e.Key == Key.Return && stopWindow == null)
            {
                UIElement elt = InputHitTest(new Point(ActualWidth / 2, ActualHeight / 2)) as UIElement;
                if (elt != null)
                {
                    lbi = getListBoxItem(elt);

                    selectionAllowed = true;
                    SelectedIndex = ItemContainerGenerator.IndexFromContainer(lbi);
                }
            }
        e.Handled = true;
    }




















    //la ficare actualizare a randarii controlului, se initializeaza/redimensioneaza chenarul
    //de oprire a elementului selectabil si campul editabil de filtrare in cazul in care acesta
    //exista
    void l_LayoutUpdated(object sender, EventArgs e)
    {
        try
        {
            if (StopWindowOffset >= 0)
            {
                lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(((SelectedIndex > 0) ? SelectedIndex : selectedIndex)));
                hitOffset = (Orientation == Orientation.Vertical) ? lbi.ActualHeight / 4 : lbi.ActualWidth / 4;
                stopWindowCoord = StopWindowOffset * ((Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth);

                if (stopWindow == null)
                {
                    stopWindow = new Rectangle();
                    stopWindow.SetValue(Grid.RowProperty, 1);
                    stopWindow.HorizontalAlignment = HorizontalAlignment.Left;
                    stopWindow.VerticalAlignment = VerticalAlignment.Top;
                    stopWindow.Fill = BorderBrush;
                    stopWindow.Opacity = 0.5;
                    stopWindow.IsHitTestVisible = false;
                    g.Children.Add(stopWindow);
                }

                stopWindow.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, stopWindowCoord, 0, 0) : new Thickness(stopWindowCoord, 0, 0, 0);
                stopWindow.Width = lbi.ActualWidth;
                stopWindow.Height = lbi.ActualHeight;
            }

            if (CanFilter)
            {
                if (filterEdit == null)
                {
                    filterEdit = new TextBox();
                    filterEdit.HorizontalAlignment = HorizontalAlignment.Center;
                    filterEdit.VerticalAlignment = VerticalAlignment.Top;
                    filterEdit.Margin = new Thickness(3);
                    filterEdit.TextChanged += new TextChangedEventHandler(filterEdit_TextChanged);
                    filterEdit.PreviewKeyDown += new KeyEventHandler(filterEdit_KeyDown);
                    g.Children.Add(filterEdit);
                }
                filterEdit.Width = ActualWidth - 20;
            }
        }
        catch (Exception) { }
    }









    //La apasarea tastei sageata jos in filtru, se coboara in lista
    void filterEdit_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Down)
        {
            sp.Focusable = true;
            Keyboard.Focus(sp);
        }
    }








    //textul dupa care are loc filtrarea
    void filterEdit_TextChanged(object sender, TextChangedEventArgs e)
    {
        Items.Filter = new Predicate<object>(PassesFilter);
    }


    public bool PassesFilter(Object value)
    {
        if (filterEdit.Text != "")
        {
            Regex regex = new Regex("^" + Regex.Escape(filterEdit.Text).Replace("\\*", ".*").Replace("\\?", ".") + ".*$");
            return regex.IsMatch(extractText(value as UIElement));
        }
        else return true;
    }













    //tratarea evenimentului de selectie asupra unui element din lista
    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        //MessageBox.Show(selectedIndex+" "+SelectedIndex);
        if (!external)
        {
            //MessageBox.Show(SelectedIndex + "");
            if (!selectionAllowed)
            {
                if (SelectedIndex != -1)
                {
                    selectedIndex = SelectedIndex;
                    SelectedIndex = -1;
                }
            }
        }
        else selectionAllowed = false;
        external = true;
        base.OnSelectionChanged(e);
    }












    //Extrage textul care apare in interiorul oricarui control
    private string extractText(UIElement item)
    {
        if (item is TextBlock) return ((TextBlock)item).Text;
        if (item is TextBox) return ((TextBox)item).Text;

        if (item is ContentControl)
        {
            object content = ((ContentControl)item).Content;
            if (content is UIElement) return extractText(content as UIElement);
            else return content.ToString();
        }
        else
        {
            string result = "";
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(item); i++)
                result += extractText(VisualTreeHelper.GetChild(item, i) as UIElement) + " ";
            return result;
        }
    }















    //scroll prin rotita mouse-ului
    void l_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        sp.Focusable = true;
        Keyboard.Focus(sp);
        adjustLimit = 0;
        scrollLessThanZero = false;
        scrollMoreThanEnd = false;
        SelectedIndex = -1;
        backOffset = ((Orientation == Orientation.Vertical) ? (sv.ExtentHeight - sv.ViewportHeight) : (sv.ExtentWidth - sv.ViewportWidth));
        scroll_mouseDown = false;
        scroll_v0 = -e.Delta / 100;
        scroll_t0 = Environment.TickCount;
        scroll_direction = (scroll_v0 >= 0);
        scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);
        scroll_timerClock.IsEnabled = true;
    }











    //la parasirea controlului de catre cursorul mouse-ului, cu butonul apasat, se simuleaza
    //invocarea unui eveniment de eliberare a butonului mouse-ului
    private void l_MouseLeave(object sender, MouseEventArgs e)
    {
        if (scroll_mouseDown)
        {
            MouseButtonEventArgs e1 = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left);
            e1.RoutedEvent = MouseLeaveEvent;
            l_MouseUp(sender, e1);
        }
    }








    //scroll controlat de miscarea mouse-ului
    private void l_MouseMove(object sender, MouseEventArgs e)
    {
        if (scroll_mouseDown)
        {
            scroll_offset = scroll_init - ((Orientation == Orientation.Vertical) ? e.GetPosition(this).Y : e.GetPosition(this).X);
            if (scroll_offset < 0)
                if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, -scroll_offset, 0, 0) : new Thickness(-scroll_offset, 0, 0, 0);
                else
                {
                    if (canUpdate)
                    {
                        object elt = Items[Items.Count - 1];
                        Items.RemoveAt(Items.Count - 1);
                        Items.Insert(0, elt);

                        lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(Items.Count - 1);
                        double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
                        scroll_init += adjust;
                        canUpdate = false;
                    }
                }
            else
                if (scroll_offset - backOffset > 0)
                    if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, 0, 0, scroll_offset - backOffset) : new Thickness(0, 0, scroll_offset - backOffset, 0);
                    else
                    {
                        if (canUpdate)
                        {
                            object elt = Items[0];
                            Items.RemoveAt(0);
                            Items.Add(elt);

                            lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(0);
                            double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
                            scroll_init -= adjust;
                            canUpdate = false;
                        }
                    }
                else canUpdate = true;


            if ((Orientation == Orientation.Vertical)) sv.ScrollToVerticalOffset(scroll_offset);
            else sv.ScrollToHorizontalOffset(scroll_offset);
        }
    }












    //comportamentul la eliberarea butonului mouse-ului; daca miscarea efectuata este una
    //minora, se recurge la selectarea elementului vizat, altfel se continua cu initierea
    //miscarii inertiale induse de acceleratia impusa de miscarea pana in acest moment
    private void l_MouseUp(object sender, MouseButtonEventArgs e)
    {
        double move_offset = move_init - ((Orientation == Orientation.Vertical) ? e.GetPosition(this).Y : e.GetPosition(this).X);

        selectionAllowed = (Math.Abs(move_offset) <= 10 && SelectedIndex != selectedIndex && StopWindowOffset < 0);
        if (selectionAllowed) SelectedIndex = selectedIndex;

        adjustLimit = 0;

        if (scroll_mouseDown && move_offset != 0)
        {
            scroll_mouseDown = false;

            scroll_span = Environment.TickCount - scroll_t0;

            if (scroll_span > 0) scroll_v0 = move_offset / scroll_span;
            else scroll_v0 = 0;


            scroll_t0 = Environment.TickCount;

            scroll_direction = (move_offset >= 0);

            scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);

            scroll_timerClock.IsEnabled = true;
        }
        else
        {
            scroll_mouseDown = false;
            if (move_offset == 0) scrollStopped();
        }
    }





    //timer-ul responsabil cu actualizarea deplasamentului de derulare in urma miscarii
    //uniform incetinite de dupa eliberarea butonului mouse-ului
    private void scroll_timerClock_Tick(object sender, EventArgs e)
    {
        double scroll_a = (scroll_direction ? -1 : 1) * ((scroll_out) ? 0.005 : 0.003),
               scroll_t = Environment.TickCount - scroll_t0,
               scroll = 0.5 * scroll_a * scroll_t * scroll_t + scroll_v0 * scroll_t + scroll_offset0,
               scroll_v = scroll_a * scroll_t + scroll_v0;



        if (scroll > 0 && scroll < Math.Abs(backOffset))
            if (scroll_out)
            {
                sp.Margin = new Thickness(0, 0, 0, 0);
                scroll_out = false;
                scrollStopped();
            }
            else
            {
                if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scroll);
                else sv.ScrollToHorizontalOffset(scroll);
                if ((scroll_v <= 0) == scroll_direction) scrollStopped();
            }
        else
            if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs)
            {
                if (scroll < 0) scrollLessThanZero = true;
                else scrollMoreThanEnd = true;
                if (scrollLessThanZero && scrollMoreThanEnd)
                {
                    sp.Margin = new Thickness(0, 0, 0, 0);
                    scrollLessThanZero = false;
                    scrollMoreThanEnd = false;
                    scroll_out = false;
                    scrollStopped();
                }
                else
                {
                    scroll_out = true;
                    if (scroll > 0) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, 0, 0, scroll - backOffset) : new Thickness(0, 0, scroll - backOffset, 0);
                    else sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, -scroll, 0, 0) : new Thickness(-scroll, 0, 0, 0);
                    if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scroll);
                    else sv.ScrollToHorizontalOffset(scroll);
                }
            }
            else
            {
                scroll_out = false;
                if (scroll <= 0)
                {
                    object elt = Items[Items.Count - 1];
                    Items.RemoveAt(Items.Count - 1);
                    Items.Insert(0, elt);

                    lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(Items.Count - 1);
                    double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.A