Пример #1
0
        /// <summary>
        /// Recalculates OS coordinates in order to support WPFs coordinate
        /// system if OS scaling (DPIs) is not 100%.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private Point GetDeviceCoordinates(Point point)
        {
            if (double.IsNaN(scalingFactor))
            {
                //calculate scaling factor in order to support non-standard DPIs
                var presentationSource = PresentationSource.FromVisual(this);

                if (presentationSource == null)
                {
                    scalingFactor = 1;
                }
                else
                {
                    var transform = presentationSource.CompositionTarget.TransformToDevice;
                    scalingFactor = 1 / transform.M11;
                }
            }

            // on standard DPI settings, just return the point
            if (Math.Abs(this.scalingFactor - 1.0) < 1.0)
            {
                return(point);
            }

            return(new Point {
                X = (int)(point.X * scalingFactor), Y = (int)(point.Y * scalingFactor)
            });
        }
Пример #2
0
        /// <summary>
        /// Displays the <see cref="ContextMenu"/> if
        /// it was set.
        /// </summary>
        private void ShowContextMenu(Point cursorPosition)
        {
            if (IsDisposed)
            {
                return;
            }

            //raise preview event no matter whether context menu is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayContextMenuOpenEvent();

            if (args.Handled)
            {
                return;
            }

            if (ContextMenu != null)
            {
                //use absolute position
                ContextMenu.Placement        = PlacementMode.AbsolutePoint;
                ContextMenu.HorizontalOffset = cursorPosition.X;
                ContextMenu.VerticalOffset   = cursorPosition.Y;
                ContextMenu.IsOpen           = true;

                //activate the message window to track deactivation - otherwise, the context menu
                //does not close if the user clicks somewhere else
                WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);

                //bubble event
                RaiseTrayContextMenuOpenEvent();
            }
        }
Пример #3
0
        /// <summary>
        /// Displays the <see cref="TrayPopup"/> control if
        /// it was set.
        /// </summary>
        private void ShowTrayPopup(Point cursorPosition)
        {
            if (IsDisposed)
            {
                return;
            }

            //raise preview event no matter whether popup is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayPopupOpenEvent();

            if (args.Handled)
            {
                return;
            }

            if (TrayPopup != null)
            {
                //use absolute position, but place the popup centered above the icon
                TrayPopupResolved.Placement        = PlacementMode.AbsolutePoint;
                TrayPopupResolved.HorizontalOffset = cursorPosition.X;
                TrayPopupResolved.VerticalOffset   = cursorPosition.Y;

                //open popup
                TrayPopupResolved.IsOpen = true;


                IntPtr handle = IntPtr.Zero;
                if (TrayPopupResolved.Child != null)
                {
                    //try to get a handle on the popup itself (via its child)
                    HwndSource source = (HwndSource)PresentationSource.FromVisual(TrayPopupResolved.Child);
                    if (source != null)
                    {
                        handle = source.Handle;
                    }
                }

                //if we don't have a handle for the popup, fall back to the message sink
                if (handle == IntPtr.Zero)
                {
                    handle = messageSink.MessageWindowHandle;
                }

                //activate either popup or message sink to track deactivation.
                //otherwise, the popup does not close if the user clicks somewhere else
                WinApi.SetForegroundWindow(handle);

                //raise attached event - item should never be null unless developers
                //changed the CustomPopup directly...
                if (TrayPopup != null)
                {
                    RaisePopupOpenedEvent(TrayPopup);
                }

                //bubble routed event
                RaiseTrayPopupOpenEvent();
            }
        }
