ScrollIntoView - элемент не должен исчезать из вида - PullRequest
2 голосов
/ 18 июля 2011

С самого начала я хочу сказать, что наградите кого-нибудь, кто может помочь мне с моей проблемой, наградить 200.

Это мой простой код (C # с WPF):

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    /// 

    public partial class Window1 : Window
    {
        string fixedItem;

        public Window1()
        {
            InitializeComponent();
            listBox1.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(list_changes);

        }



        private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
        {
            listBox1.ScrollIntoView(fixedItem);
        }

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {      
            fixedItem = (string)listBox1.SelectedItem;        
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add("item0");
            listBox1.Items.Add("item1");
            listBox1.Items.Add("item2");
            listBox1.Items.Add("item3");
            listBox1.Items.Add("item4");
            listBox1.Items.Add("item5");
            listBox1.Items.Add("item6");

        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Insert(0, "item7");
            listBox1.Items.Insert(0, "item8");
            listBox1.Items.Insert(0, "item9");
            listBox1.Items.Insert(0, "item10");
            listBox1.Items.Insert(0, "item11");
            listBox1.Items.Insert(0, "item12");
            listBox1.Items.Insert(0, "item13");
            listBox1.Items.Insert(0, "item14");
            listBox1.Items.Insert(0, "item15");
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Insert(0, "item16");
            listBox1.Items.Insert(0, "item17");
            listBox1.Items.Insert(0, "item18");
            listBox1.Items.Insert(0, "item19");
            listBox1.Items.Insert(0, "item20");
            listBox1.Items.Insert(0, "item21");
            listBox1.Items.Insert(0, "item22");
            listBox1.Items.Insert(0, "item23");
            listBox1.Items.Insert(0, "item24");
        }
    }
}
* 1005Сначала я сделал 3 кнопки, чтобы вставить текст в список.Допустим, я нажимаю button1 и button2, у меня будет этот список:
item15
item14
item13
item12
......
item7
item0
item1
.....
item6

После этого я хочу нажать на «item12», затем, когда я нажимаю button3, я хочу, чтобы мой «item12» оставался в том же состоянии.spot, пока текст генерируется (4-ая позиция в списке).
Короче говоря, каждый раз, когда я нажимаю на элемент, я хочу, чтобы он оставался в точно такой же позиции при создании текста.

Так у кого-нибудь есть идеи, как это сделать?Нужно ли использовать объект ScrollViewer для работы с VerticallOffset и ViewportHeigth?Этот простой код, который я разместил, когда я нажимаю на элемент, а затем генерирую текст, перемещает элемент внизу (видимая позиция) и остается там после.Но я вообще не хочу его перемещать.

Редактировать:

Хорошо, я попробовал этот код с здесь :

FrameworkElement container = listRadioItems.ItemContainerGenerator.ContainerFromItem(fixedItem) as FrameworkElement;

            if (null != container)
            {
                if (ScrollViewer.GetCanContentScroll(listBox))
                {
                    IScrollInfo scrollInfo = VisualTreeHelper.GetParent(container) as IScrollInfo;
                    if (null != scrollInfo)
                    {
                        StackPanel stackPanel = scrollInfo as StackPanel;
                        VirtualizingStackPanel virtualizingStackpanel = scrollInfo as VirtualizingStackPanel;

                        int index = listBox.ItemContainerGenerator.IndexFromContainer(container);

                        if (((null != stackPanel) && (Orientation.Horizontal == stackPanel.Orientation)) || ((null != virtualizingStackpanel) && (Orientation.Horizontal == virtualizingStackpanel.Orientation)))
                        {
                            scrollInfo.SetHorizontalOffset(index - Math.Floor(scrollInfo.ViewportWidth / 2));
                        }
                        else
                        {
                            scrollInfo.SetVerticalOffset(index - Math.Floor(scrollInfo.ViewportHeight / 2));
                        }
                    }
                }
                else
                {
                    Rect rect = new Rect(new Point(), container.RenderSize);

                    FrameworkElement constrainingParent = container;
                    do
                    {
                        constrainingParent = VisualTreeHelper.GetParent(constrainingParent) as FrameworkElement;
                    } while ((null != constrainingParent) && (listBox != constrainingParent) && !(constrainingParent is ScrollContentPresenter));

                    if (null != constrainingParent)
                    {
                        rect.Inflate(Math.Max((constrainingParent.ActualWidth - rect.Width) / 2, 0), Math.Max((constrainingParent.ActualHeight - rect.Height) / 2, 0));
                    }

                    container.BringIntoView(rect);
                }
            }

