Bonjour à tous,

Je sèche sur un truc qui aurait quand même pu être plus simple.

Contexte:

Une simple Listbox visualise les transmissions entrante et sortant sur le port COM.

Le souci, c'est que je souhaite respecter le pattern MVVM et a chaque ajout de texte j'ai un défilement automatique sur le dernier texte ajouté (ou arrêt du défilement si l'utilisateur souhaite vérifier manuellement les captures ,par exemple en cliquant sur une checkbox).

J'ai trouvé un code C# que j'ai adapté VB, mais s'il fonctionne bien au début ensuite lorsque la listbox ce rempli cela part en sucette avec des bon vers le bas puis vers le haut, c'est pas fluide (j’appelle sa des rebond). Pas clean donc. Et puis même sur le code C# je n'arrive pas a empêcher le défilement automatique à l'aide d'une Checkbox qui n'a du coup aucun effet.

Bizarrement sur cet exemple j'ai pas ce problème de rebond de l'autoscroll, pourtant le code est le même.



Exemple complet en C#:

Vue Model:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Threading;
 
namespace Test_Listbox_Autoscroll 
{
    class VueModele : INotifyPropertyChanged
    {
 
        DispatcherTimer Timer;
        ObservableCollection<String> _LST_ListBox1_Collection;
        String _LST_ListBox1_SelectedItem;
        int i;
        public VueModele()
        {
            LST_ListBox1_Collection = new ObservableCollection<string>();
            Timer = new DispatcherTimer();
            Timer.Interval = new TimeSpan(0, 0, 1);
            Timer.Tick += new EventHandler(Timer_Tick);
            Timer.Start();
        }
        private void Timer_Tick(object sender, EventArgs e)
        {
            i += 1;
            LST_ListBox1_Collection.Add("Ligne " + i);
        }
        public ObservableCollection<String> LST_ListBox1_Collection
        {
            get { return _LST_ListBox1_Collection; }
            set {
                _LST_ListBox1_Collection = value;
                OnPropertyChanged("_LST_ListBox1_Collection");
            }
        }
        public String LST_ListBox1_SelectedItem
        {
            get { return _LST_ListBox1_SelectedItem; }
            set {
                _LST_ListBox1_SelectedItem = value;
                OnPropertyChanged("LST_ListBox1_SelectedItem");
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string Name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(Name));
            }
        }
    }
}
Classe LoggingListBox qui n'est pas de moi:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
 
namespace Test_Listbox_Autoscroll
{
    public class LoggingListBox : ListBox
    {
        ///<summary>
        ///Define the AutoScroll property. If enabled, causes the ListBox to scroll to 
        ///the last item whenever a new item is added.
        ///</summary>
        public static readonly DependencyProperty AutoScrollProperty =
            DependencyProperty.Register(
                "AutoScroll",
                typeof(Boolean),
                typeof(LoggingListBox),
                new FrameworkPropertyMetadata(
                    true, //Default value.
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    AutoScroll_PropertyChanged));
 
        /// <summary>
        /// Gets or sets whether or not the list should scroll to the last item 
        /// when a new item is added.
        /// </summary>
        [Category("Common")] //Indicate where the property is located in VS designer.
        public bool AutoScroll
        {
            get { return (bool)GetValue(AutoScrollProperty); }
            set { SetValue(AutoScrollProperty, value); }
        }
 
        /// <summary>
        /// Event handler for when the AutoScroll property is changed.
        /// This delegates the call to SubscribeToAutoScroll_ItemsCollectionChanged().
        /// </summary>
        /// <param name="d">The DependencyObject whose property was changed.</param>
        /// <param name="e">Change event args.</param>
        private static void AutoScroll_PropertyChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SubscribeToAutoScroll_ItemsCollectionChanged(
                (LoggingListBox)d,
                (bool)e.NewValue);
        }
 
