我使用 Hardcodet WPF NotifyIcon 在某些事件上显示自定义气球。
如果我在MainWindow的xaml中创建
TaskbarIcon
,那么我的气球就会放置在任务栏附近:
但是当我在资源文件(xaml)或应用程序类中创建
TaskbarIcon
时,我的气球就会放置在任务栏上:
为什么这些情况之间的行为存在差异以及如何控制自定义气球的位置?
编辑:我使用下一个代码来测试它:
(应用程序.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>
(应用程序.xaml.cs):
public partial class App : Application
{
public TaskbarIcon AppTrayIcon;
protected override void OnStartup(StartupEventArgs e)
{
AppTrayIcon = (TaskbarIcon)FindResource("TrayIcon");
}
}
(主窗口.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);
}
}
我知道,我是这里的考古学家,但是嘿,问题还没有得到解答!
在我的设置上进行了测试,即 Windows 10 build 19045.3570,在 .NET Framework 4.8 和 .NET6 中安装了 WPF App,并安装了 Hardcodet.NotifyIcon.Wpf v.1.1.0。
问题不再存在,在我的情况下,调用在
TaskBatItem
或App.xaml
中创建的MainWindow.xaml
,FancyBalloon
已正确显示。
顺便说一句,有问题的是
FancyBalloon UserControl
实现缺失(不是我的代码,取自https://www.codeproject.com/Articles/36468/WPF-NotifyIcon-2)。
<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>
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;
}
}
}