Пример #4
0
        /// <summary>
        /// Displays the <see cref="ContextMenu"/> if it was set.
        /// </summary>
        private void ShowContextMenu(Point cursorPosition)
        {
            if (IsDisposed)
            {
                return;
            }

            // raise preview event no matter whether context menu is currently set
            // or not (enables client to set it on demand)
            var args = RaisePreviewTrayContextMenuOpenEvent();

            if (args.Handled)
            {
                return;
            }

            if (ContextMenu == null)
            {
                return;
            }

            // use absolute positioning. We need to set the coordinates, or a delayed opening
            // (e.g. when left-clicked) opens the context menu at the wrong place if the mouse
            // is moved!
            ContextMenu.Placement        = PlacementMode.AbsolutePoint;
            ContextMenu.HorizontalOffset = cursorPosition.X;
            ContextMenu.VerticalOffset   = cursorPosition.Y;
            ContextMenu.IsOpen           = true;

            IntPtr handle = IntPtr.Zero;

            // try to get a handle on the context itself
            HwndSource source = (HwndSource)PresentationSource.FromVisual(ContextMenu);

            if (source != null)
            {
                handle = source.Handle;
            }

            // if we don't have a handle for the popup, fall back to the message sink
            if (handle == IntPtr.Zero)
            {
                handle = messageSink.MessageWindowHandle;
            }

            // activate the context menu or the message window to track deactivation - otherwise, the context menu
            // does not close if the user clicks somewhere else. With the message window
            // fallback, the context menu can't receive keyboard events - should not happen though
            WinApi.SetForegroundWindow(handle);

            // bubble event
            RaiseTrayContextMenuOpenEvent();
        }
Пример #5
0
        public static Interop.Point GetMousePosition(TaskbarIcon icon)
        {
            Interop.Point cursorPosition = new Interop.Point();
            if (icon.MessageSink.Version == NotifyIconVersion.Vista)
            {
                //physical cursor position is supported for Vista and above
                WinApi.GetPhysicalCursorPos(ref cursorPosition);
            }
            else
            {
                WinApi.GetCursorPos(ref cursorPosition);
            }

            return(icon.GetDeviceCoordinates(cursorPosition));
        }
Пример #6
0
        /// <summary>
        /// Displays the <see cref="TrayPopup"/> control if
        /// it was set.
        /// </summary>
        private void ShowTrayPopup(Point cursorPosition)
        {
            if (IsDisposed)
            {
                return;
            }

            //raise preview event no matter whether popup is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayPopupOpenEvent();

            if (args.Handled)
            {
                return;
            }

            if (TrayPopup != null)
            {
                //use absolute position, but place the popup centered above the icon
                TrayPopupResolved.Placement        = PlacementMode.AbsolutePoint;
                TrayPopupResolved.HorizontalOffset = cursorPosition.X;
                TrayPopupResolved.VerticalOffset   = cursorPosition.Y;

                //open popup
                TrayPopupResolved.IsOpen = true;

                //activate the message window to track deactivation - otherwise, the context menu
                //does not close if the user clicks somewhere else
                WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);

                //raise attached event - item should never be null unless developers
                //changed the CustomPopup directly...
                if (TrayPopup != null)
                {
                    RaisePopupOpenedEvent(TrayPopup);
                }

                //bubble routed event
                RaiseTrayPopupOpenEvent();
            }
        }
Пример #7
0
        /// <summary>
        /// Displays the <see cref="TrayPopup"/> control if
        /// it was set.
        /// </summary>
        private void ShowTrayPopup(Point cursorPosition)
        {
            if (IsDisposed) return;

            //raise preview event no matter whether popup is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayPopupOpenEvent();
            if (args.Handled) return;

            if (TrayPopup != null)
            {
                //use absolute position, but place the popup centered above the icon
                TrayPopupResolved.Placement = PlacementMode.AbsolutePoint;
                TrayPopupResolved.HorizontalOffset = cursorPosition.X;
                TrayPopupResolved.VerticalOffset = cursorPosition.Y;

                //open popup
                TrayPopupResolved.IsOpen = true;

                IntPtr handle = IntPtr.Zero;
                if (TrayPopupResolved.Child != null)
                {
                    //try to get a handle on the popup itself (via its child)
                    HwndSource source = (HwndSource) PresentationSource.FromVisual(TrayPopupResolved.Child);
                    if (source != null) handle = source.Handle;
                }

                //if we don't have a handle for the popup, fall back to the message sink
                if (handle == IntPtr.Zero) handle = messageSink.MessageWindowHandle;

                //activate either popup or message sink to track deactivation.
                //otherwise, the popup does not close if the user clicks somewhere else
                WinApi.SetForegroundWindow(handle);

                //raise attached event - item should never be null unless developers
                //changed the CustomPopup directly...
                if (TrayPopup != null) RaisePopupOpenedEvent(TrayPopup);

                //bubble routed event
                RaiseTrayPopupOpenEvent();
            }
        }
