Bonjour à tous,

Je débute sur avalonia et wpf et je rencontre un problème avec le listbox. En effet, la sélection par lasso n'est pas implémentée de base sur le contrôle. Je m'attache donc à la tache pour la créer. J'ai essayé avec un adornerlayer mais le canvas utilisé pour le dessin masque les items. Je suis donc partis sur un behavior afin d'étendre le control listbox. Ca marche comme plus ou moins comme je veux. Le problème vient que le début de la zone de selection ne suit pas le scroll de la listbox et donc perd les items selectionnés lors du scroll.

Comment puis je faire un behavior qui dessine bien sur le canvas et garde la position initiale pour avoir une sélection complète même après avoir scrollé? En gros il faudrait que le canvas scroll comme la listbox et mette à jour le point de départ.

Ou alors je me plante complètement dans l'approche et il faut faire autrement. La question est donc comment?

Merci pour l'aide et les éclaircissements.

Le code xaml :
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
 
        <!-- DIRECTORY CONTENT -->
        <Grid Grid.Row="2">
 
            <Canvas x:Name="SelectionCanvas" Grid.Row="2"
                IsHitTestVisible="False"
                Margin="5" Panel.ZIndex="10"
                Width="{Binding Width, ElementName=listBox}"
                Height="{Binding Height, ElementName=listBox}"/>
 
            <ListBox x:Name="listBox"
                ItemsSource="{Binding FiltredContent}"
                SelectionMode="Multiple"
                SelectedItems="{Binding SelectedItems}"
                HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                Margin="5" >
 
                <interactivity:Interaction.Behaviors>
                    <behaviors:TestBehavior/>
                </interactivity:Interaction.Behaviors>
 
                <ItemsControl.Styles>
                    <Style Selector="ContentPresenter"  x:DataType="vm:DirContentItemViewModel">
                        <Setter Property="Margin" Value="10"/>
                    </Style>
                </ItemsControl.Styles>
 
                <ListBox.ItemsPanel >
                    <ItemsPanelTemplate >
                        <WrapPanel Orientation="Horizontal" />
                        <!-- https://stackoverflow.com/questions/4656717/how-to-set-margin-for-inner-controls-of-wrappanel -->
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
 
            </ListBox>
 
            <TextBlock Text="Pas de fichier à afficher"
                IsVisible="{Binding EmptyContent}"
                Foreground="Red"
                HorizontalAlignment="Center" VerticalAlignment="Center"
                FontSize="15"/>
 
        </Grid>
        <!-- END DIRECTORY CONTENT -->
