How to set custom balloon position?
Asked Answered
F

1

6

I use Hardcodet WPF NotifyIcon to show custom ballons on some event.

If I create TaskbarIcon in xaml of MainWindow, then my balloon is placed near taskbar:

TaskbarIcon created in MainWindow

But when I create TaskbarIcon in resource file (xaml) or an application class, then my balloon is placed over taskbar:

enter image description here

Why there is the difference in behaviour between these cases and how to control position of custom balloons?

EDIT: I use next code to test it:

(App.xaml):

<Application x:Class="TestBalloon.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:tb="http://www.hardcodet.net/taskbar"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <tb:TaskbarIcon x:Key="TrayIcon" ToolTipText="Created From Resources" />
    </Application.Resources>
</Application>

(App.xaml.cs):

public partial class App : Application
{
    public TaskbarIcon AppTrayIcon;

    protected override void OnStartup(StartupEventArgs e)
    {
        AppTrayIcon = (TaskbarIcon)FindResource("TrayIcon");
    }
}

(MainWindow.xaml):

<Window x:Class="TestBalloon.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:tb="http://www.hardcodet.net/taskbar"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <tb:TaskbarIcon x:Name="MainWindowTrayIcon" ToolTipText="Created in MainWindow" />

        <Button x:Name="MyButton" 
                Content="ClickMe"
                Margin="10,10,10,10"
                Click="MyButton_OnClick"/>
    </Grid>
</Window>

(MainWindow.xaml.cs):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void MyButton_OnClick(object sender, RoutedEventArgs e)
    {
        FancyBalloon bal = new FancyBalloon(); // From Hardcodet WPF NotifyIcon Tutorial

        // To use TaskbarItem created in MainWindow.xaml
        //MainWindowTrayIcon.ShowCustomBalloon(bal, PopupAnimation.Slide, null);

        // To use TaskbarItem created in App.xaml
        ((App)Application.Current).AppTrayIcon.ShowCustomBalloon(bal, PopupAnimation.Slide, null);
    }
}
Firstclass answered 30/6, 2014 at 14:15 Comment(10)
Post the XAML in both cases.Laterite
Why there is the difference in behaviour between these cases... because you defined different code for them. Please show it.Inpour
There you go... you probably get a difference in appearance because you are using different code to launch them: AppTrayIcon = (TaskbarIcon)FindResource("TrayIcon"); and ((App)Application.Current).AppTrayIcon.ShowCustomBalloon(bal, PopupAnimation.Slide, null);.Inpour
@Sheridan: I do this only to get object (TaskbarIcon). AppTrayIcon is only name of reference on TaskbarIcon (created in App.xaml). Name of similar object (created in MainWindow.xaml) is MainWindowTrayIcon. What difference in running the code?Firstclass
Why there is the difference in behaviour between these cases... as previously requested, please show the code for both of your cases.Inpour
@Sheridan, code presented already for both cases, see last code block in post. Call of method ShowCustomBalloon for first case is commented, because in such case I did not launch both cases simultaniusly. So, first launch is MainWindowTrayIcon.ShowCustomBalloon(...), and second one is ((App)Application.Current).AppTrayIcon.ShowCustomBalloon(...).Firstclass
Apologies, my eyes must just automatically ignore comments. Now that I see it, I think that you've presented a good question. +1Inpour
Cannot reproduce the issue in Windows 8. Both popups display above the taskbar rather than beside of it.Decarlo
@Herdo, I try it on Win8. On one machine the behaviour was the same, but on another machine (also Win8) both balloons were near the taskbar. I'm confused.Firstclass
Is there any solution for this problem?Nubilous
A
0

I know, I'm being archeologist here, but hey, question has not been answered!

Tested on my setup, which is Windows 10 build 19045.3570, with WPF App both in .NET Framework 4.8 and .NET6 with Hardcodet.NotifyIcon.Wpf v.1.1.0 installed.

The problem is no longer there, in my case in both, calling the TaskBatItem created in App.xaml or in MainWindow.xaml, FancyBalloon was being showed correctly.

And btw in question there's FancyBalloon UserControl implementation missing (not my code, taken from https://www.codeproject.com/Articles/36468/WPF-NotifyIcon-2).

UI