Пример #8
0
        /// <summary>
        /// Processes mouse events, which are bubbled
        /// through the class' routed events, trigger
        /// certain actions (e.g. show a popup), or
        /// both.
        /// </summary>
        /// <param name="me">Event flag.</param>
        private void OnMouseEvent(MouseEvent me)
        {
            if (IsDisposed) return;

            switch (me)
            {
                case MouseEvent.MouseMove:
                    RaiseTrayMouseMoveEvent();
                    //immediately return - there's nothing left to evaluate
                    return;
                case MouseEvent.IconRightMouseDown:
                    RaiseTrayRightMouseDownEvent();
                    break;
                case MouseEvent.IconLeftMouseDown:
                    RaiseTrayLeftMouseDownEvent();
                    break;
                case MouseEvent.IconRightMouseUp:
                    RaiseTrayRightMouseUpEvent();
                    break;
                case MouseEvent.IconLeftMouseUp:
                    RaiseTrayLeftMouseUpEvent();
                    break;
                case MouseEvent.IconMiddleMouseDown:
                    RaiseTrayMiddleMouseDownEvent();
                    break;
                case MouseEvent.IconMiddleMouseUp:
                    RaiseTrayMiddleMouseUpEvent();
                    break;
                case MouseEvent.IconDoubleClick:
                    //cancel single click timer
                    singleClickTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    //bubble event
                    RaiseTrayMouseDoubleClickEvent();
                    break;
                case MouseEvent.BalloonToolTipClicked:
                    RaiseTrayBalloonTipClickedEvent();
                    break;
                default:
                    throw new ArgumentOutOfRangeException("me", "Missing handler for mouse event flag: " + me);
            }


            //get mouse coordinates
            Point cursorPosition = new Point();
            if (messageSink.Version == NotifyIconVersion.Vista)
            {
                //physical cursor position is supported for Vista and above
                WinApi.GetPhysicalCursorPos(ref cursorPosition);
            }
            else
            {
                WinApi.GetCursorPos(ref cursorPosition);
            }

            cursorPosition = GetDeviceCoordinates(cursorPosition);

            bool isLeftClickCommandInvoked = false;

            //show popup, if requested
            if (me.IsMatch(PopupActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show popup once we are sure it's not a double click
                    singleClickTimerAction = () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        ShowTrayPopup(cursorPosition);
                    };
                    singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show popup immediately
                    ShowTrayPopup(cursorPosition);
                }
            }


            //show context menu, if requested
            if (me.IsMatch(MenuActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show context menu once we are sure it's not a double click
                    singleClickTimerAction = () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        ShowContextMenu(cursorPosition);
                    };
                    singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show context menu immediately
                    ShowContextMenu(cursorPosition);
                }
            }

            //make sure the left click command is invoked on mouse clicks
            if (me == MouseEvent.IconLeftMouseUp && !isLeftClickCommandInvoked)
            {
                //show context menu once we are sure it's not a double click
                singleClickTimerAction =
                    () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                    };
                singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
            }
        }
Пример #9
0
        /// <summary>
        /// Displays the <see cref="ContextMenu"/> if
        /// it was set.
        /// </summary>
        private void ShowContextMenu(Point cursorPosition)
        {
            if (IsDisposed) return;

              //raise preview event no matter whether context menu is currently set
              //or not (enables client to set it on demand)
              var args = RaisePreviewTrayContextMenuOpenEvent();
              if (args.Handled) return;

              if (ContextMenu != null)
              {
            //use absolute position
            ContextMenu.Placement = PlacementMode.MousePoint;
            ContextMenu.IsOpen = true;

            //activate the message window to track deactivation - otherwise, the context menu
            //does not close if the user clicks somewhere else
            WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);

            //bubble event
            RaiseTrayContextMenuOpenEvent();
              }
        }
