1 //----------------------------------------------------------------
2 // <copyright company="Microsoft Corporation">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //----------------------------------------------------------------
7 namespace System.Activities.Presentation.View
11 using System.Windows.Controls;
12 using System.Windows.Input;
14 internal class ScrollViewerPanner
16 private ScrollViewer scrollViewer;
17 private Point panningStartPosition;
18 private PanState currentPanState;
19 private bool inPanMode;
20 private MouseButton draggingMouseButton;
22 public ScrollViewerPanner(ScrollViewer scrollViewer)
24 Fx.Assert(scrollViewer != null, "ScrollViewer should never be null");
25 this.ScrollViewer = scrollViewer;
28 internal enum PanState
30 Normal, // Normal editing mode
35 public ScrollViewer ScrollViewer
39 return this.scrollViewer;
44 if (value != this.scrollViewer)
46 if (this.scrollViewer != null)
48 this.UnregisterEvents();
51 this.scrollViewer = value;
52 if (this.scrollViewer != null)
54 this.RegisterEvents();
64 return this.inPanMode;
69 if (this.inPanMode != value)
71 this.inPanMode = value;
74 this.CurrentPanState = PanState.ReadyToPan;
78 this.CurrentPanState = PanState.Normal;
84 public Cursor Hand { get; set; }
86 public Cursor DraggingHand { get; set; }
88 internal PanState CurrentPanState
92 return this.currentPanState;
97 if (this.currentPanState != value)
99 this.currentPanState = value;
100 switch (this.currentPanState)
102 case PanState.ReadyToPan:
103 this.scrollViewer.Cursor = this.Hand;
105 case PanState.Panning:
106 this.scrollViewer.Cursor = this.DraggingHand;
109 this.scrollViewer.Cursor = null;
113 this.UpdateForceCursorProperty();
118 internal bool IsInScrollableArea(Point mousePosition)
120 return mousePosition.X < this.scrollViewer.ViewportWidth && mousePosition.Y < this.scrollViewer.ViewportHeight;
123 internal void OnScrollViewerMouseDown(object sender, MouseButtonEventArgs e)
125 switch (this.CurrentPanState)
127 case PanState.Normal:
128 if (e.ChangedButton == MouseButton.Middle)
130 this.StartPanningIfNecessary(e);
134 case PanState.ReadyToPan:
135 switch (e.ChangedButton)
137 case MouseButton.Left:
138 this.StartPanningIfNecessary(e);
140 case MouseButton.Middle:
141 this.StartPanningIfNecessary(e);
143 case MouseButton.Right:
149 case PanState.Panning:
157 internal void OnLostMouseCapture(object sender, MouseEventArgs e)
162 internal void OnScrollViewerMouseMove(object sender, MouseEventArgs e)
164 switch (this.CurrentPanState)
166 case PanState.Panning:
167 Point currentPosition = Mouse.GetPosition(this.scrollViewer);
168 Vector offset = Point.Subtract(currentPosition, this.panningStartPosition);
169 this.panningStartPosition = currentPosition;
170 this.scrollViewer.ScrollToHorizontalOffset(this.scrollViewer.HorizontalOffset - offset.X);
171 this.scrollViewer.ScrollToVerticalOffset(this.scrollViewer.VerticalOffset - offset.Y);
174 case PanState.ReadyToPan:
175 this.UpdateForceCursorProperty();
182 internal void OnScrollViewerMouseUp(object sender, MouseButtonEventArgs e)
184 switch (this.CurrentPanState)
186 case PanState.ReadyToPan:
187 this.StopPanningIfNecessary(e);
189 // When the mouse is captured by other windows/views, that
190 // window/view needs this mouse-up message to release
192 if (!this.IsMouseCapturedByOthers())
198 case PanState.Panning:
199 this.StopPanningIfNecessary(e);
207 internal void OnScrollViewerKeyDown(object sender, KeyEventArgs e)
209 switch (this.CurrentPanState)
211 // Don't change to ReadyToPan mode if the space is a
212 // repeated input, because repeated-key input may come
213 // from activity element on the scroll view.
214 case PanState.Normal:
215 if (e.Key == Key.Space
217 && this.AllowSwitchToPanning())
219 this.CurrentPanState = PanState.ReadyToPan;
228 internal void OnScrollViewerKeyUp(object sender, KeyEventArgs e)
230 switch (this.CurrentPanState)
232 case PanState.ReadyToPan:
233 if (e.Key == Key.Space && !this.InPanMode)
235 this.CurrentPanState = PanState.Normal;
244 private void StartPanningIfNecessary(MouseButtonEventArgs e)
246 if (DesignerView.IsMouseInViewport(e, this.scrollViewer))
248 this.draggingMouseButton = e.ChangedButton;
249 this.CurrentPanState = PanState.Panning;
250 this.scrollViewer.Focus();
251 this.panningStartPosition = Mouse.GetPosition(this.scrollViewer);
252 Mouse.Capture(this.scrollViewer);
257 private void StopPanningIfNecessary(MouseButtonEventArgs e)
259 if (e.ChangedButton == this.draggingMouseButton && object.Equals(this.scrollViewer, Mouse.Captured))
261 // Trigers OnLostMouseCapture
262 this.scrollViewer.ReleaseMouseCapture();
266 private void StopPanning()
270 || (Keyboard.IsKeyDown(Key.Space) && this.CurrentPanState == PanState.Panning))
272 this.CurrentPanState = PanState.ReadyToPan;
276 this.CurrentPanState = PanState.Normal;
280 private void UpdateForceCursorProperty()
282 Point pt = Mouse.GetPosition(this.ScrollViewer);
283 if (this.IsInScrollableArea(pt))
285 this.scrollViewer.ForceCursor = true;
289 this.scrollViewer.ForceCursor = false;
293 // Mouse is sometimes captured by ScrollViewer's children, like
294 // RepeatButton in Scroll Bar.
295 private bool IsMouseCapturedByOthers()
297 return (Mouse.Captured != null)
298 && !object.Equals(Mouse.Captured, this.ScrollViewer);
301 private bool AllowSwitchToPanning()
303 return Mouse.LeftButton == MouseButtonState.Released
304 && Mouse.RightButton == MouseButtonState.Released;
307 private void RegisterEvents()
309 this.scrollViewer.PreviewMouseDown += new MouseButtonEventHandler(this.OnScrollViewerMouseDown);
310 this.scrollViewer.PreviewMouseMove += new MouseEventHandler(this.OnScrollViewerMouseMove);
311 this.scrollViewer.PreviewMouseUp += new MouseButtonEventHandler(this.OnScrollViewerMouseUp);
312 this.scrollViewer.LostMouseCapture += new MouseEventHandler(this.OnLostMouseCapture);
313 this.scrollViewer.KeyDown += new KeyEventHandler(this.OnScrollViewerKeyDown);
314 this.scrollViewer.KeyUp += new KeyEventHandler(this.OnScrollViewerKeyUp);
317 private void UnregisterEvents()
319 this.scrollViewer.PreviewMouseDown -= new MouseButtonEventHandler(this.OnScrollViewerMouseDown);
320 this.scrollViewer.PreviewMouseMove -= new MouseEventHandler(this.OnScrollViewerMouseMove);
321 this.scrollViewer.PreviewMouseUp -= new MouseButtonEventHandler(this.OnScrollViewerMouseUp);
322 this.scrollViewer.LostMouseCapture -= new MouseEventHandler(this.OnLostMouseCapture);
323 this.scrollViewer.KeyDown -= new KeyEventHandler(this.OnScrollViewerKeyDown);
324 this.scrollViewer.KeyUp -= new KeyEventHandler(this.OnScrollViewerKeyUp);