WPF NotifyIcon

--------------
CHG   Changed show/hide functionality in order to work better with showcase window (which is no longer the app's main window).

git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@105 9f600761-6f11-4665-b6dc-0185e9171623
This commit is contained in:
Philipp Sumi
2009-05-16 12:14:02 +00:00
parent 97ef369a02
commit c8c988bb17
16 changed files with 219 additions and 207 deletions

View File

@@ -10,6 +10,10 @@ Contact and Information: http://www.hardcodet.net
CHG DataContext is also assigned to ContextMenu, and properly coerced for
ToolTips and Popups. Also checks whether target item has a binding
on the DataContext (does not just override if DataContext is null).
Thanks Nic Pilllinger for pointing this one out.
CHG Popup creation no longer calls Popup.CreateRootPopup which tries to
bind to dependency properties that do not exist, thus causing debug
warnings. Thanks to Loic Berthollet for the hint.
CHG The LeftClickCommand now executes with a delay in order to mak sure
it's not a double-click.
FIX Removed debug output in WindowMessageSink.

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Hardcodet.Wpf.TaskbarNotification.TaskbarIcon">
<Position X="1" Y="0.5" Width="3.5" />
<Position X="1.75" Y="0.5" Width="3.5" />
<Compartments>
<Compartment Name="Fields" Collapsed="true" />
<Compartment Name="Properties" Collapsed="true" />
<Compartment Name="Methods" Collapsed="true" />
</Compartments>
<TypeIdentifier>
<HashCode>N6qdVIeUdLmQtSUbiJhEGdYRjvJYXlhbEVBDKuPRO5s=</HashCode>

View File

@@ -196,7 +196,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
//events or override
popup.PopupAnimation = animation;
Popup.CreateRootPopup(popup, balloon);
popup.Child = balloon;
//don't set the PlacementTarget as it causes the popup to become hidden if the
//TaskbarIcon's parent is hidden, too...
@@ -589,9 +589,9 @@ namespace Hardcodet.Wpf.TaskbarNotification
popup.PopupAnimation = PopupAnimation.None;
//the CreateRootPopup method outputs binding errors in the debug window because
//it tries to bind to "Popup-specific" properties in case they are provided by the child
//not a problem.
Popup.CreateRootPopup(popup, TrayPopup);
//it tries to bind to "Popup-specific" properties in case they are provided by the child.
//We don't need that so just assign the control as the child.
popup.Child = TrayPopup;
//do *not* set the placement target, as it causes the popup to become hidden if the
//TaskbarIcon's parent is hidden, too. At runtime, the parent can be resolved through

View File

@@ -3,6 +3,8 @@ using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using Hardcodet.Wpf.TaskbarNotification;
namespace Samples.Commands
{
@@ -73,5 +75,84 @@ namespace Samples.Commands
.Metadata.DefaultValue;
}
}
/// <summary>
/// Resolves the window that owns the TaskbarIcon class.
/// </summary>
/// <param name="commandParameter"></param>
/// <returns></returns>
protected Window GetTaskbarWindow(object commandParameter)
{
if (IsDesignMode) return null;
//get the showcase window off the taskbaricon
var tb = commandParameter as TaskbarIcon;
return tb == null ? null : TryFindParent<Window>(tb);
}
#region TryFindParent helper
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Keep in mind that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//if it's not a ContentElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
#endregion
}
}

View File