Пример #10
0
        /// <summary>
        /// Processes mouse events, which are bubbled
        /// through the class' routed events, trigger
        /// certain actions (e.g. show a popup), or
        /// both.
        /// </summary>
        /// <param name="me">Event flag.</param>
        private void OnMouseEvent(MouseEvent me)
        {
            if (this.IsDisposed)
            {
                return;
            }

            switch (me)
            {
            case MouseEvent.MouseMove:
                this.RaiseTrayMouseMoveEvent();
                //immediately return - there's nothing left to evaluate
                return;

            case MouseEvent.IconRightMouseDown:
                this.RaiseTrayRightMouseDownEvent();
                break;

            case MouseEvent.IconLeftMouseDown:
                this.RaiseTrayLeftMouseDownEvent();
                break;

            case MouseEvent.IconRightMouseUp:
                this.RaiseTrayRightMouseUpEvent();
                break;

            case MouseEvent.IconLeftMouseUp:
                this.RaiseTrayLeftMouseUpEvent();
                break;

            case MouseEvent.IconMiddleMouseDown:
                this.RaiseTrayMiddleMouseDownEvent();
                break;

            case MouseEvent.IconMiddleMouseUp:
                this.RaiseTrayMiddleMouseUpEvent();
                break;

            case MouseEvent.IconDoubleClick:
                //cancel single click timer
                this.singleClickTimer.Change(Timeout.Infinite, Timeout.Infinite);
                //bubble event
                this.RaiseTrayMouseDoubleClickEvent();
                break;

            case MouseEvent.BalloonToolTipClicked:
                this.RaiseTrayBalloonTipClickedEvent();
                break;
                //default:
                //    throw new ArgumentOutOfRangeException("me", "Missing handler for mouse event flag: " + me);
            }


            //get mouse coordinates
            Point cursorPosition = new Point();

            if (messageSink.Version == NotifyIconVersion.Vista)
            {
                //physical cursor position is supported for Vista and above
                WinApi.GetPhysicalCursorPos(ref cursorPosition);
            }
            else
            {
                WinApi.GetCursorPos(ref cursorPosition);
            }

            cursorPosition = this.GetDeviceCoordinates(cursorPosition);

            bool isLeftClickCommandInvoked = false;

            //show popup, if requested
            if (me.IsMatch(this.PopupActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show popup once we are sure it's not a double click
                    singleClickTimerAction = () =>
                    {
                        this.LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        this.ShowTrayPopup(cursorPosition);
                    };

                    this.singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show popup immediately
                    this.ShowTrayPopup(cursorPosition);
                }
            }


            //show context menu, if requested
            if (me.IsMatch(this.MenuActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show context menu once we are sure it's not a double click
                    singleClickTimerAction = () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        ShowContextMenu(cursorPosition);
                    };
                    singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show context menu immediately
                    ShowContextMenu(cursorPosition);
                }
            }

            //make sure the left click command is invoked on mouse clicks
            if (me == MouseEvent.IconLeftMouseUp && !isLeftClickCommandInvoked)
            {
                //show context menu once we are sure it's not a double click
                this.singleClickTimerAction = () =>
                {
                    this.LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                };

                this.singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
            }
        }
Пример #11
0
        /// <summary>
        /// Shows a custom control as a tooltip in the tray location.
        /// </summary>
        /// <param name="balloon"></param>
        /// <param name="animation">An optional animation for the popup.</param>
        /// <param name="timeout">The time after which the popup is being closed.
        /// Submit null in order to keep the balloon open inde
        /// </param>
        /// <exception cref="ArgumentNullException">If <paramref name="balloon"/>
        /// is a null reference.</exception>
        public void ShowCustomBalloon(UIElement balloon, PopupAnimation animation, int?timeout)
        {
            Dispatcher dispatcher = this.GetDispatcher();

            if (!dispatcher.CheckAccess())
            {
                dispatcher.InvokeAsync(
                    (() => ShowCustomBalloon(balloon, animation, timeout)),
                    DispatcherPriority.Normal
                    );
                return;
            }

            if (balloon == null)
            {
                throw new ArgumentNullException("balloon");
            }

            if (timeout.HasValue && timeout < 500)
            {
                throw new ArgumentOutOfRangeException("timeout", string.Format("Invalid timeout of {0} milliseconds. Timeout must be at least 500 ms", timeout));
            }

            if (this.IsDisposed)
            {
                throw new ObjectDisposedException(this.Name ?? this.GetType().Name);
            }

            //make sure we don't have an open balloon
            lock (this.SyncLock)
            {
                CloseBalloon();
            }

            // create an invisible popup that hosts the UIElement
            Popup popup = new Popup
            {
                AllowsTransparency = true
            };

            //provide the popup with the taskbar icon's data context
            UpdateDataContext(popup, null, DataContext);

            //don't animate by default - devs can use attached events or override
            popup.PopupAnimation = animation;

            //in case the balloon is cleaned up through routed events, the
            //control didn't remove the balloon from its parent popup when
            //if was closed the last time - just make sure it doesn't have
            //a parent that is a popup
            var parent = LogicalTreeHelper.GetParent(balloon) as Popup;

            if (parent != null)
            {
                parent.Child = null;
            }

            if (parent != null)
            {
                string msg =
                    "Cannot display control [{0}] in a new balloon popup - that control already has a parent. You may consider creating new balloons every time you want to show one.";
                msg = String.Format(msg, balloon);
                throw new InvalidOperationException(msg);
            }

            popup.Child = balloon;

            //don't set the PlacementTarget as it causes the popup to become hidden if the
            //TaskbarIcon's parent is hidden, too...
            //popup.PlacementTarget = this;

            popup.Placement = PlacementMode.AbsolutePoint;
            popup.StaysOpen = true;

            Point position = TrayInfo.GetTrayLocation();

            position = GetDeviceCoordinates(position);
            popup.HorizontalOffset = position.X - 1;
            popup.VerticalOffset   = position.Y - 1;

            //store reference
            lock (this.SyncLock)
            {
                SetCustomBalloon(popup);
            }

            //assign this instance as an attached property
            SetParentTaskbarIcon(balloon, this);

            //fire attached event
            RaiseBalloonShowingEvent(balloon, this);

            //display item
            popup.IsOpen = true;

            if (timeout.HasValue)
            {
                //register timer to close the popup
                balloonCloseTimer.Change(timeout.Value, Timeout.Infinite);
            }
        }