        /// <summary>
        /// Subscribes to the list items' collection changed event if AutoScroll is enabled.
        /// Otherwise, it unsubscribes from that event.
        /// For this to work, the underlying list must implement INotifyCollectionChanged.
        ///
        /// (This function was only creative for brevity)
        /// </summary>
        /// <param name="listBox">The list box containing the items collection.</param>
        /// <param name="subscribe">Subscribe to the collection changed event?</param>
        private static void SubscribeToAutoScroll_ItemsCollectionChanged(
            LoggingListBox listBox, bool subscribe)
        {
            INotifyCollectionChanged notifyCollection =
                listBox.Items.SourceCollection as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                if (subscribe)
                {
                    //AutoScroll is turned on, subscribe to collection changed events.
                    notifyCollection.CollectionChanged +=
                        listBox.AutoScroll_ItemsCollectionChanged;
                }
                else
                {
                    //AutoScroll is turned off, unsubscribe from collection changed events.
                    notifyCollection.CollectionChanged -=
                        listBox.AutoScroll_ItemsCollectionChanged;
                }
            }
        }
 
        /// <summary>
        /// Event handler called only when the ItemCollection changes
        /// and if AutoScroll is enabled.
        /// </summary>
        /// <param name="sender">The ItemCollection.</param>
        /// <param name="e">Change event args.</param>
        private void AutoScroll_ItemsCollectionChanged(
            object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                int count = Items.Count;
                ScrollIntoView(Items[count - 1]);
            }
        }
 
        /// <summary>
        /// Constructor a new LoggingListBox.
        /// </summary>
        public LoggingListBox()
        {
            //Subscribe to the AutoScroll property's items collection 
            //changed handler by default if AutoScroll is enabled by default.
            SubscribeToAutoScroll_ItemsCollectionChanged(
                this, (bool)AutoScrollProperty.DefaultMetadata.DefaultValue);
        }
    }
}

MainWindow XAML:
Code XAML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<Window x:Class="Test_Listbox_Autoscroll.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test_Listbox_Autoscroll"
        xmlns:tools="clr-namespace:Test_Listbox_Autoscroll"
        mc:Ignorable="d"
        Title="MainWindow" Width="800"
         SizeToContent =" WidthAndHeight">
    <Grid>
        <StackPanel >
            <tools:LoggingListBox x:Name="Listbox1"
                              Margin=" 10"
                              Background="Bisque"
                                  Height="180"
                              ItemsSource="{Binding LST_ListBox1_Collection}"
                              SelectedItem="{Binding LST_ListBox1_SelectedItem}"/>
            <CheckBox x:Name="ChkAutoScroll"
                      Margin="10"
                      Content="Auto Scroll"
                      Checked="ChkAutoScroll_Checked"
                      Click="ChkAutoScroll_Click"/>
        </StackPanel>
 
    </Grid>
</Window>

MainWindow Code:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace Test_Listbox_Autoscroll
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            VueModele VM = new VueModele();
            this.DataContext = VM;
 
        }
 
        private void ChkAutoScroll_Checked(object sender, RoutedEventArgs e)
        {
 
        }
 
        private void ChkAutoScroll_Click(object sender, RoutedEventArgs e)
        {
            if (ChkAutoScroll.IsChecked == true)
            {
                Listbox1.AutoScroll = true;
            }
            else
            {
                Listbox1.AutoScroll = false;
            }
        }
    }
}


Voila le même code en VB:
Vue Model:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Windows.Threading
 
 
Public Class VueModele
    Implements INotifyPropertyChanged
 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Public Sub OnPropertyChanged(propname As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname))
    End Sub
 
    Private _LST_ListBox1_Collection As ObservableCollection(Of String)
    Private _LST_ListBox1_SelectedItem As Integer
    Private Timer As DispatcherTimer
    Private i As Integer
 
    Public Sub New()
        LST_ListBox1_Collection = New ObservableCollection(Of String)
        Timer = New DispatcherTimer
        Timer.Interval = New TimeSpan(0, 0, 1)
        AddHandler Timer.Tick, AddressOf Timer_Tick
        Timer.Start()
    End Sub
 
    Public Property LST_ListBox1_SelectedItem As Integer
        Get
            Return _LST_ListBox1_SelectedItem
        End Get
        Set(value As Integer)
            _LST_ListBox1_SelectedItem = value
            OnPropertyChanged("LST_ListBox1_SelectedItem")
        End Set
    End Property
    Public Property LST_ListBox1_Collection As ObservableCollection(Of String)
        Get
            Return _LST_ListBox1_Collection
        End Get
        Set(value As ObservableCollection(Of String))
            _LST_ListBox1_Collection = value
            OnPropertyChanged("LST_ListBox1_Collection")
        End Set
    End Property
    Private Sub Timer_Tick()
        i += 1
        LST_ListBox1_Collection.Add("Ligne " & CStr(i))
    End Sub
 
 
 