@@ -6,19 +6,20 @@ namespace Samples.Commands
/// <summary>
/// Hides the main window.
/// </summary>
public class HideMainWindowCommand : CommandBase<HideMainWindowCommand>
public class HideSampleWindowCommand : CommandBase<HideSampleWindowCommand>
{
public override void Execute(object parameter)
{
Application.Current.MainWindow.Hide();
GetTaskbarWindow(parameter).Hide();
CommandManager.InvalidateRequerySuggested();
}
public override bool CanExecute(object parameter)
{
return !IsDesignMode && Application.Current.MainWindow.IsVisible;
Window win = GetTaskbarWindow(parameter);
return win != null && win.IsVisible;
}

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace Samples.Commands
{
/// <summary>
/// Shows the main window.
/// </summary>
public class ShowMainWindowCommand : CommandBase<ShowMainWindowCommand>
{
public override void Execute(object parameter)
{
Application.Current.MainWindow.Show();
CommandManager.InvalidateRequerySuggested();
}
public override bool CanExecute(object parameter)
{
return !IsDesignMode && Application.Current.MainWindow.IsVisible == false;
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Windows;
using System.Windows.Input;
namespace Samples.Commands
{
/// <summary>
/// Shows the main window.
/// </summary>
public class ShowSampleWindowCommand : CommandBase<ShowSampleWindowCommand>
{
public override void Execute(object parameter)
{
GetTaskbarWindow(parameter).Show();
CommandManager.InvalidateRequerySuggested();
}
public override bool CanExecute(object parameter)
{
Window win = GetTaskbarWindow(parameter);
return win != null && !win.IsVisible;
}
}
}

View File

@@ -3,8 +3,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main"
Height="595"
Width="596"
Height="563"
Width="703"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
@@ -31,8 +31,7 @@
FontStyle="Italic"
FontWeight="Bold"
TextWrapping="Wrap"><Run
Text="WPF NotifyIcon 1.0.1 - Samples"
/></TextBlock>
Text="WPF NotifyIcon 1.0.1 - Samples" /></TextBlock>
<Button
HorizontalAlignment="Left"
Margin="10,133,0,0"
@@ -48,8 +47,7 @@
VerticalAlignment="Top"
FontWeight="Bold"
TextWrapping="Wrap"><Run
Text="Tutorials:"
/></TextBlock>
Text="Tutorials:" /></TextBlock>
<TextBlock
HorizontalAlignment="Left"
Margin="10,75.96,0,0"
@@ -58,8 +56,7 @@
Width="224.31"
Height="47.04"
d:LayoutOverrides="HorizontalAlignment"><Run
Text="Tutorials follow the contents of the CodeProject article. Check the &quot;Tutorials&quot; folder for the source code."
/></TextBlock>
Text="Tutorials follow the contents of the CodeProject article. Check the &quot;Tutorials&quot; folder for the source code." /></TextBlock>
<Button
HorizontalAlignment="Left"
Margin="10,170,0,0"
@@ -115,59 +112,53 @@
x:Name="btnToolTipControl"
Click="btnToolTipControl_Click" />
<TextBlock
Margin="0,50,166.627,0"
Margin="0,50,328.76,0"
VerticalAlignment="Top"
FontWeight="Bold"
TextWrapping="Wrap"
HorizontalAlignment="Right"
d:LayoutOverrides="HorizontalAlignment, Width"><Run
Text="Showcase Sample:"
/></TextBlock>
Text="Showcase Sample:" /></TextBlock>
<TextBlock
Margin="0,75.96,90.247,0"
Margin="255.31,75.96,145.38,0"
VerticalAlignment="Top"
TextWrapping="Wrap"
Height="47.04"
HorizontalAlignment="Right"
Width="179.31"><Run
Text="An interactive sample that shows off most features on a single NotifyIcon."
/></TextBlock>
Height="47.04"><Run
Text="An interactive sample that shows off most features on a single NotifyIcon." /></TextBlock>
<Button
HorizontalAlignment="Right"
Margin="0,133,105.557,0"
Margin="255.31,133,0,0"
VerticalAlignment="Top"
Width="164"
Height="27"
Content="Open Sample Window"
x:Name="btnMainSample"
Click="btnMainSample_Click" />
Click="btnMainSample_Click"
Width="164"
HorizontalAlignment="Left" />
<Path
Fill="#FFFFFFFF"
Stretch="Fill"
Stroke="#FF60758A"
HorizontalAlignment="Left"
Margin="269,57,0,172"
Margin="244.31,50,0,66"
Width="1"
Data="M269,57 L269,390.18163" />
<TextBlock
Margin="10,413,20,0"
Margin="255.31,191,10,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Height="102.76"><Run
Text="You will always find the latest news related to the component on the project page:"
/><LineBreak />
<Hyperlink
NavigateUri="http://www.hardcodet.net/wpf-notifyicon">http://www.hardcodet.net/wpf-notifyicon</Hyperlink>
<LineBreak /><Run
Text=""
/><LineBreak /><Run
Text="Critical feedback is appreciated - please post bug reports, requests, questions etc. to the CodeProject forum @"
/>
<Hyperlink
NavigateUri="http://www.codeproject.com/wpf-notifyicon">http://www.codeproject.com/wpf-notifyicon</Hyperlink>
<LineBreak /><Run
Text="And if you love it or hate it, please let me know and leave your rating - thanks!"
/></TextBlock>
VerticalAlignment="Top"><Run
Text="You will always find the latest news related to the component on the project page:" /><LineBreak /><Hyperlink
NavigateUri="http://www.hardcodet.net/wpf-notifyicon"><Run
Text="http://www.hardcodet.net/wpf-notifyicon" /></Hyperlink><LineBreak /><Run
Text="" /><LineBreak /><Run
Text="Critical feedback is appreciated - please post bug reports, requests, questions etc. to the CodeProject forum" /><Run
Language="de-ch"
Text=":" /><LineBreak /><Hyperlink
NavigateUri="http://www.codeproject.com/KB/WPF/wpf_notifyicon.aspx"><Run
Text="http://www.codeproject.com/KB/WPF/wpf_notifyicon.aspx" /></Hyperlink><LineBreak /><Run
Text="" /><LineBreak /><Run
Language="de-ch"
Text="I" /><Run
Text="f you love it or hate it, please let me know and leave your rating - thanks!" /></TextBlock>
<TextBlock
Margin="10,0,10,10"
VerticalAlignment="Bottom"
@@ -186,6 +177,24 @@
VerticalAlignment="Bottom"
Height="1"
Data="M11,517 L561.07363,517" />
<Button
HorizontalAlignment="Left"
Margin="11,397,0,0"
Width="164"
Content="Events"
VerticalAlignment="Top"
Height="27"
x:Name="btnEvents"
Click="btnEvents_Click" />
<Button
HorizontalAlignment="Left"
Margin="11,434,0,0"
Width="164"
Content="Data Binding"
VerticalAlignment="Top"
Height="27"
x:Name="btnDataBinding"
Click="btnDataBinding_Click" />
</Grid>
</Window>

View File

@@ -16,6 +16,8 @@ using Samples.Tutorials;
using Samples.Tutorials.Balloons;
using Samples.Tutorials.Commands;
using Samples.Tutorials.ContextMenus;
using Samples.Tutorials.DataBinding;
using Samples.Tutorials.Events;
using Samples.Tutorials.Popups;
using Samples.Tutorials.ToolTips;
@@ -82,6 +84,16 @@ namespace Samples
ShowDialog(new CommandWindow());
}
private void btnEvents_Click(object sender, RoutedEventArgs e)
{
ShowDialog(new EventVisualizerWindow());
}
private void btnDataBinding_Click(object sender, RoutedEventArgs e)
{
ShowDialog(new DataBoundToolTipWindow());
}
private void btnMainSample_Click(object sender, RoutedEventArgs e)
{
var sampleWindow = new ShowcaseWindow();
@@ -98,8 +110,5 @@ namespace Samples
e.Handled = true;
}
}
}

View File

@@ -155,8 +155,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Commands\CommandBase.cs" />
<Compile Include="Commands\HideMainWindowCommand.cs" />
<Compile Include="Commands\ShowMainWindowCommand.cs" />
<Compile Include="Commands\HideSampleWindowCommand.cs" />
<Compile Include="Commands\ShowSampleWindowCommand.cs" />
<Compile Include="Showcase\FancyBalloon.xaml.cs">
<DependentUpon>FancyBalloon.xaml</DependentUpon>
</Compile>

View File

@@ -27,9 +27,14 @@
</Setter>
</Style>
<!-- The taskbar context menu - the first row is a dummy to show of simple data binding -->
<!--
The "shared" directive is needed if we reopen the sample a few times - WPF will otherwise
reuse the same context menu again (which will have its DataContext set to the old TaskbarIcon)
-->
<ContextMenu
x:Shared="false"
x:Key="tbMenu">
<MenuItem
IsEnabled="False"
@@ -43,8 +48,9 @@
</MenuItem>
<MenuItem
Header="Show Main Window"
Command="{Commands:ShowMainWindowCommand}">
Header="Show Showcase Window"
Command="{Commands:ShowSampleWindowCommand}"
CommandParameter="{Binding}">
<MenuItem.Icon>
<Image
Width="16"
@@ -56,8 +62,9 @@
<Separator />
<MenuItem
Header="Hide Main Window"
Command="{Commands:HideMainWindowCommand}">
Header="Hide Showcase Window"
Command="{Commands:HideSampleWindowCommand}"
CommandParameter="{Binding}">
<MenuItem.Icon>
<Image
Width="16"
@@ -68,88 +75,4 @@
</ContextMenu>
<ToolTip
x:Key="tbToolTip"
Background="Transparent"
BorderThickness="0"
HasDropShadow="False"
VerticalOffset="-10"
>
<Grid
>
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Height="Auto"
CornerRadius="6,6,6,6"
BorderThickness="3,3,3,3"
Margin="0,0,5,5">
<Border.Effect>
<DropShadowEffect
Color="#FF7A7A7A" />
</Border.Effect>
<Border.Background>
<LinearGradientBrush
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop
Color="#FFFFD283"
Offset="0" />
<GradientStop
Color="#FFFFFFFF"
Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
<Image
HorizontalAlignment="Left"
Margin="10,10,0,26"
Width="72"
Source="Images\Info.png"
Stretch="Fill"
VerticalAlignment="Top"
RenderTransformOrigin="0.792,0.486" />
<TextBlock
Margin="82,10,20,0"
TextWrapping="Wrap"
Height="32"
VerticalAlignment="Top"
FontSize="16"
FontWeight="Bold"
Foreground="#FF575757"><Run
Text="This is a fancy ToolTip..."
Language="de-ch" /></TextBlock>
<TextBlock
FontSize="12"
FontWeight="Normal"
Foreground="#FF141414"
TextWrapping="Wrap"
Margin="82,52,20,0"
VerticalAlignment="Top"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tb:TaskbarIcon}}, Path=ToolTipText}"
/>
</Grid>
</ToolTip>
<!--
As we don't declare the popup with the control, use
commands to trigger an action.
-->
<Popup
x:Key="tbPopupSimple"
PopupAnimation="Slide"
Placement="Mouse"
AllowsTransparency="True"
StaysOpen="False"
>
<Border
Width="200"
Height="200"
Background="Red">
<Button>Click me</Button>
</Border>
</Popup>
</ResourceDictionary>