Чтодля меня это значит, что он центрирует выбранный элемент, но свиток уменьшается, только иногда он также центрируется.И моя проблема в том, что опять же, только иногда выбранный элемент исчезает из поля зрения.

Что было бы замечательно, если бы я мог сделать и элемент, и прокрутку по центру.Но в первую очередь предмет не должен исчезать из виду.

Ответы [ 6 ]

2 голосов
/ 20 июля 2011

Вот некоторый код, который получает первый видимый элемент в вашем ListBox, добавляет элементы к любому указанному вами индексу, а затем использует Dispatcher для прокрутки списка обратно к первому видимому элементу после того, как все новые элементы были отображены.*

Метод расширения ScrollIntoViewTop() - это тот же метод, что и ссылка, которую вы разместили в исходном вопросе, но я изменил его, чтобы оставить элемент в верхней части списка.

Метод WPFHelpers.IsObjectVisibleInContainer()это тот, который я использовал в прошлом, чтобы проверить, является ли объект полностью или частично видимым в другом контейнере.Чтобы получить первый видимый элемент, я просто перебираю элементы ListBox, получаю контейнер ListBoxItem, связанный с каждым элементом, а затем проверяю, является ли этот контейнер видимым или нет.Первый, который возвращает true - это первый видимый элемент в ListBox.

Вот полный код:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item0");
        listBox1.Items.Insert(0, "item1");
        listBox1.Items.Insert(0, "item2");
        listBox1.Items.Insert(0, "item3");
        listBox1.Items.Insert(0, "item4");
        listBox1.Items.Insert(0, "item5");
        listBox1.Items.Insert(0, "item6");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private void button2_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item7");
        listBox1.Items.Insert(0, "item8");
        listBox1.Items.Insert(0, "item9");
        listBox1.Items.Insert(0, "item10");
        listBox1.Items.Insert(0, "item11");
        listBox1.Items.Insert(0, "item12");
        listBox1.Items.Insert(0, "item13");
        listBox1.Items.Insert(0, "item14");
        listBox1.Items.Insert(0, "item15");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private void button3_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item16");
        listBox1.Items.Insert(0, "item17");
        listBox1.Items.Insert(0, "item18");
        listBox1.Items.Insert(0, "item19");
        listBox1.Items.Insert(0, "item20");
        listBox1.Items.Insert(0, "item21");
        listBox1.Items.Insert(0, "item22");
        listBox1.Items.Insert(0, "item23");
        listBox1.Items.Insert(0, "item24");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private object GetFirstVisibleItem(ListBox listBox)
    {
        foreach (var item in listBox.Items)
        {
            var itemContainer = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item);

            if (WPFHelpers.IsObjectVisibleInContainer(itemContainer, listBox) == ControlVisibility.Full)
            {
                return item;
            }
        }

        return null;
    }
}

public enum ControlVisibility
{
    Hidden,
    Partial,
    Full,
    FullHeightPartialWidth,
    FullWidthPartialHeight
}

public class WPFHelpers
{
    /// <summary>
    /// Checks to see if an object is rendered visible within a parent container
    /// </summary>
    /// <param name="child">UI element of child object</param>
    /// <param name="parent">UI Element of parent object</param>
    /// <returns>ControlVisibility Enum: Hidden, Partial or Visible</returns>
    public static ControlVisibility IsObjectVisibleInContainer(FrameworkElement child, UIElement parent)
    {
        GeneralTransform childTransform = child.TransformToAncestor(parent);
        //Rect childSize = childTransform.TransformBounds(new Rect(new Point(0, 0), child.RenderSize));
        Rect childSize = childTransform.TransformBounds(new Rect(new Point(0, 0), new Point(child.ActualWidth, child.ActualHeight)));

        Rect result = Rect.Intersect(new Rect(new Point(0, 0), parent.RenderSize), childSize);
        if (result == Rect.Empty)
        {
            return ControlVisibility.Hidden;
        }
        if (result.Height == childSize.Height && result.Width == childSize.Width)
        {
            return ControlVisibility.Full;
        }
        if (result.Height == childSize.Height)
        {
            return ControlVisibility.FullHeightPartialWidth;
        }
        if (result.Width == childSize.Width)
        {
            return ControlVisibility.FullWidthPartialHeight;
        }
        return ControlVisibility.Partial;
    }
}