Пример #12
0
        /// <summary>
        /// Displays the <see cref="ContextMenu"/> if
        /// it was set.
        /// </summary>
        private void ShowContextMenu(Point cursorPosition)
        {
            if (IsDisposed)
            {
                return;
            }

            //raise preview event no matter whether context menu is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayContextMenuOpenEvent();

            if (args.Handled)
            {
                return;
            }

            if (ContextMenu != null)
            {
                //use absolute positioning. We need to set the coordinates, or a delayed opening
                //(e.g. when left-clicked) opens the context menu at the wrong place if the mouse
                //is moved!
                ContextMenu.Placement = PlacementMode.AbsolutePoint;

                /*http://stackoverflow.com/questions/28238856/wpf-notifyicon-context-menu-not-centered-on-tray-icon-at-100-dpi
                 * replace
                 * ContextMenu.HorizontalOffset = cursorPosition.X;
                 * ContextMenu.VerticalOffset = cursorPosition.Y;
                 * with*/
                var g      = Graphics.FromHwnd(IntPtr.Zero);
                var scaleX = g.DpiX / 96.0;
                var scaleY = g.DpiY / 96.0;
                ContextMenu.HorizontalOffset = cursorPosition.X / scaleX;
                ContextMenu.VerticalOffset   = cursorPosition.Y / scaleY;
                //end replace

                ContextMenu.IsOpen = true;

                IntPtr handle = IntPtr.Zero;

                //try to get a handle on the context itself
                HwndSource source = (HwndSource)PresentationSource.FromVisual(ContextMenu);
                if (source != null)
                {
                    handle = source.Handle;
                }

                //if we don't have a handle for the popup, fall back to the message sink
                if (handle == IntPtr.Zero)
                {
                    handle = messageSink.MessageWindowHandle;
                }

                //activate the context menu or the message window to track deactivation - otherwise, the context menu
                //does not close if the user clicks somewhere else. With the message window
                //fallback, the context menu can't receive keyboard events - should not happen though
                WinApi.SetForegroundWindow(handle);

                //bubble event
                RaiseTrayContextMenuOpenEvent();
            }
        }
