/// <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;
         }
     }));
 }
Exemplo n.º 3
0
        /// <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."); }));
        }