WPF NotifyIcon 引用 TaskbarIcon(无窗口)
WPF NotifyIcon Reference the TaskbarIcon (Windowless)
我是 WPF NotifyIcon 的新手,我正在尝试使用无窗口示例,它使用 ResourceDictionary 而不是 window,并且 TaskbarIcon.DataContext 设置为我的 ViewModel。我可以调用示例命令(ShowWindowCommand 等),它工作正常。
但是,在我的 ViewModel 中,我不知道如何引用 TaskbarIcon。我想显示一个标准气球,类似于 NotifyIcon.ShowBallonTip(title, text, BalloonIcon.Error)。我试过给 tb:TaskbarIcon 一个 x:Name 但我的 ViewModel 仍然看不到它。
如何从我的 ViewModel 引用 TaskbarIcon?谢谢!
<tb:TaskbarIcon x:Key="NotifyIcon"
IconSource="/Red.ico"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}">
<tb:TaskbarIcon.DataContext>
<local:NotifyIconViewModel />
</tb:TaskbarIcon.DataContext>
</tb:TaskbarIcon>
在 MVVM 中,您无法直接在 VM 中操作控件。
VM 必须对 View 一无所知。而不是它,你必须在 VM 中定义 属性 Icon(我看到它是 NotifyIconViewModel),然后在 View (TaskbarIcon) 中你需要将它绑定到 IconSource。
虚拟机:
public Icon { get { new System.Drawing.Icon(@"..\Properties\Icons\YourIcon.ico"); }};
查看
<tb:TaskbarIcon DataContext="YourViewModel"
IconSource="{Binding Path=Icon}"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}"/>
这是一种适合我的实现...
MainWindow.xaml:
xmlns:tb="http://www.hardcodet.net/taskbar"
<tb:TaskbarIcon x:Name="TrayIcon" IconSource="icon.ico" ToolTipText="{Binding TrayIconText}" MenuActivation="RightClick"
LeftClickCommand="{Binding TrayClickCommand}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Exit" Command="{Binding CloseAppCommand}" CommandParameter="{Binding}">
<MenuItem.Icon>
<Image Source="pack://application:,,,/Resources/Img/Shutdown32.png" Style="{StaticResource MenuItemImage}"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
MainWindow.xaml.cs:
//Subscribe to VM event:
var mvm = DataContext as MainViewModel;
mvm.DisplayTrayBalloonNotice += OnDisplayTrayBalloon;
private void OnDisplayTrayBalloon(object sender, NotificationEventArgs<string> e)
{
var balloon = new TrayBalloon { NotificationText = e.Data };
PopupAnimation pa = PopupAnimation.Fade;
TrayIcon.ShowCustomBalloon(balloon, pa, Settings.Default.NotificationDuration * 1000);
}
TrayBalloon.xaml:
<UserControl x:Class="MCPublisher.Resources.TrayBalloon"
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"
Height="150" Width="300">
<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" />
</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 Storyboard="{StaticResource FadeIn}" x:Name="FadeInBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource HighlightCloseButton}" x:Name="HighlightCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource FadeCloseButton}" x:Name="FadeCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeInBeginStoryboard" />
<BeginStoryboard x:Name="FadeBackBeginStoryboard1" Storyboard="{StaticResource FadeBack}" />
</EventTrigger>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonClosing">
<BeginStoryboard Storyboard="{StaticResource FadeOut}" x:Name="FadeOutBeginStoryboard" />
</EventTrigger>
</UserControl.Triggers>
<Grid x:Name="Grid" MouseEnter="Grid_OnMouseEnter" MouseLeave="Grid_OnMouseLeave">
<Border x:Name="BnBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="1" CornerRadius="5"
BorderBrush="{DynamicResource GridView_FilteringControlOuterBorder}"
Background="{DynamicResource GridView_FilteringControlBackground}">
<Border.Effect>
<DropShadowEffect Color="#FF747474" />
</Border.Effect>
</Border>
<Image HorizontalAlignment="Left" Margin="10 10 0 0" Width="64" Height="64" Stretch="Fill" VerticalAlignment="Top"
Source="{Binding ImageSource}" />
<TextBlock Margin="84 35 10 0" VerticalAlignment="Top" FontSize="16" FontWeight="Bold" Foreground="#FF575757">
<Run Text="VHI Notification"/>
</TextBlock>
<Path Fill="#FFFFFFFF" Stretch="Fill" Margin="84 60 10 0" VerticalAlignment="Top" Height="1"
Data="M26,107 L220.04123,107" SnapsToDevicePixels="True">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.973,0.5" StartPoint="0.005,0.5">
<GradientStop Color="#FF6868FF" Offset="0" />
<GradientStop Color="#346868FF" Offset="1" />
</LinearGradientBrush>
</Path.Stroke>
</Path>
<TextBlock Margin="20 90 10 0" VerticalAlignment="Top" Height="24" TextWrapping="Wrap" FontSize="12" FontWeight="Bold">
<Run Text="⦁ "/>
<Run Text="{Binding NotificationText}"/>
</TextBlock>
<Image x:Name="ImgClose" HorizontalAlignment="Right" Margin="0 10 10 0" VerticalAlignment="Top" Width="16" Height="16"
Stretch="Fill" Opacity="0.4" ToolTip="Close Balloon" Source="pack://application:,,,/Resources/Img/Exit32.png"
MouseDown="ImgClose_OnMouseDown" />
</Grid>
</UserControl>
TrayBalloon.xaml.cs:
public partial class TrayBalloon : UserControl, INotifyPropertyChanged
{
private bool isClosing;
public TrayBalloon()
{
InitializeComponent();
DataContext = this;
TaskbarIcon.AddBalloonClosingHandler(this, OnBalloonClosing);
}
private string imageSource;
public string ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
OnPropertyChanged("ImageSource");
}
}
private string notificationText;
public string NotificationText
{
get { return notificationText; }
set
{
notificationText = value;
OnPropertyChanged("NotificationText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnBalloonClosing(object sender, RoutedEventArgs e)
{
e.Handled = true;
isClosing = true;
}
private void OnFadeOutCompleted(object sender, EventArgs e)
{
var pp = (Popup)Parent;
pp.IsOpen = false;
}
private void Grid_OnMouseEnter(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.ResetBalloonCloseTimer();
}
private void Grid_OnMouseLeave(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
private void ImgClose_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
}
我不确定这是否是在 ViewModel 中完成此操作的首选方法,但这就是我获得对 TaskbarIcon 的引用的方式。
tb = (任务栏图标)Application.Current.FindResource("MyTray");
我是 WPF NotifyIcon 的新手,我正在尝试使用无窗口示例,它使用 ResourceDictionary 而不是 window,并且 TaskbarIcon.DataContext 设置为我的 ViewModel。我可以调用示例命令(ShowWindowCommand 等),它工作正常。
但是,在我的 ViewModel 中,我不知道如何引用 TaskbarIcon。我想显示一个标准气球,类似于 NotifyIcon.ShowBallonTip(title, text, BalloonIcon.Error)。我试过给 tb:TaskbarIcon 一个 x:Name 但我的 ViewModel 仍然看不到它。
如何从我的 ViewModel 引用 TaskbarIcon?谢谢!
<tb:TaskbarIcon x:Key="NotifyIcon"
IconSource="/Red.ico"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}">
<tb:TaskbarIcon.DataContext>
<local:NotifyIconViewModel />
</tb:TaskbarIcon.DataContext>
</tb:TaskbarIcon>
在 MVVM 中,您无法直接在 VM 中操作控件。 VM 必须对 View 一无所知。而不是它,你必须在 VM 中定义 属性 Icon(我看到它是 NotifyIconViewModel),然后在 View (TaskbarIcon) 中你需要将它绑定到 IconSource。
虚拟机:
public Icon { get { new System.Drawing.Icon(@"..\Properties\Icons\YourIcon.ico"); }};
查看
<tb:TaskbarIcon DataContext="YourViewModel"
IconSource="{Binding Path=Icon}"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}"/>
这是一种适合我的实现...
MainWindow.xaml:
xmlns:tb="http://www.hardcodet.net/taskbar"
<tb:TaskbarIcon x:Name="TrayIcon" IconSource="icon.ico" ToolTipText="{Binding TrayIconText}" MenuActivation="RightClick"
LeftClickCommand="{Binding TrayClickCommand}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Exit" Command="{Binding CloseAppCommand}" CommandParameter="{Binding}">
<MenuItem.Icon>
<Image Source="pack://application:,,,/Resources/Img/Shutdown32.png" Style="{StaticResource MenuItemImage}"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
MainWindow.xaml.cs:
//Subscribe to VM event:
var mvm = DataContext as MainViewModel;
mvm.DisplayTrayBalloonNotice += OnDisplayTrayBalloon;
private void OnDisplayTrayBalloon(object sender, NotificationEventArgs<string> e)
{
var balloon = new TrayBalloon { NotificationText = e.Data };
PopupAnimation pa = PopupAnimation.Fade;
TrayIcon.ShowCustomBalloon(balloon, pa, Settings.Default.NotificationDuration * 1000);
}
TrayBalloon.xaml:
<UserControl x:Class="MCPublisher.Resources.TrayBalloon"
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"
Height="150" Width="300">
<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" />
</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 Storyboard="{StaticResource FadeIn}" x:Name="FadeInBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource HighlightCloseButton}" x:Name="HighlightCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource FadeCloseButton}" x:Name="FadeCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeInBeginStoryboard" />
<BeginStoryboard x:Name="FadeBackBeginStoryboard1" Storyboard="{StaticResource FadeBack}" />
</EventTrigger>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonClosing">
<BeginStoryboard Storyboard="{StaticResource FadeOut}" x:Name="FadeOutBeginStoryboard" />
</EventTrigger>
</UserControl.Triggers>
<Grid x:Name="Grid" MouseEnter="Grid_OnMouseEnter" MouseLeave="Grid_OnMouseLeave">
<Border x:Name="BnBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="1" CornerRadius="5"
BorderBrush="{DynamicResource GridView_FilteringControlOuterBorder}"
Background="{DynamicResource GridView_FilteringControlBackground}">
<Border.Effect>
<DropShadowEffect Color="#FF747474" />
</Border.Effect>
</Border>
<Image HorizontalAlignment="Left" Margin="10 10 0 0" Width="64" Height="64" Stretch="Fill" VerticalAlignment="Top"
Source="{Binding ImageSource}" />
<TextBlock Margin="84 35 10 0" VerticalAlignment="Top" FontSize="16" FontWeight="Bold" Foreground="#FF575757">
<Run Text="VHI Notification"/>
</TextBlock>
<Path Fill="#FFFFFFFF" Stretch="Fill" Margin="84 60 10 0" VerticalAlignment="Top" Height="1"
Data="M26,107 L220.04123,107" SnapsToDevicePixels="True">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.973,0.5" StartPoint="0.005,0.5">
<GradientStop Color="#FF6868FF" Offset="0" />
<GradientStop Color="#346868FF" Offset="1" />
</LinearGradientBrush>
</Path.Stroke>
</Path>
<TextBlock Margin="20 90 10 0" VerticalAlignment="Top" Height="24" TextWrapping="Wrap" FontSize="12" FontWeight="Bold">
<Run Text="⦁ "/>
<Run Text="{Binding NotificationText}"/>
</TextBlock>
<Image x:Name="ImgClose" HorizontalAlignment="Right" Margin="0 10 10 0" VerticalAlignment="Top" Width="16" Height="16"
Stretch="Fill" Opacity="0.4" ToolTip="Close Balloon" Source="pack://application:,,,/Resources/Img/Exit32.png"
MouseDown="ImgClose_OnMouseDown" />
</Grid>
</UserControl>
TrayBalloon.xaml.cs:
public partial class TrayBalloon : UserControl, INotifyPropertyChanged
{
private bool isClosing;
public TrayBalloon()
{
InitializeComponent();
DataContext = this;
TaskbarIcon.AddBalloonClosingHandler(this, OnBalloonClosing);
}
private string imageSource;
public string ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
OnPropertyChanged("ImageSource");
}
}
private string notificationText;
public string NotificationText
{
get { return notificationText; }
set
{
notificationText = value;
OnPropertyChanged("NotificationText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnBalloonClosing(object sender, RoutedEventArgs e)
{
e.Handled = true;
isClosing = true;
}
private void OnFadeOutCompleted(object sender, EventArgs e)
{
var pp = (Popup)Parent;
pp.IsOpen = false;
}
private void Grid_OnMouseEnter(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.ResetBalloonCloseTimer();
}
private void Grid_OnMouseLeave(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
private void ImgClose_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
}
我不确定这是否是在 ViewModel 中完成此操作的首选方法,但这就是我获得对 TaskbarIcon 的引用的方式。
tb = (任务栏图标)Application.Current.FindResource("MyTray");