Пример #13
0
        /// <summary>
        /// Shows a custom control as a tooltip in the tray location.
        /// </summary>
        /// <param name="balloon"></param>
        /// <param name="animation">An optional animation for the popup.</param>
        /// <param name="timeout">The time after which the popup is being closed.
        /// Submit null in order to keep the balloon open inde
        /// </param>
        /// <exception cref="ArgumentNullException">If <paramref name="balloon"/>
        /// is a null reference.</exception>
        public void ShowCustomBalloon(UIElement balloon, PopupAnimation animation, int?timeout)
        {
            Dispatcher dispatcher = this.GetDispatcher();

            if (!dispatcher.CheckAccess())
            {
                var action = new Action(() => ShowCustomBalloon(balloon, animation, timeout));
                dispatcher.Invoke(DispatcherPriority.Normal, action);
                return;
            }

            if (balloon == null)
            {
                throw new ArgumentNullException("balloon");
            }
            if (timeout.HasValue && timeout < 500)
            {
                string msg = "Invalid timeout of {0} milliseconds. Timeout must be at least 500 ms";
                msg = String.Format(msg, timeout);
                throw new ArgumentOutOfRangeException("timeout", msg);
            }

            EnsureNotDisposed();

            //make sure we don't have an open balloon
            lock (this)
            {
                CloseBalloon();
            }

            //create an invisible popup that hosts the UIElement
            Popup popup = new Popup();

            popup.AllowsTransparency = true;

            //provide the popup with the taskbar icon's data context
            UpdateDataContext(popup, null, DataContext);

            //don't animate by default - devs can use attached
            //events or override
            popup.PopupAnimation = animation;

            popup.Child = balloon;

            //don't set the PlacementTarget as it causes the popup to become hidden if the
            //TaskbarIcon's parent is hidden, too...
            //popup.PlacementTarget = this;

            popup.Placement = PlacementMode.AbsolutePoint;
            popup.StaysOpen = true;

            Point position = TrayInfo.GetTrayLocation();

            popup.HorizontalOffset = position.X - 1;
            popup.VerticalOffset   = position.Y - 1;

            //store reference
            lock (this)
            {
                SetCustomBalloon(popup);
            }

            //assign this instance as an attached property
            SetParentTaskbarIcon(balloon, this);

            //fire attached event
            RaiseBalloonShowingEvent(balloon, this);

            //display item
            popup.IsOpen = true;

            if (timeout.HasValue)
            {
                //register timer to close the popup
                balloonCloseTimer.Change(timeout.Value, Timeout.Infinite);
            }
        }
Пример #14
0
        /// <summary>
        /// Processes mouse events, which are bubbled
        /// through the class' routed events, trigger
        /// certain actions (e.g. show a popup), or
        /// both.
        /// </summary>
        /// <param name="me">Event flag.</param>
        private void OnMouseEvent(MouseEvent me)
        {
            if (IsDisposed)
            {
                return;
            }

            switch (me)
            {
            case MouseEvent.MouseMove:
                RaiseTrayMouseMoveEvent();
                //immediately return - there's nothing left to evaluate
                return;

            case MouseEvent.IconRightMouseDown:
                RaiseTrayRightMouseDownEvent();
                break;

            case MouseEvent.IconLeftMouseDown:
                RaiseTrayLeftMouseDownEvent();
                break;

            case MouseEvent.IconRightMouseUp:
                RaiseTrayRightMouseUpEvent();
                break;

            case MouseEvent.IconLeftMouseUp:
                RaiseTrayLeftMouseUpEvent();
                break;

            case MouseEvent.IconMiddleMouseDown:
                RaiseTrayMiddleMouseDownEvent();
                break;

            case MouseEvent.IconMiddleMouseUp:
                RaiseTrayMiddleMouseUpEvent();
                break;

            case MouseEvent.IconDoubleClick:
                //cancel single click timer
                singleClickTimer.Change(Timeout.Infinite, Timeout.Infinite);
                //bubble event
                RaiseTrayMouseDoubleClickEvent();
                break;

            case MouseEvent.BalloonToolTipClicked:
                RaiseTrayBalloonTipClickedEvent();
                break;

            default:
                throw new ArgumentOutOfRangeException("me", "Missing handler for mouse event flag: " + me);
            }


            //get mouse coordinates
            Point apiCursorPosition = new Point();

            WinApi.GetCursorPos(ref apiCursorPosition);
            var cursorPosition = apiCursorPosition.ToWpfPoint();


            bool isLeftClickCommandInvoked = false;

            //show popup, if requested
            if (me.IsMatch(PopupActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show popup once we are sure it's not a double click
                    delayedTimerAction = () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        ShowTrayPopup(cursorPosition);
                    };
                    singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show popup immediately
                    ShowTrayPopup(cursorPosition);
                }
            }


            //show context menu, if requested
            if (me.IsMatch(MenuActivation))
            {
                if (me == MouseEvent.IconLeftMouseUp)
                {
                    //show context menu once we are sure it's not a double click
                    delayedTimerAction = () =>
                    {
                        LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                        ShowContextMenu(cursorPosition);
                    };
                    singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
                    isLeftClickCommandInvoked = true;
                }
                else
                {
                    //show context menu immediately
                    ShowContextMenu(cursorPosition);
                }
            }

            //make sure the left click command is invoked on mouse clicks
            if (me == MouseEvent.IconLeftMouseUp && !isLeftClickCommandInvoked)
            {
                //show context menu once we are sure it's not a double click
                delayedTimerAction = () => LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
                singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
            }
        }