End Class

Classe LoggingListBox que j'ai converti:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Imports System.Collections.Specialized
Imports System.ComponentModel
 
 
Public Class LoggingListBox
        Inherits ListBox
 
 
        Public Shared ReadOnly AutoScrollProperty As DependencyProperty = DependencyProperty.Register("AutoScroll", GetType(Boolean), GetType(LoggingListBox), New FrameworkPropertyMetadata(True, FrameworkPropertyMetadataOptions.AffectsArrange Or FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf AutoScroll_PropertyChanged))
 
 
        <Category("Common")>
        Public Property AutoScroll() As Boolean
            Get
                Return DirectCast(GetValue(AutoScrollProperty), Boolean)
            End Get
            Set(ByVal value As Boolean)
                SetValue(AutoScrollProperty, value)
            End Set
        End Property
 
 
        Private Shared Sub AutoScroll_PropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
            SubscribeToAutoScroll_ItemsCollectionChanged(CType(d, LoggingListBox), DirectCast(e.NewValue, Boolean))
        End Sub
 
 
        Private Shared Sub SubscribeToAutoScroll_ItemsCollectionChanged(ByVal listBox As LoggingListBox, ByVal subscribe As Boolean)
            Dim notifyCollection As INotifyCollectionChanged = TryCast(listBox.Items.SourceCollection, INotifyCollectionChanged)
            If notifyCollection IsNot Nothing Then
                If subscribe Then
                    'AutoScroll is turned on, subscribe to collection changed events.
                    AddHandler notifyCollection.CollectionChanged, AddressOf listBox.AutoScroll_ItemsCollectionChanged
                Else
                    'AutoScroll is turned off, unsubscribe from collection changed events.
                    RemoveHandler notifyCollection.CollectionChanged, AddressOf listBox.AutoScroll_ItemsCollectionChanged
                End If
            End If
        End Sub
 
 
        Private Sub AutoScroll_ItemsCollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
            If e.Action = NotifyCollectionChangedAction.Add Then
                Dim count As Integer = Items.Count
                ScrollIntoView(Items(count - 1))
            End If
        End Sub
 
 
        Public Sub New()
            'Subscribe to the AutoScroll property's items collection 
            'changed handler by default if AutoScroll is enabled by default.
            SubscribeToAutoScroll_ItemsCollectionChanged(Me, DirectCast(AutoScrollProperty.DefaultMetadata.DefaultValue, Boolean))
        End Sub
    End Class
MainWindow Xaml:
Code XAML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test_Listbox_Autoscroll"
        xmlns:tools="clr-namespace:Test_Listbox_Autoscroll"
        mc:Ignorable="d"
        Title="MainWindow" Width="800"
         SizeToContent =" WidthAndHeight">
    <Grid>
        <StackPanel >
 
            <tools:LoggingListBox x:Name="Listbox1"
                              Margin=" 10"
                              Background="Bisque"
                                  Height="180"
                              ItemsSource="{Binding LST_ListBox1_Collection}"
                              SelectedItem="{Binding LST_ListBox1_SelectedItem}"/>
            <CheckBox x:Name="ChkAutoScroll"
                      Margin="10"
                      Content="Auto Scroll"
                      Checked="ChkAutoScroll_Checked"
                      Click="ChkAutoScroll_Click"/>
        </StackPanel>
 
    </Grid>
</Window>


MainWindow Code:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Class MainWindow
    Public Sub New()
 
        ' Cet appel est requis par le concepteur.
        InitializeComponent()
 
        ' Ajoutez une initialisation quelconque après l'appel InitializeComponent().
        Dim VM As New VueModele
        Me.DataContext = VM
    End Sub
 
    Private Sub ChkAutoScroll_Checked(sender As Object, e As RoutedEventArgs)
 
    End Sub
 
    Private Sub ChkAutoScroll_Click(sender As Object, e As RoutedEventArgs)
        If (ChkAutoScroll.IsChecked = True) Then
 
            Listbox1.AutoScroll = True
        Else
 
            Listbox1.AutoScroll = False
        End If
    End Sub
End Class


ce qui m’intéresse c'est le VB.net. Mais je vous ai mis le C# car c'est le code d'origine, mais lui aussi l'autoscroll est toujours actif.