/// <summary>
/// Class implementing helpful extensions to ListBox.
/// </summary>
public static class ListBoxExtensions
{
    /// <summary>
    /// Causes the object to scroll into view centered.
    /// </summary>
    /// <param name="listBox">ListBox instance.</param>
    /// <param name="item">Object to scroll.</param>
    //[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
    //    Justification = "Deliberately targeting ListBox.")]
    public static void ScrollIntoViewTop(this ListBox listBox, object item)
    {
        Debug.Assert(!VirtualizingStackPanel.GetIsVirtualizing(listBox),
            "VirtualizingStackPanel.IsVirtualizing must be disabled for ScrollIntoViewCentered to work.");

        // Get the container for the specified item
        var container = listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
        if (null != container)
        {
            if (ScrollViewer.GetCanContentScroll(listBox))
            {
                // Get the parent IScrollInfo
                var scrollInfo = VisualTreeHelper.GetParent(container) as IScrollInfo;
                if (null != scrollInfo)
                {
                    // Need to know orientation, so parent must be a known type
                    var stackPanel = scrollInfo as StackPanel;
                    var virtualizingStackPanel = scrollInfo as VirtualizingStackPanel;
                    Debug.Assert((null != stackPanel) || (null != virtualizingStackPanel),
                        "ItemsPanel must be a StackPanel or VirtualizingStackPanel for ScrollIntoViewCentered to work.");

                    // Get the container's index
                    var index = listBox.ItemContainerGenerator.IndexFromContainer(container);

                    // Center the item by splitting the extra space
                    if (((null != stackPanel) && (Orientation.Horizontal == stackPanel.Orientation)) ||
                        ((null != virtualizingStackPanel) && (Orientation.Horizontal == virtualizingStackPanel.Orientation)))
                    {
                        //scrollInfo.SetHorizontalOffset(index - Math.Floor(scrollInfo.ViewportWidth / 2));
                        scrollInfo.SetHorizontalOffset(index);
                    }
                    else
                    {
                        //scrollInfo.SetVerticalOffset(index - Math.Floor(scrollInfo.ViewportHeight / 2));
                        scrollInfo.SetVerticalOffset(index);
                    }
                }
            }
            else
            {
                // Get the bounds of the item container
                var rect = new Rect(new Point(), container.RenderSize);

                // Find constraining parent (either the nearest ScrollContentPresenter or the ListBox itself)
                FrameworkElement constrainingParent = container;
                do
                {
                    constrainingParent = VisualTreeHelper.GetParent(constrainingParent) as FrameworkElement;
                } while ((null != constrainingParent) &&
                         (listBox != constrainingParent) &&
                         !(constrainingParent is ScrollContentPresenter));

                if (null != constrainingParent)
                {
                    // Inflate rect to fill the constraining parent
                    rect.Inflate(
                        Math.Max((constrainingParent.ActualWidth - rect.Width) / 2, 0),
                        Math.Max((constrainingParent.ActualHeight - rect.Height) / 2, 0));
                }

                // Bring the (inflated) bounds into view
                container.BringIntoView(rect);
            }
        }
    }
}
1 голос
/ 21 июля 2011

Ниже работает код, просто удерживайте его, пока вы вручную не измените выбранный элемент.

код XAML

 <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="189*" VirtualizingStackPanel.IsVirtualizing="False"  VirtualizingStackPanel.VirtualizationMode="Recycling"/>
            <RowDefinition Height="72*" />
        </Grid.RowDefinitions>
        <ListBox x:Name="listBox1" Grid.Row="0" />
        <Button Click="Button_Click"  Grid.Row="1" />
    </Grid>

C # код

  private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (listBox1.Items.Count <= 0)
            {
                for (int i = 0; i < 25; i++)
                {
                    ListBoxItem item = new ListBoxItem();
                    item.Content = "Content " + i;
                    listBox1.Items.Insert(i, item);
                  //  listBox1.SelectedItem = item;
                }
                listBox1.SelectedItem = listBox1.Items[12];
            }
            else
            {
                ListBoxItem item = new ListBoxItem();
                item.Content = "Content " + listBox1.Items.Count;
                listBox1.Items.Insert(listBox1.Items.Count, item);
              //  listBox1.SelectedItem = item;
            }

            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoView(listBox1.SelectedItem);
                }));

        }