View File

@@ -70,7 +70,9 @@
Visibility="{Binding Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=iconVisibility, Mode=Default}"
MenuActivation="{Binding Path=SelectedItem, ElementName=lstMenuTrigger, Mode=Default}"
PopupActivation="{Binding Path=SelectedItem, ElementName=lstPopupTrigger, Mode=Default}"
DoubleClickCommand="{Commands:ShowMainWindowCommand}">
DoubleClickCommand="{Commands:ShowSampleWindowCommand}"
DoubleClickCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
>
<tb:TaskbarIcon.TrayPopup>
<!-- the control will be put into a popup with an explicit DataContext -->

View File

@@ -253,9 +253,7 @@
VerticalAlignment="Top"
Height="31"
TextWrapping="Wrap"
FontWeight="Bold"><Run
Language="de-ch"
Text="The green elipses are animated based on routed events of the NotifyIcon" /></TextBlock>
FontWeight="Bold"><Run Language="de-ch" Text="The green ellipses are animated based on routed events of the NotifyIcon"/></TextBlock>
</Grid>
</Window>

View File

@@ -1,15 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows;
namespace Samples.Tutorials.Events
{

View File

@@ -4,12 +4,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
Height="300"
Width="300">
Width="500">
<Grid>
<!-- the ToolTipText property is bound to the TextBox below -->
<tb:TaskbarIcon
x:Name="MyNotifyIcon"
x:Name="MyNotifyIcon1"
IconSource="/Icons/Error.ico"
ToolTipText="{Binding ElementName=txtToolTip, Path=Text}">
@@ -36,16 +36,19 @@
</tb:TaskbarIcon>
<!-- the ToolTipText property is bound to the TextBox below -->
<!-- This NotifyIcon has its DataContext set - implicit binding is no longer possible -->
<tb:TaskbarIcon
x:Name="MyNotifyIcon2"
DataContext="WPF IS GREAT: "
IconSource="/Icons/Inactive.ico"
ToolTipText="{Binding ElementName=txtToolTip, Path=Text}">
<!--
The TextBlock bound to the ToolTipText property of the NotifyIcon
The binding is explicit (via attached ParentTaskbarIcon property)
-->
<tb:TaskbarIcon.TrayToolTip>
<!--
Important: The attached property is assigned to the border!
The NotifyIcon does not touch the underlying controls.
-->
<Border
Background="White"
BorderBrush="Orange"
@@ -54,10 +57,16 @@
Opacity="0.8"
Width="160"
Height="40">
<!-- Implicitly access the DataContext (which is a string this time)-->
<TextBlock Text="{Binding}">
<!-- Explicitly access the NotifyIcon -->
<TextBlock
Text="{Binding (tb:TaskbarIcon.ParentTaskbarIcon), Path=ToolTipText}"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Border}},
Path=(tb:TaskbarIcon.ParentTaskbarIcon).ToolTipText}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</TextBlock>
</Border>
</tb:TaskbarIcon.TrayToolTip>

View File

@@ -1,15 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows;
namespace Samples.Tutorials.DataBinding
{
@@ -27,9 +16,10 @@ namespace Samples.Tutorials.DataBinding
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
//clean up notifyicon (would otherwise stay open until application finishes)
MyNotifyIcon.Dispose();
MyNotifyIcon1.Dispose();
MyNotifyIcon2.Dispose();
base.OnClosing(e);
}
}
}
}