/// <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; Point position = TrayInfo.GetTrayLocation(); TrayPopupResolved.HorizontalOffset = position.X - 20; TrayPopupResolved.VerticalOffset = position.Y - 1; //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(); } }
public ChimeHelperTray(TaskbarIcon trayIcon) { _tray = trayIcon; _tray.DataContext = MEETINGS_LOADING; // workaround for an apparent bug in TrayIcon where the first menu appearance // often happens in one of the corners of the screen. Calling GetTrayLocation // seems to wake Windows up to the real location which is then used subsequently. TrayInfo.GetTrayLocation(); }
/// <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); } }
/// <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) { if (!Application.Current.Dispatcher.CheckAccess()) { var action = new Action(() => ShowCustomBalloon(balloon, animation, timeout)); Application.Current.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); } return; }
public Point GetPopupTrayPosition() { return(TrayInfo.GetTrayLocation()); }
public static MessageToken <TipItem> ShowMessage(System.Windows.FrameworkElement balloon, ObservableCollection <TipItem> data, int?timeout, NotifyPosition position = NotifyPosition.ActiveScreen, PopupAnimation animation = PopupAnimation.None) { var dispatcher = balloon.Dispatcher; if (!dispatcher.CheckAccess()) { return(dispatcher.Invoke(DispatcherPriority.Normal, (Func <MessageToken <TipItem> >)(() => ShowMessage(balloon, data, timeout, position, animation))) as MessageToken <TipItem>); } if (balloon == null) { throw new ArgumentNullException("balloon"); } if (timeout.HasValue && timeout < 500) { var msg = "Invalid timeout of {0} milliseconds. Timeout must be at least 500 ms"; msg = String.Format(msg, timeout); throw new ArgumentOutOfRangeException("timeout", msg); } if (LogicalTreeHelper.GetParent(balloon) is Popup parent) { parent.Child = null; var 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); } var popup = new Popup(); popup.AllowsTransparency = true; popup.PopupAnimation = animation; popup.Placement = PlacementMode.AbsolutePoint; popup.StaysOpen = true; popup.DataContext = data; Point point; switch (position) { case NotifyPosition.Caret: { var rect = Utils.Window.GetCurrentWindowCaretPosition(); var X = (rect.Left + rect.Width / 2 - balloon.ActualWidth / 2); var Y = (rect.Bottom + rect.Height / 2 - balloon.ActualHeight / 2); if (X == 0 && Y == 0) { goto case NotifyPosition.ActiveWindowCenter; } point = new Point(X, Y); break; } case NotifyPosition.ActiveWindowCenter: { var rect = Utils.Window.GetCurrentWindowRect(); var X = (rect.X + rect.Width / 2 - balloon.ActualWidth / 2); var Y = (rect.Y + rect.Height / 2 - balloon.ActualHeight / 2); point = new Point(X, Y); break; } case NotifyPosition.ActiveScreen: { var screen = Screen.FromHandle(Utils.Window.CurrentWindowHandle); if (screen.Equals(Screen.PrimaryScreen)) { var p = TrayInfo.GetTrayLocation(); point = new Point(p.X, p.Y); break; } var bounds = screen.Bounds; var X = bounds.X + bounds.Width; var Y = bounds.Y + bounds.Height; point = new Point(X, Y); break; } case NotifyPosition.Default: { var p = TrayInfo.GetTrayLocation(); point = new Point(p.X, p.Y); break; } default: throw new ArgumentOutOfRangeException(nameof(position) + " not supported", position, null); } popup.Child = balloon; popup.HorizontalOffset = point.X + 1; popup.VerticalOffset = point.Y - 2; balloon.Focusable = true; IInputElement element = null; popup.Opened += (s, a) => { element = Keyboard.FocusedElement; var source = (HwndSource)PresentationSource.FromVisual(balloon); var handle = source.Handle; PInvokes.SetForegroundWindow(handle); Keyboard.Focus(balloon); }; popup.IsOpen = true; popup.Focus(); var r = new MessageToken <TipItem>(popup); popup.Closed += (s, a) => { Keyboard.Focus(element); r.Close(); }; void TimerTick(object sender, EventArgs e) { r.Timer.Tick -= TimerTick; r.Close(); } if (timeout.HasValue) { r.Timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(timeout.Value) }; r.Timer.Tick += TimerTick; r.Timer.Start(); } return(r); }