5 using System.Windows.Controls;
6 using System.Windows.Controls.Primitives;
7 using System.Windows.Documents;
8 using System.Windows.Input;
9 using System.Windows.Media;
10 using System.Windows.Threading;
11 using SiliconStudio.Presentation.Extensions;
16 namespace SiliconStudio.Presentation.Behaviors
20 private ListBox listBox;
21 private FrameworkElement itemsPresenter;
23 private SelectionAdorner selectionRect;
24 private AutoScroller autoScroller;
25 private ItemsControlSelector selector;
27 private bool mouseCaptured;
33 listBox = AssociatedObject;
37 if (listBox.SelectionMode == SelectionMode.Single)
38 listBox.SelectionMode = SelectionMode.Extended;
48 private void Register()
50 itemsPresenter = listBox.FindVisualChildOfType<ItemsPresenter>();
52 if (itemsPresenter == null)
55 var adornerLayer = AdornerLayer.GetAdornerLayer(itemsPresenter);
56 if (adornerLayer == null)
59 selectionRect =
new SelectionAdorner(itemsPresenter);
60 adornerLayer.Add(selectionRect);
62 selector =
new ItemsControlSelector(listBox);
64 autoScroller =
new AutoScroller(listBox);
65 autoScroller.OffsetChanged += OnOffsetChanged;
71 listBox.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
72 listBox.MouseLeftButtonUp += OnMouseLeftButtonUp;
73 listBox.MouseMove += OnMouseMove;
76 private void Unregister()
78 if (selectionRect != null && autoScroller != null)
84 listBox.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
85 listBox.MouseLeftButtonUp -= OnMouseLeftButtonUp;
86 listBox.MouseMove -= OnMouseMove;
88 if (autoScroller != null)
89 autoScroller.Unregister();
92 private Point positionAtMouseDown;
93 private bool isDragging;
94 private bool isSelectedAtMouseDown;
96 private readonly Type[] knownPresenterTypes =
new[]
98 typeof(ContentPresenter),
99 typeof(GridViewRowPresenterBase)
102 private void OnPreviewMouseLeftButtonDown(
object sender, MouseButtonEventArgs e)
104 positionAtMouseDown = e.GetPosition(itemsPresenter);
106 mouseCaptured =
false;
108 var item = GetItemAt(listBox, e.GetPosition) as ListBoxItem;
110 isSelectedAtMouseDown =
false;
115 isSelectedAtMouseDown =
true;
118 var result = VisualTreeHelper.HitTest(item, e.GetPosition(item));
119 if (result != null && result.VisualHit != null)
121 var presenter = result.VisualHit.FindVisualParentOfType<FrameworkElement>();
123 if (presenter != null)
125 var presenterType = presenter.GetType();
126 if (
Array.Exists(knownPresenterTypes, t => t.IsAssignableFrom(presenterType)))
127 isSelectedAtMouseDown =
true;
134 private void OnMouseMove(
object sender, MouseEventArgs e)
136 if (e.LeftButton != MouseButtonState.Pressed)
139 if (isDragging ==
false)
141 var currentMousePosition = e.GetPosition(itemsPresenter);
143 var dx = currentMousePosition.X - positionAtMouseDown.X;
144 var dy = currentMousePosition.Y - positionAtMouseDown.Y;
146 if (Math.Abs(dx) > SystemParameters.MinimumHorizontalDragDistance ||
147 Math.Abs(dy) > SystemParameters.MinimumVerticalDragDistance)
151 if (isSelectedAtMouseDown ==
false)
153 if (positionAtMouseDown.X >= 0 && positionAtMouseDown.X < itemsPresenter.ActualWidth &&
154 positionAtMouseDown.Y >= 0 && positionAtMouseDown.Y < itemsPresenter.ActualHeight)
156 mouseCaptured = TryCaptureMouse(e);
159 StartSelection(positionAtMouseDown);
170 end = e.GetPosition(itemsPresenter);
171 autoScroller.Update(end);
176 private void OnMouseLeftButtonUp(
object sender, MouseButtonEventArgs e)
178 if (isDragging ==
false)
180 var item = GetItemAt(listBox, e.GetPosition) as ListBoxItem;
183 var isControlPressed = (Keyboard.Modifiers & ModifierKeys.Control) != 0;
185 if (isControlPressed)
196 listBox.SelectedItems.Clear();
197 item.IsSelected =
true;
202 listBox.SelectedItems.Clear();
210 mouseCaptured =
false;
211 itemsPresenter.ReleaseMouseCapture();
216 private void OnOffsetChanged(
object sender, OffsetChangedEventArgs e)
218 selector.Scroll(e.HorizontalChange, e.VerticalChange);
222 private void StartSelection(
Point location)
232 if ((
Keyboard.Modifiers & ModifierKeys.Control) == 0 &&
233 (Keyboard.Modifiers & ModifierKeys.Shift) == 0)
237 listBox.SelectedItems.Clear();
243 selectionRect.IsEnabled =
true;
244 autoScroller.IsEnabled =
true;
247 private void UpdateSelection()
250 var transStart = autoScroller.TranslatePoint(start);
254 var x = Math.Min(transStart.X, end.X);
255 var
y = Math.Min(transStart.Y, end.Y);
256 var width = Math.Abs(end.X - transStart.X);
257 var height = Math.Abs(end.Y - transStart.Y);
259 var area =
new Rect(x, y, width, height);
260 selectionRect.SelectionArea = area;
264 var topLeft = itemsPresenter.TranslatePoint(area.TopLeft, listBox);
265 var bottomRight = itemsPresenter.TranslatePoint(area.BottomRight, listBox);
268 selector.UpdateSelection(
new Rect(topLeft, bottomRight));
271 private void StopSelection()
274 selectionRect.IsEnabled =
false;
275 autoScroller.IsEnabled =
false;
278 private FrameworkElement GetItemAt(
ItemsControl itemsControl, Func<IInputElement, Point> getPosition)
280 for (
int i = 0; i < itemsControl.Items.Count; i++)
282 var item = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
285 var bounds = VisualTreeHelper.GetDescendantBounds(item);
286 if (bounds.Contains(getPosition(item)))
294 private bool TryCaptureMouse(MouseEventArgs e)
296 var position = e.GetPosition(itemsPresenter);
299 var element = itemsPresenter.InputHitTest(position) as UIElement;
304 var args =
new MouseButtonEventArgs(
308 e.StylusDevice) { RoutedEvent = Mouse.MouseDownEvent,
Source = e.Source };
310 element.RaiseEvent(args);
314 if (!ReferenceEquals(
Mouse.Captured, listBox))
319 return itemsPresenter.CaptureMouse();
328 private sealed
class AutoScroller
330 private readonly DispatcherTimer autoScroll =
new DispatcherTimer();
332 private readonly ScrollViewer scrollViewer;
333 private readonly ScrollContentPresenter scrollContent;
334 private bool isEnabled;
335 private Point offset;
345 if (itemsControl == null)
347 throw new ArgumentNullException(
"itemsControl");
350 this.itemsControl = itemsControl;
351 scrollViewer = itemsControl.FindVisualChildOfType<ScrollViewer>();
352 scrollViewer.ScrollChanged += OnScrollChanged;
353 scrollContent = scrollViewer.FindVisualChildOfType<ScrollContentPresenter>();
355 autoScroll.Tick += delegate { PreformScroll(); };
356 autoScroll.Interval = TimeSpan.FromMilliseconds(GetRepeatRate());
360 public event EventHandler<OffsetChangedEventArgs> OffsetChanged;
366 public bool IsEnabled
374 if (isEnabled != value)
379 autoScroll.IsEnabled =
false;
380 offset =
new Point();
392 return new Point(point.X - offset.X, point.Y - offset.Y);
398 public void Unregister()
400 scrollViewer.ScrollChanged -= OnScrollChanged;
409 public void Update(
Point mousePosition)
411 mouse = mousePosition;
414 if (autoScroll.IsEnabled ==
false)
421 private static int GetRepeatRate()
426 const double Ratio = (400.0 - 33.0) / 31.0;
427 return 400 - (int)(SystemParameters.KeyboardSpeed * Ratio);
430 private double CalculateOffset(
int startIndex,
int endIndex)
434 for (
int i = startIndex; i != endIndex; i++)
436 var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
437 if (container != null)
440 sum += container.ActualHeight;
441 sum += container.Margin.Top + container.Margin.Bottom;
448 private void OnScrollChanged(
object sender, ScrollChangedEventArgs e)
453 var horizontal = e.HorizontalChange;
454 var vertical = e.VerticalChange;
459 if (scrollViewer.CanContentScroll)
462 if (e.VerticalChange < 0)
464 var start = (int)e.VerticalOffset;
465 var end = (
int)(e.VerticalOffset - e.VerticalChange);
466 vertical = -CalculateOffset(start, end);
470 var start = (int)(e.VerticalOffset - e.VerticalChange);
471 var end = (int)e.VerticalOffset;
472 vertical = CalculateOffset(start, end);
476 offset.X += horizontal;
477 offset.Y += vertical;
479 var callback = OffsetChanged;
480 if (callback != null)
482 callback(
this,
new OffsetChangedEventArgs(horizontal, vertical));
487 private void PreformScroll()
489 var scrolled =
false;
491 var
size = VisualTreeHelper.GetDescendantBounds(scrollViewer);
493 if (mouse.X > size.Width )
495 scrollViewer.LineRight();
498 else if (mouse.X < 0)
500 scrollViewer.LineLeft();
504 if (mouse.Y > scrollContent.ActualHeight)
506 scrollViewer.LineDown();
509 else if (mouse.Y < 0)
511 scrollViewer.LineUp();
518 autoScroll.IsEnabled = scrolled;
523 private sealed
class ItemsControlSelector
526 private Rect previousArea;
537 if (itemsControl == null)
538 throw new ArgumentNullException(
"itemsControl");
540 this.itemsControl = itemsControl;
548 previousArea =
new Rect();
556 public void Scroll(
double x,
double y)
558 previousArea.Offset(-x, -
y);
567 public void UpdateSelection(Rect area)
570 for (
int i = 0; i < itemsControl.Items.Count; i++)
572 var item = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
576 var topLeft = item.TranslatePoint(
new Point(0, 0), itemsControl);
577 var itemBounds =
new Rect(topLeft.X, topLeft.Y, item.ActualWidth, item.ActualHeight);
581 if (itemBounds.IntersectsWith(area))
583 Selector.SetIsSelected(item,
true);
585 else if (itemBounds.IntersectsWith(previousArea))
589 Selector.SetIsSelected(item,
false);
599 private sealed
class OffsetChangedEventArgs :
EventArgs
606 internal OffsetChangedEventArgs(
double horizontal,
double vertical)
608 HorizontalChange = horizontal;
609 VerticalChange = vertical;
613 public double HorizontalChange {
get;
private set; }
616 public double VerticalChange {
get;
private set; }
620 private sealed
class SelectionAdorner :
Adorner
622 private Rect selectionRect;
623 private readonly Brush fill;
624 private readonly Pen pen;
633 public SelectionAdorner(UIElement parent)
637 IsHitTestVisible =
false;
639 fill = SystemColors.HighlightBrush.Clone();
647 IsEnabledChanged += delegate { InvalidateVisual(); };
651 public Rect SelectionArea
655 return selectionRect;
659 selectionRect = value;
668 protected override void OnRender(DrawingContext drawingContext)
670 base.OnRender(drawingContext);
675 double[] x = { SelectionArea.Left + 0.5, SelectionArea.Right + 0.5 };
676 double[]
y = { SelectionArea.Top + 0.5, SelectionArea.Bottom + 0.5 };
677 drawingContext.PushGuidelineSet(
new GuidelineSet(x, y));
679 drawingContext.DrawRectangle(fill, pen, SelectionArea);
override void OnDetachingOverride()
System.Windows.Media.Colors SystemColors
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
The validation occurs every time the mouse moves.
override void OnAttachedOverride()
The device failed due to a badly formed command. This is a run-time issue; The application should des...
_In_ size_t _In_ size_t size
System.Windows.Point Point