c# - openclassroom - Comment contrôler la position de défilement d'un ListBox dans une application MVVM WPF



xaml openclassroom (3)

J'ai un grand ListBox avec le défilement vertical activé, mon MVVM a New et Edit ICommands. J'ajoute un nouvel élément à la fin de la collection mais je souhaite que la barre de défilement se positionne automatiquement à la fin lorsque j'appelle mon MVVM-AddCommand. Je modifie également un élément (en appelant EditCommand avec un élément de ligne particulier) depuis une autre partie de l'application afin que mon objet ListBoxItem passe en mode édition à l'aide de DataTrigger, mais comment vais-je amener cette ligne particulière (ListBoxItem) dans la vue en ajustant la position de défilement.

Si je le fais du côté de la vue, je peux appeler listBox.ScrollInToView (lstBoxItem). Mais quel est le meilleur moyen de résoudre ce problème de défilement commun du point de vue du MVVM?

https://code.i-harness.com


Ceci est la forme de la pièce jointe de la réponse acceptée:

using System.Windows;
using System.Windows.Controls;

namespace CommonBehaviors
{
    public static class ScrollCurrentItemIntoViewBehavior
    {
        public static readonly DependencyProperty AutoScrollToCurrentItemProperty =
            DependencyProperty.RegisterAttached("AutoScrollToCurrentItem",
                typeof(bool), typeof(ScrollCurrentItemIntoViewBehavior),
                new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));

        public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoScrollToCurrentItemProperty);
        }

        public static void OnAutoScrollToCurrentItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var listBox = obj as ListBox;
            if (listBox == null) return;

            var newValue = (bool)e.NewValue;
            if (newValue)
                listBox.SelectionChanged += listBoxSelectionChanged;
            else
                listBox.SelectionChanged -= listBoxSelectionChanged;
        }

        static void listBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var listBox = sender as ListBox;
            if (listBox == null || listBox.SelectedItem == null || listBox.Items == null) return;

            listBox.Items.MoveCurrentTo(listBox.SelectedItem);
            listBox.ScrollIntoView(listBox.SelectedItem);
        }

        public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoScrollToCurrentItemProperty, value);
        }
    }
}

Usage:

<ListBox ItemsSource="{Binding}"
          IsSynchronizedWithCurrentItem="True"
          behaviors:ScrollCurrentItemIntoViewBehavior.AutoScrollToCurrentItem="True">

Si le code ci-dessus ne fonctionne pas pour vous, essayez-le

public class ListBoxExtenders : DependencyObject
{
    public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));

    public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoScrollToSelectedItemProperty);
    }

    public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoScrollToSelectedItemProperty, value);
    }

    public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
        var listBox = s as ListBox;
        if (listBox != null)
        {
            var listBoxItems = listBox.Items;
            if (listBoxItems != null)
            {
                var newValue = (bool)e.NewValue;

                var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition));

                if (newValue)
                    listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker;
                else
                    listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker;
            }
        }
    }

    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
    {
        if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0)
            listBox.ScrollIntoView(listBox.Items[index]);
    }

}

Utilisation dans XAML

<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>

Je règle généralement IsSynchronizedWithCurrentItem="True" sur le ListBox . Ensuite, j'ajoute un gestionnaire SelectionChanged et mets toujours en évidence l'élément sélectionné, avec le code suivant:

    private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e)
    {
        Selector selector = sender as Selector;
        if (selector is ListBox)
        {
            (selector as ListBox).ScrollIntoView(selector.SelectedItem);
        }
    }

À partir de ma machine virtuelle, je peux obtenir la vue de collection par défaut et utiliser l'une des MoveCurrent*() pour vérifier que l'élément en cours de modification est l'élément en cours.

CollectionViewSource.GetDefaultView(_myCollection).MoveCurrentTo(thisItem);

REMARQUE: ListBox.ScrollIntoView() pour utiliser ListBox.ScrollIntoView() pour ListBox.ScrollIntoView() virtualisation