Et le code du behavior :
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
public class TestBehavior : Behavior<ListBox>
    {
        enum SelectionMode
        {
            None,
            New,
            Add,
            Multi
        }
 
        ListBox? listBox;
        Canvas? _canvas;
        readonly Border _selectionRectangle = new()
        {
            BorderBrush = Brushes.Blue,
            BorderThickness = new Thickness(1),
            Background = new SolidColorBrush(Colors.LightBlue, 0.3),
            IsHitTestVisible = false,
            IsVisible = false
        };
        SelectionMode _mode = SelectionMode.None;
        bool _isSelecting;
        Point _startPoint;
        HashSet<object> _initialSelection = new();
        HashSet<object> newSelection = new();
 
        protected override void OnAttached()
        {
            base.OnAttached();
            if (AssociatedObject is not ListBox) throw new ArgumentException("AssociatedObject is null or is not Type of Listbox");
            listBox = AssociatedObject;
            listBox.PointerPressed += OnPointerPressedEvent;
            listBox.PointerMoved += OnPointerMoved;
            listBox.PointerReleased += OnPointerReleased;
 
            var scrollViewer = listBox.FindAncestorOfType<ScrollViewer>();
            if (scrollViewer != null)
            {
                scrollViewer.ScrollChanged += OnScrollChanged;
            }
 
            Grid? ct = listBox.Parent as Grid ?? throw new ArgumentException("AssociatedObject is not in a Grid");
            _canvas = ct.Children[0] as Canvas ?? throw new ArgumentException("AssociatedObject has not a canvas");
            _canvas.Children.Add(_selectionRectangle);
        }
 
        protected override void OnDetaching()
        {
            if (_canvas is not null) _canvas = null;
            if (listBox is not null)
            {
                var scrollViewer = listBox.FindAncestorOfType<ScrollViewer>();
                if (scrollViewer != null)
                {
                    scrollViewer.ScrollChanged -= OnScrollChanged;
                }
 
                listBox.PointerPressed -= OnPointerPressedEvent;
                listBox.PointerMoved -= OnPointerMoved;
                listBox.PointerReleased -= OnPointerReleased;
            }
            base.OnDetaching();
        }
 
        void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
        {
            if (_canvas is null || listBox is null) return;
 
            var scrollViewer = sender as ScrollViewer;
            if (scrollViewer is null) return;
 
            var offsetY = scrollViewer.Offset.Y;
 
            // Mise à jour de la position du rectangle de sélection
            var currentTop = Canvas.GetTop(_selectionRectangle);
            Canvas.SetTop(_selectionRectangle, _startPoint.Y - offsetY);
        }
 
        void OnPointerPressedEvent(object? sender, PointerPressedEventArgs e)
        {
            if (!e.GetCurrentPoint(listBox).Properties.IsLeftButtonPressed) return;
            if (listBox is null || _canvas is null) return;
            SetMode(e.KeyModifiers);
            _startPoint = e.GetPosition(_canvas);
            _selectionRectangle.Width = 0;
            _selectionRectangle.Height = 0;
            Canvas.SetLeft(_selectionRectangle, _startPoint.X);
            Canvas.SetTop(_selectionRectangle, _startPoint.Y);
        }
 
        void OnPointerMoved(object? sender, PointerEventArgs e)
        {
            if (!_isSelecting || listBox is null || _canvas is null) return;
            if (!e.GetCurrentPoint(listBox).Properties.IsLeftButtonPressed) return;
            var pointerPosition = e.GetPosition(listBox);
            _selectionRectangle.IsVisible = true;
            var endPoint = e.GetPosition(_canvas);
            double x = Math.Min(_startPoint.X, endPoint.X);
            double y = Math.Min(_startPoint.Y, endPoint.Y);
            double width = Math.Abs(endPoint.X - _startPoint.X);
            double height = Math.Abs(endPoint.Y - _startPoint.Y);
            Canvas.SetLeft(_selectionRectangle, x);
            Canvas.SetTop(_selectionRectangle, y);
            _selectionRectangle.Width = width;
            _selectionRectangle.Height = height;
            SelectItems(listBox, new Rect(x, y, width, height));
        }
 
        void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
        {
            _isSelecting = false;
            _selectionRectangle.IsVisible = false;
            _initialSelection.Clear();
            newSelection.Clear();
        }
 
        void SetMode(KeyModifiers key)
        {
            _isSelecting = true;
            if (key.HasFlag(KeyModifiers.Control))
            {
                _mode = SelectionMode.Multi;
            }
            else if (key.HasFlag(KeyModifiers.Shift))
            {
                _mode = SelectionMode.Add;
            }
            else
            {
                _mode = SelectionMode.New;
                listBox?.SelectedItems?.Clear();
            }
        }
 
        private void SelectItems(ListBox listBox, Rect selectionBounds)
        {
            if (listBox is null || _canvas is null) return;
 
            var scrollViewer = listBox.FindAncestorOfType<ScrollViewer>();
            var scrollOffset = scrollViewer?.Offset.Y ?? 0;
 
            foreach (var item in listBox.Items)
            {
                var container = listBox.ContainerFromItem(item) as Control;
                if (container == null) continue;
 
                var itemBounds = new Rect(container.TranslatePoint(new Point(0, 0), _canvas) ?? new Point(), container.Bounds.Size);
                itemBounds = new Rect(itemBounds.X, itemBounds.Y - scrollOffset, itemBounds.Width, itemBounds.Height);
 
                bool isInside = selectionBounds.Intersects(itemBounds);
                switch (_mode)
                {
                    case SelectionMode.New:
                        if (isInside)
                        {
                            if (!listBox.SelectedItems.Contains(item))
                            {
                                listBox.SelectedItems.Add(item);
                            }
                        }
                        else
                        {
                            if (listBox.SelectedItems.Contains(item))
                            {
                                listBox.SelectedItems.Remove(item);
                            }
                        }
                        break;
                    case SelectionMode.Add:
                        if (isInside && !listBox.SelectedItems.Contains(item))
                        {
                            listBox.SelectedItems.Add(item);
                        }
                        break;
                    case SelectionMode.Multi:
                        if (isInside)
                        {
                            if (listBox.SelectedItems.Contains(item))
                            {
                                listBox.SelectedItems.Remove(item);
                            }
                            else
                            {
                                listBox.SelectedItems.Add(item);
                            }
                        }
                        break;
                }
            }
        }
    }