/// <summary> /// Returns an observable that returns a SW.DragDropKeyStates value /// whenever it changes, even if one of the events involved /// is handled. /// </summary> /// <param name="that">The element to listen to.</param> /// <param name="initialState">The initial state SW.DragDropKeyStates. /// </param> /// <returns>An observable that returns a SW.DragDropKeyStates value /// whenever it changes, even if one of the events involved /// is handled.</returns> internal static IObservable <SW.DragDropKeyStates> GetKeyStateChangedAlways( this UIElement that, SW.DragDropKeyStates initialState) { return (GetKeyStateChanged(that.GetMouseLeftButtonDownAlways(), that.GetMouseLeftButtonUpAlways(), that.GetKeyDownAlways(), that.GetKeyUpAlways(), initialState)); }
/// <summary> /// Returns an observable that returns a SW.DragDropKeyStates value. The /// observable is composed of mouse down and up observables and key down /// and up observables. /// </summary> /// <param name="mouseDownObservable">An event raised when a mouse /// button is depressed.</param> /// <param name="mouseUpObservable">An event raised when a mouse button /// is released.</param> /// <param name="keyDownObservable">An event raised when a key is /// pressed down.</param> /// <param name="keyUpObservable">An event raised when a key is /// released.</param> /// <param name="initialState">The initial state of the drag and /// drop keys.</param> /// <returns>An observable that returns a SW.DragDropKeyStates value /// whenever it changes, even if one of the events involved /// is handled.</returns> private static IObservable <SW.DragDropKeyStates> GetKeyStateChanged( IObservable <IEvent <MouseButtonEventArgs> > mouseDownObservable, IObservable <IEvent <MouseButtonEventArgs> > mouseUpObservable, IObservable <IEvent <KeyEventArgs> > keyDownObservable, IObservable <IEvent <KeyEventArgs> > keyUpObservable, SW.DragDropKeyStates initialState) { return (mouseDownObservable .Select(ev => Tuple.Create(true, SW.DragDropKeyStates.LeftMouseButton)) .Merge( mouseUpObservable .Select(ev => Tuple.Create(false, SW.DragDropKeyStates.LeftMouseButton))) .Merge( keyUpObservable .Select(ev => Tuple.Create(false, ToDragDropKeyStates(ev.EventArgs.Key)))) .Merge( keyDownObservable .Select(ev => Tuple.Create(true, ToDragDropKeyStates(ev.EventArgs.Key)))) .Scan <Tuple <bool, SW.DragDropKeyStates>, SW.DragDropKeyStates>( initialState, (acc, current) => { if (current.Item1) { return acc | current.Item2; } else { return acc & ~current.Item2; } })); }
/// <summary> /// Initializes a new instance of the DragOperation class. /// </summary> /// <param name="dragSource">The source of the drag operation.</param> /// <param name="data">The data associated with the drag operation. /// </param> /// <param name="allowedEffects">The allowed effects of the drag /// operation. /// </param> /// <param name="initialKeyState">The initial state of the keys relevant /// to drag operations.</param> public DragOperation(DependencyObject dragSource, object data, SW.DragDropEffects allowedEffects, SW.DragDropKeyStates initialKeyState) { SW.IDataObject dataObject = data as SW.IDataObject; if (dataObject == null) { dataObject = new DataObject(data); } _allowedEffects = allowedEffects; KeyStates = initialKeyState; _dragSource = dragSource; SW.DragEventArgs dragStartEventArgs = new SW.DragEventArgs() { OriginalSource = dragSource, AllowedEffects = allowedEffects, Effects = allowedEffects, Data = dataObject }; _lastDragEventArgs = dragStartEventArgs; dragStartEventArgs.AllowedEffects = allowedEffects; dragStartEventArgs.Data = dataObject; this._dragStartEventArgs = dragStartEventArgs; IObservable <Event <SW.QueryContinueDragEventArgs> > sourceQueryContinue = from dragStartedEvent in _dragStarting from queryContinueDragEvent in _dragSourceQueryContinueDrag.Until(_dragCompleted) where queryContinueDragEvent.EventArgs.Handled select queryContinueDragEvent; sourceQueryContinue .Where(queryContinueDragEvent => queryContinueDragEvent.EventArgs.Action == SW.DragAction.Drop) .Subscribe(queryContinueDragEvent => OnTargetDrop()); sourceQueryContinue .Where(queryContinueDragEvent => queryContinueDragEvent.EventArgs.Action == SW.DragAction.Cancel) .Subscribe(queryContinueDragEvent => OnCancel()); _dragCompleted.Subscribe(_ => IsDragging = false); }
/// <summary> /// Returns an observable that returns a SW.DragDropKeyStates value /// whenever it changes, even if one of the events involved /// is handled or occurs in a sibling. /// </summary> /// <param name="that">The element to listen to.</param> /// <param name="initialState">The initial state SW.DragDropKeyStates. /// </param> /// <returns>An observable that returns a SW.DragDropKeyStates value /// whenever it changes, even if one of the events involved /// is handled.</returns> internal static IObservable <SW.DragDropKeyStates> GetKeyStateChangedOnSelfAndSiblingsAlways(this UIElement that, SW.DragDropKeyStates initialState) { IEnumerable <UIElement> popupRoots = VisualTreeExtensions.GetVisualDescendants(that) .OfType <Popup>() .Select(popup => popup.Child) .Where(popupRoot => popupRoot != null); return(GetKeyStateChanged( popupRoots .Select(popupRoot => popupRoot.GetMouseLeftButtonDownAlways()) .Aggregate(that.GetMouseLeftButtonDownAlways(), (left, right) => left.Merge(right)), popupRoots .Select(popupRoot => popupRoot.GetMouseLeftButtonUpAlways()) .Aggregate(that.GetMouseLeftButtonUpAlways(), (left, right) => left.Merge(right)), popupRoots .Select(popupRoot => popupRoot.GetKeyUpAlways()) .Aggregate(that.GetKeyUpAlways(), (left, right) => left.Merge(right)), popupRoots .Select(popupRoot => popupRoot.GetKeyDownAlways()) .Aggregate(that.GetKeyDownAlways(), (left, right) => left.Merge(right)), initialState)); }
public IDisposable Subscribe(IObserver <SW.DragDropEffects> observer) { if (IsDragging) { throw new InvalidOperationException("A drag operation is already in progress."); } IsDragging = true; // Always execute a QueryContinueDrag first. _dragStarting.Take(1) .ObserveOnDispatcher() .Subscribe( dragEventArgs => { SW.QueryContinueDragEventArgs args = OnDragSourceQueryContinueDrag(); if (!(args.Handled && args.Action == SW.DragAction.Cancel)) { OnDragSourceGiveFeedback(_lastDragEventArgs); _dragStarted.OnNext(dragEventArgs); } }); IObservable <SW.DragDropKeyStates> keyStatesChanged = from dragStarted in _dragStarting where Application.Current != null from keyState in Application.Current.RootVisual .GetKeyStateChangedOnSelfAndSiblingsAlways(KeyStates) .TakeUntil(_dragCompleted) select keyState; keyStatesChanged.Subscribe(keyState => KeyStates = keyState); IObservable <bool> escapePressedChanged = from dragStated in _dragStarting where Application.Current != null from escapePressed in Application.Current.RootVisual .GetEscapePressedChangedOnSelfAndSiblingsAlways() .TakeUntil(_dragCompleted) select escapePressed; escapePressedChanged.Subscribe(pressed => _escapePressed = pressed); Observable .Merge( keyStatesChanged.IgnoreAll(), escapePressedChanged.IgnoreAll()) .TakeUntil(_dragCompleted) .ObserveOnDispatcher() .Subscribe(_ => RaiseDragSourceEvents(_lastDragEventArgs)); IObservable <IEvent <SW.DragEventArgs> > dragOver = from dragStartedEvent in _dragStarted let mouseMoveDragOver = GetMouseMoveDragOver() from dragMouseOverEvent in mouseMoveDragOver.TakeUntil(_dragCompleted) from pulseDragMouseOverEvent in Observable.Merge( Observable.Return(dragMouseOverEvent), Observable .Interval(TimeSpan.FromMilliseconds(MouseOverPulseIntervalInMilliseconds)) .ObserveOnDispatcher() .Select(_ => { UIElement originalSource = dragMouseOverEvent.EventArgs.OriginalSource as UIElement; if (originalSource != null) { Point pointWithinOriginalSource = dragMouseOverEvent.EventArgs.GetPosition((UIElement)dragMouseOverEvent.EventArgs.OriginalSource); // if point is no longer within original source find the element // in the tree that contains the point and make it the original source (and sender) // or the next dragover event. if (!new Rect(new Point(0, 0), originalSource.GetSize()).Contains(pointWithinOriginalSource)) { originalSource = GetDragOverOriginalSource(dragMouseOverEvent.EventArgs); } } return(Event.Create(originalSource, new SW.DragEventArgs(dragMouseOverEvent.EventArgs) { OriginalSource = originalSource })); }) .TakeUntil( Observable.Merge( mouseMoveDragOver.IgnoreAll(), _dragCompleted.IgnoreAll()))) where IsDragging select pulseDragMouseOverEvent; dragOver .ObserveOnDispatcher() .Subscribe(dragOverEvent => OnDragOver(dragOverEvent.Sender, dragOverEvent.EventArgs)); _dragStarting.OnNext(Event.Create(_dragSource, _dragStartEventArgs)); _dragCompleted .ObserveOnDispatcher() .Subscribe(observer); return(new AnonymousDisposable(() => { Debug.Assert(false, "Should never detach from DragOperation."); })); }