1 голос
/ 18 июля 2011

Я сделал несколько изменений в вашем коде, пожалуйста, посмотрите.

Перед добавлением элементов на кнопку 2 и кнопку 3 нажмите ... я получаю фиксированный элемент и индекс фиксированного элемента и переставляю фиксированный элемент после добавленияitems ....

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        string fixedItem;
        public Window1()
        {
            InitializeComponent();
            listBox1.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(list_changes);
        }



        private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
        {
            listBox1.ScrollIntoView(fixedItem);
        }

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //fixedItem = (string)listBox1.SelectedItem;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add("item0");
            listBox1.Items.Add("item1");
            listBox1.Items.Add("item2");
            listBox1.Items.Add("item3");
            listBox1.Items.Add("item4");
            listBox1.Items.Add("item5");
            listBox1.Items.Add("item6");

        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            fixedItem = (string)listBox1.SelectedItem;
            int selectedIndex = listBox1.SelectedIndex;

            listBox1.Items.Insert(0, "item7");
            listBox1.Items.Insert(0, "item8");
            listBox1.Items.Insert(0, "item9");
            listBox1.Items.Insert(0, "item10");
            listBox1.Items.Insert(0, "item11");
            listBox1.Items.Insert(0, "item12");
            listBox1.Items.Insert(0, "item13");
            listBox1.Items.Insert(0, "item14");
            listBox1.Items.Insert(0, "item15");

            listBox1.Items.Remove(fixedItem);
            listBox1.Items.Insert(selectedIndex, fixedItem);
            listBox1.SelectedItem = fixedItem;
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            fixedItem = (string)listBox1.SelectedItem;
            int selectedIndex = listBox1.SelectedIndex;

            listBox1.Items.Insert(0, "item16");
            listBox1.Items.Insert(0, "item17");
            listBox1.Items.Insert(0, "item18");
            listBox1.Items.Insert(0, "item19");
            listBox1.Items.Insert(0, "item20");
            listBox1.Items.Insert(0, "item21");
            listBox1.Items.Insert(0, "item22");
            listBox1.Items.Insert(0, "item23");
            listBox1.Items.Insert(0, "item24");

            listBox1.Items.Remove(fixedItem);
            listBox1.Items.Insert(selectedIndex, fixedItem);
            listBox1.SelectedItem = fixedItem;
        }
    }
}

Обновление с типом ссылки: я создал класс с именем item .. вместо добавления строки я добавляю item ...

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        Item fixedItem;
        int selectedIndex;
        public Window1()
        {
            InitializeComponent();
            listBox1.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(list_changes);
        }



        private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
        {

        }

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add(new Item { ItemName = "item0" });
            listBox1.Items.Add(new Item { ItemName = "item1" });
            listBox1.Items.Add(new Item { ItemName = "item2" });
            listBox1.Items.Add(new Item { ItemName = "item3" });
            listBox1.Items.Add(new Item { ItemName = "item4" });
            listBox1.Items.Add(new Item { ItemName = "item5" });
            listBox1.Items.Add(new Item { ItemName = "item6" });
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {

            if (listBox1.SelectedItem != null)
            {
                fixedItem = (Item)listBox1.SelectedItem;
                selectedIndex = listBox1.SelectedIndex;
            }
            listBox1.Items.Insert(0, new Item { ItemName = "item7" });
            listBox1.Items.Insert(0, new Item { ItemName = "item8" });
            listBox1.Items.Insert(0, new Item { ItemName = "item9" });
            listBox1.Items.Insert(0, new Item { ItemName = "item10" });
            listBox1.Items.Insert(0, new Item { ItemName = "item11" });
            listBox1.Items.Insert(0, new Item { ItemName = "item12" });
            listBox1.Items.Insert(0, new Item { ItemName = "item13" });
            listBox1.Items.Insert(0, new Item { ItemName = "item14" });
            listBox1.Items.Insert(0, new Item { ItemName = "item15" });

            listBox1.Items.Remove(fixedItem);
            listBox1.Items.Insert(selectedIndex, fixedItem);
            listBox1.SelectedItem = fixedItem;
            listBox1.ScrollIntoView(fixedItem);
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            if (listBox1.SelectedItem != null)
            {
                fixedItem = (Item)listBox1.SelectedItem;
                selectedIndex = listBox1.SelectedIndex;
            }

            listBox1.Items.Insert(0, new Item { ItemName = "item16" });
            listBox1.Items.Insert(0, new Item { ItemName = "item17" });
            listBox1.Items.Insert(0, new Item { ItemName = "item18" });
            listBox1.Items.Insert(0, new Item { ItemName = "item19" });
            listBox1.Items.Insert(0, new Item { ItemName = "item20" });
            listBox1.Items.Insert(0, new Item { ItemName = "item21" });
            listBox1.Items.Insert(0, new Item { ItemName = "item22" });
            listBox1.Items.Insert(0, new Item { ItemName = "item23" });
            listBox1.Items.Insert(0, new Item { ItemName = "item24" });

            listBox1.Items.Remove(fixedItem);
            listBox1.Items.Insert(selectedIndex, fixedItem);
            listBox1.SelectedItem = fixedItem;
            listBox1.ScrollIntoView(fixedItem);
        }
    }

    class Item
    {
        public string ItemName { get; set; }
    }
}

