0
votes

I'm using below code for fix my screen when i insert listboxItem.

public partial class KeepItemsInViewListBox : ListBox
    {
        private ScrollViewer ScrollViewer { get; set; }

        #region Overrides of FrameworkElement

        /// <inheritdoc />
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            if (TryFindVisualChildElement(this, out ScrollViewer scrollViewer))
            {
                this.ScrollViewer = scrollViewer;
            }
        }

        #endregion

        #region Overrides of ListView

        /// <inheritdoc />
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);

            if (this.ScrollViewer == null)
            {
                return;
            }

            double verticalOffset;
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add when e.NewItems != null:
                    // Check if insert or add
                    verticalOffset = e.NewStartingIndex < this.ScrollViewer.VerticalOffset
                      ? this.ScrollViewer.VerticalOffset + e.NewItems.Count
                      : this.ScrollViewer.VerticalOffset;
                    break;
                case NotifyCollectionChangedAction.Remove when e.OldItems != null:
                    verticalOffset = this.ScrollViewer.VerticalOffset - e.OldItems.Count;
                    break;
                default:
                    verticalOffset = this.ScrollViewer.VerticalOffset;
                    break;
            }

            this.ScrollViewer?.ScrollToVerticalOffset(verticalOffset);
        }


        #endregion

        public bool TryFindVisualChildElement<TChild>(DependencyObject parent, out TChild childElement)
          where TChild : FrameworkElement
        {
            childElement = null;
            if (parent == null)
            {
                return false;
            }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                if (child is TChild resultElement)
                {
                    childElement = resultElement;
                    return true;
                }

                if (TryFindVisualChildElement(child, out childElement))
                {
                    return true;
                }
            }

            return false;
        }
    }

that class makes my listbox's scroll to item-by-item.

see the below picture.

enter image description here

before upper Class(KeepItemsInViewListBox), my scrolling condition is Pixel. but after using that code my scrolling condition is changed.

because all of my listboxitem's size is diffrent, I added VirtualizingPanel.ScrollUnit="Pixel" or ScrollViewer.CanContentScroll = "False" in xaml, or ScrollViewer.CanContentScroll = false; in .cs file then my screen is also move...

1

1 Answers

0
votes

First thanks to Mr.Code

I modify Mr.Code's class, but my Code is not a perfect.

public class KeepItemsInViewListBox : ListBox
    {
        private ScrollViewer ScrollViewer { get; set; }

        private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {


            if (insertCheck)
            {
                ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset + e.ExtentHeightChange);
                insertCheck = false;
            }
        }

        #region Overrides of FrameworkElement

        /// <inheritdoc />
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            if (TryFindVisualChildElement(this, out ScrollViewer scrollViewer))
            {
                this.ScrollViewer = scrollViewer;
                ScrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
            }
        }

        #endregion
        private bool insertCheck = false;
        #region Overrides of ListView

        /// <inheritdoc />
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add when e.NewItems != null:
                    bool isInsert = this.ScrollViewer.CanContentScroll
                      ? e.NewStartingIndex < this.ScrollViewer.VerticalOffset
                      : e.NewStartingIndex + 1 != this.Items.Count - 1;

                    if (!isInsert)
                    {
                        return;
                    }
                    insertCheck = isInsert;
                    break;
            }
        }
        #endregion

        public bool TryFindVisualChildElement<TChild>(DependencyObject parent, out TChild childElement)
          where TChild : FrameworkElement
        {
            childElement = null;
            if (parent == null)
            {
                return false;
            }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                if (child is TChild resultElement)
                {
                    childElement = resultElement;
                    return true;
                }

                if (TryFindVisualChildElement(child, out childElement))
                {
                    return true;
                }
            }

            return false;
        }
    }

I just redefine "ScrollViewer_ScrollChanged" method and set verticalOffset to changed offset value.

It works in my test Project. but in my real Project doesn't work. Then i use ScrollViewer's ExtentHeight value. I checked the first ExtentHeight and it was saved. After scrolling, I compare two of ExtentHeight and set the diffrence to verticalOffset.

But it isn't work Beautifully