<UserControl x:Class="WpfApp9.FancyBalloon"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:WpfApp9"
             xmlns:tb="http://www.hardcodet.net/taskbar"
             x:Name="me"
             Width="240"
             Height="120"
             mc:Ignorable="d">
    <UserControl.Resources>
        <Storyboard x:Key="FadeIn">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="grid"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.95"/>
                <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0.95"/>
                <!--                <SplineDoubleKeyFrame KeyTime="00:00:05" Value="0"/>-->
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="HighlightCloseButton">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="imgClose"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.4"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="FadeCloseButton">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="imgClose"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.4"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="FadeBack">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="grid"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="FadeOut" Completed="OnFadeOutCompleted">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                           Storyboard.TargetName="grid"
                                           Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.2"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </UserControl.Resources>
    <UserControl.Triggers>
        <EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonShowing">
            <BeginStoryboard x:Name="FadeIn_BeginStoryboard" Storyboard="{StaticResource FadeIn}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="imgClose">
            <BeginStoryboard x:Name="HighlightCloseButton_BeginStoryboard" Storyboard="{StaticResource HighlightCloseButton}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="imgClose">
            <BeginStoryboard x:Name="FadeCloseButton_BeginStoryboard" Storyboard="{StaticResource FadeCloseButton}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseEnter">
            <StopStoryboard BeginStoryboardName="FadeIn_BeginStoryboard"/>
            <BeginStoryboard x:Name="FadeBack_BeginStoryboard1" Storyboard="{StaticResource FadeBack}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonClosing">
            <BeginStoryboard x:Name="FadeOut_BeginStoryboard" Storyboard="{StaticResource FadeOut}"/>
        </EventTrigger>
    </UserControl.Triggers>
    <Grid x:Name="grid" MouseEnter="grid_MouseEnter">
        <Border Margin="5,5,5,5"
                HorizontalAlignment="Stretch"
                BorderThickness="1,1,1,1"
                BorderBrush="#FF997137">
            <Border.Effect>
                <DropShadowEffect Color="#FF747474"/>
            </Border.Effect>
            <Border.Background>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Offset="0" Color="#FF4B4B4B"/>
                    <GradientStop Offset="1" Color="#FF8F8F8F"/>
                </LinearGradientBrush>
            </Border.Background>
        </Border>
        <Image Width="72"
               Height="72"
               Margin="0,10,0,0"
               HorizontalAlignment="Left"
               VerticalAlignment="Top"
               Source="/Images/Info.png"
               Stretch="Fill"/>
        <TextBlock Margin="72,49.2,10,0"
                   VerticalAlignment="Top"
                   Foreground="#FFECAD25"
                   TextWrapping="Wrap">
            <Run Text="This is a user control. The animation uses the attached "/>
            <Run FontStyle="Italic"
                 FontWeight="Bold"
                 Text="BalloonShowing "/>
            <Run Text="event."/>
        </TextBlock>
        <Path Height="1"
              Margin="72,38.2,34,0"
              VerticalAlignment="Top"
              Fill="#FFFFFFFF"
              Stretch="Fill"
              Data="M26,107 L220.04123,107"
              SnapsToDevicePixels="True">
            <Path.Stroke>
                <LinearGradientBrush StartPoint="0.005,0.5" EndPoint="0.973,0.5">
                    <GradientStop Offset="1" Color="#00ECAD25"/>
                    <GradientStop Offset="0" Color="#87ECAD25"/>
                </LinearGradientBrush>
            </Path.Stroke>
        </Path>
        <TextBlock Height="23.2"
                   Margin="72,10,10,0"
                   VerticalAlignment="Top"
                   Text="{Binding Path=BalloonText, ElementName=me, Mode=Default}"
                   TextWrapping="Wrap"
                   Foreground="#FFECAD25"
                   FontWeight="Bold"/>
        <Image x:Name="imgClose"
               Width="16"
               Height="16"
               Margin="0,10,10,0"
               HorizontalAlignment="Right"
               VerticalAlignment="Top"
               Source="/Images/Close.png"
               Stretch="Fill"
               Opacity="0.4"
               ToolTip="Close Balloon"
               MouseDown="imgClose_MouseDown"/>
    </Grid>
</UserControl>

CodeBehind

using Hardcodet.Wpf.TaskbarNotification;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace WpfApp9
{
    public partial class FancyBalloon : UserControl
    {
        private bool isClosing = false;

        #region BalloonText dependency property

        /// <summary>
        /// Description
        /// </summary>
        public static readonly DependencyProperty BalloonTextProperty =
            DependencyProperty.Register("BalloonText",
                typeof(string),
                typeof(FancyBalloon),
                new FrameworkPropertyMetadata(""));

        /// <summary>
        /// A property wrapper for the <see cref="BalloonTextProperty"/>
        /// dependency property:<br/>
        /// Description
        /// </summary>
        public string BalloonText
        {
            get { return (string)GetValue(BalloonTextProperty); }
            set { SetValue(BalloonTextProperty, value); }
        }

        #endregion

        public FancyBalloon()
        {
            InitializeComponent();
            TaskbarIcon.AddBalloonClosingHandler(this, OnBalloonClosing);
        }


        /// <summary>
        /// By subscribing to the <see cref="TaskbarIcon.BalloonClosingEvent"/>
        /// and setting the "Handled" property to true, we suppress the popup
        /// from being closed in order to display the custom fade-out animation.
        /// </summary>
        private void OnBalloonClosing(object sender, RoutedEventArgs e)
        {
            e.Handled = true; //suppresses the popup from being closed immediately
            isClosing = true;
        }


        /// <summary>
        /// Resolves the <see cref="TaskbarIcon"/> that displayed
        /// the balloon and requests a close action.
        /// </summary>
        private void imgClose_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //the tray icon assigned this attached property to simplify access
            TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
            taskbarIcon.CloseBalloon();
        }

        /// <summary>
        /// If the users hovers over the balloon, we don't close it.
        /// </summary>
        private void grid_MouseEnter(object sender, MouseEventArgs e)
        {
            //if we're already running the fade-out animation, do not interrupt anymore
            //(makes things too complicated for the sample)
            if (isClosing) return;

            //the tray icon assigned this attached property to simplify access
            TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
            taskbarIcon.ResetBalloonCloseTimer();
        }


        /// <summary>
        /// Closes the popup once the fade-out animation completed.
        /// The animation was triggered in XAML through the attached
        /// BalloonClosing event.
        /// </summary>
        private void OnFadeOutCompleted(object sender, EventArgs e)
        {
            Popup pp = (Popup)Parent;
            pp.IsOpen = false;
        }
    }
}
Alcazar answered 8/11, 2023 at 12:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.