XAML Change....

<ListBox x:Name="listBox1" Height="300" SelectionChanged="listBox1_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding ItemName}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
1 голос
/ 18 июля 2011

Вы хотите выбрать «item12» - затем добавить больше элементов - и после этого «item12» все еще должен быть выбран и отображен?

, если да, почему бы не использовать ICollectionView .Метод MoveTo после добавления новых предметов?вам просто нужно запомнить последние выбранные элементы перед добавлением новых элементов.

Я использую MoveTo и ScrollIntoView в моих проектах, все работает отлично.

РЕДАКТИРОВАТЬ:

я не делаюдобавить элементы прямо в список.Я использую коллекцию и добавляю эту коллекцию в источник элементов списка.Я использую событие SelectionChanged для ScrollItemsIntoView.

 var _myview = (ICollectionView)CollectionViewSource.GetDefaultView(this._mycollection);

 _myview.MoveCurrentTo(this.rememberredItem);

, если вы работаете с коллекциями Ado.net, тогда вы должны взять BindingListCollectionView вместо ICollectionView

EDIT2: вместо CollectionViewSource.GetDefaultView (listBox1.ItemsSource) вы можете попробовать следующее.создайте наблюдаемую коллекцию и установите коллекцию как источник элементов для вашего списка.

ICollectionView myview;
OberservableCollection<string> mysource = new ObservableCollection<string>();
myview = (ICollectionView)CollectionViewSource.GetDefaultView(this.mysource);
listbox1.ItemsSource = mysource; //you should better use binding in xaml here

, если вы хотите добавить элементы, просто добавьте их в коллекцию.

this.mysource.Add("Item 13");
this.mysource.Add("Item 14");

EDIT3: скопировано из здесь

Дэвид Энсон разместил в своем блоге несколько статей, которые могут вам помочь: Часть 1 и Часть 2. Он дает метод расширения, который центрирует элемент в списке.Вы могли бы быть в состоянии основываться на этом

0 голосов
/ 22 июля 2011

Я проверил это в Silverlight, но разница есть

Ваш код не работает

private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
{
      listBox1.ScrollIntoView(fixedItem);
}

, но этот код работает!

private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
{
      Dispatcher.BeginInvoke(() => listBox1.ScrollIntoView(fixedItem));
}

проблема возникает из-за такта, который прокручивает список, а затем добавляет новый элемент, а затем повторяет прокрутку .. если вы вызовете прокрутку на диспетчере, список завершит свою работу, добавив элементы и, затем, прокрутит ваш элемент

0 голосов
/ 18 июля 2011

Вы можете попробовать это:

namespace WpfApplication1 {   
/// <summary>     
/// Interaction logic for Window1.xaml  
/// </summary>  
///
public partial class Window1 : Window  
{
     string fixedItem;
      public Window1()
     {
         InitializeComponent();
         listBox1.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(list_changes);
      }
        private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
     {
         listBox1.UpdateLayout();
         listBox1.ScrollIntoView(fixedItem); 
    }
     // REST OF YOUR CODE...

UPDATE Если вы имели в виду, что ваша прокрутка уже правильная, только перемещение элемента в последнюю видимую позицию, я бы получил элемент с тремя (или более) индексами вперед, чтобы централизовать элемент в списке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...