Пример #15
0
        /// <summary>
        /// Displays the <see cref="ContextMenu"/> if
        /// it was set.
        /// </summary>
        private void ShowContextMenu(Point cursorPosition)
        {
            if (IsDisposed) return;

            //raise preview event no matter whether context menu is currently set
            //or not (enables client to set it on demand)
            var args = RaisePreviewTrayContextMenuOpenEvent();
            if (args.Handled) return;

            if (ContextMenu != null)
            {
                //use absolute positioning. We need to set the coordinates, or a delayed opening
                //(e.g. when left-clicked) opens the context menu at the wrong place if the mouse
                //is moved!
                ContextMenu.Placement = PlacementMode.AbsolutePoint;
                ContextMenu.HorizontalOffset = cursorPosition.X;
                ContextMenu.VerticalOffset = cursorPosition.Y;
                ContextMenu.IsOpen = true;

                IntPtr handle = IntPtr.Zero;

                //try to get a handle on the context itself
                HwndSource source = (HwndSource) PresentationSource.FromVisual(ContextMenu);
                if (source != null)
                {
                    handle = source.Handle;
                }

                //if we don't have a handle for the popup, fall back to the message sink
                if (handle == IntPtr.Zero) handle = messageSink.MessageWindowHandle;

                //activate the context menu or the message window to track deactivation - otherwise, the context menu
                //does not close if the user clicks somewhere else. With the message window
                //fallback, the context menu can't receive keyboard events - should not happen though
                WinApi.SetForegroundWindow(handle);

                //bubble event
                RaiseTrayContextMenuOpenEvent();
            }
        }
Пример #16
0
        /// <summary>
        /// Recalculates OS coordinates in order to support WPFs coordinate
        /// system if OS scaling (DPIs) is not 100%.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private Point GetDeviceCoordinates(Point point)
        {
            if (double.IsNaN(scalingFactor))
            {
                //calculate scaling factor in order to support non-standard DPIs
                var presentationSource = PresentationSource.FromVisual(this);
                if (presentationSource == null)
                {
                    scalingFactor = 1;
                }
                else
                {
                    var transform = presentationSource.CompositionTarget.TransformToDevice;
                    scalingFactor = 1/transform.M11;
                }
            }

            //on standard DPI settings, just return the point
            if (scalingFactor == 1.0) return point;

            return new Point() {X = (int) (point.X*scalingFactor), Y = (int) (point.Y*scalingFactor)};
        }
Пример #17
0
        /// <summary>
        ///     Displays the <see cref="TrayPopup" /> control if
        ///     it was set.
        /// </summary>
        private void ShowTrayPopup(Point cursorPosition)
        {
            if (IsDisposed) return;

            //raise preview event no matter whether popup is currently set
            //or not (enables client to set it on demand)
            RoutedEventArgs args = RaisePreviewTrayPopupOpenEvent();
            if (args.Handled) return;

            if (TrayPopup != null)
            {
                //use absolute position, but place the popup centered above the icon
                TrayPopupResolved.Placement = PlacementMode.AbsolutePoint;
                TrayPopupResolved.HorizontalOffset = cursorPosition.X;
                TrayPopupResolved.VerticalOffset = cursorPosition.Y;

                //open popup
                TrayPopupResolved.IsOpen = true;

                //activate the message window to track deactivation - otherwise, the context menu
                //does not close if the user clicks somewhere else
                WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);

                //raise attached event - item should never be null unless developers
                //changed the CustomPopup directly...
                if (TrayPopup != null) RaisePopupOpenedEvent(TrayPopup);

                //bubble routed event
                RaiseTrayPopupOpenEvent();
            }
        }