WPF NotifyIcon

--------------
CHG   DataContext now also set on ContextMenu property. Revamped DataContext handling, which
      now not only checks whether DataContext is not null, but also leaves DataContext of
      controls unchanged, if the DataContext is bound.
CHG   Some documentation changes and cleanup, added class diagram.

git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@100 9f600761-6f11-4665-b6dc-0185e9171623
This commit is contained in:
Philipp Sumi
2009-05-12 11:24:22 +00:00
parent 98a0017687
commit 34c589eac4
7 changed files with 142 additions and 44 deletions

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Hardcodet.Wpf.TaskbarNotification.TaskbarIcon">
<Position X="0.5" Y="0.5" Width="3.5" />
<Compartments>
<Compartment Name="Fields" Collapsed="true" />
<Compartment Name="Methods" Collapsed="true" />
<Compartment Name="Properties" Collapsed="true" />
</Compartments>
<TypeIdentifier>
<HashCode>N6qdVIeUdLmQtSUbiJhEGdYRjvJYXlhbEVBBKuPRO5s=</HashCode>
<FileName>TaskbarIcon.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@@ -1,4 +1,4 @@
// Interop code taken from Mike Marshall's AnyForm
// Some interop code taken from Mike Marshall's AnyForm
using System;
using System.Drawing;
@@ -12,11 +12,10 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop
/// </summary>
public static class TrayInfo
{
[DllImport("user32.dll")]
private static extern Int32 GetWindowLong(IntPtr hWnd, Int32 Offset);
/// <summary>
/// Gets the position of the system tray.
/// </summary>
/// <returns>Tray coordinates.</returns>
public static Point GetTrayLocation()
{
var info = new AppBarInfo();

View File

@@ -85,6 +85,7 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Diagrams\TaskbarIcon Overview.cd" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>

View File

@@ -53,5 +53,5 @@ using System.Windows.Markup;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]

View File

@@ -557,7 +557,24 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
#region DataContext dependency property override
#region DataContext dependency property override / target update
/// <summary>
/// Updates the <see cref="FrameworkElement.DataContextProperty"/> of a given
/// <see cref="FrameworkElement"/>. This method only updates target elements
/// that do not already have a data context of their own, and either assigns
/// the <see cref="FrameworkElement.DataContext"/> of the NotifyIcon, or the
/// NotifyIcon itself, if no data context was assigned at all.
/// </summary>
private void UpdateDataContext(FrameworkElement target, object oldDataContextValue, object newDataContextValue)
{
if (target != null && !target.IsDataContextDataBound() && Equals(oldDataContextValue, target.DataContext))
{
//assign own data context, if available. If there is no data
//context at all, assign NotifyIcon itself.
target.DataContext = newDataContextValue ?? this;
}
}
/// <summary>
/// A static callback listener which is being invoked if the
@@ -586,24 +603,58 @@ namespace Hardcodet.Wpf.TaskbarNotification
object newValue = e.NewValue;
object oldValue = e.OldValue;
//replace custom data context for popup and tooltip, if
//they are reflecting the data context's data context
var popup = TrayPopupResolved;
var toolTip = TrayToolTipResolved;
if (popup != null && Equals(popup.DataContext, oldValue))
{
popup.DataContext = newValue;
}
if (toolTip != null && Equals(toolTip.DataContext, oldValue))
{
toolTip.DataContext = newValue;
}
//replace custom data context for ToolTips, Popup, and
//ContextMenu
UpdateDataContext(TrayPopupResolved, oldValue, newValue);
UpdateDataContext(TrayToolTipResolved, oldValue, newValue);
UpdateDataContext(ContextMenu, oldValue, newValue);
}
#endregion
#region ContextMenu dependency property override
/// <summary>
/// A static callback listener which is being invoked if the
/// <see cref="FrameworkElement.ContextMenuProperty"/> dependency property has
/// been changed. Invokes the <see cref="OnContextMenuPropertyChanged"/>
/// instance method of the changed instance.
/// </summary>
/// <param name="d">The currently processed owner of the property.</param>
/// <param name="e">Provides information about the updated property.</param>
private static void ContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TaskbarIcon owner = (TaskbarIcon)d;
owner.OnContextMenuPropertyChanged(e);
}
/// <summary>
/// Releases the old and updates the new <see cref="ContextMenu"/> property
/// in order to reflect both the NotifyIcon's <see cref="FrameworkElement.DataContext"/>
/// property and have the <see cref="ParentTaskbarIconProperty"/> assigned.
/// </summary>
/// <param name="e">Provides information about the updated property.</param>
private void OnContextMenuPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
{
//remove the taskbar icon reference from the previously used element
SetParentTaskbarIcon((DependencyObject)e.OldValue, null);
}
if (e.NewValue != null)
{
//set this taskbar icon as a reference to the new tooltip element
SetParentTaskbarIcon((DependencyObject)e.NewValue, this);
}
UpdateDataContext((ContextMenu) e.NewValue, null, DataContext);
}
#endregion
#region DoubleClickCommand dependency property
@@ -1752,7 +1803,12 @@ namespace Hardcodet.Wpf.TaskbarNotification
//register change listener for the DataContext property
md = new FrameworkPropertyMetadata(new PropertyChangedCallback(DataContextPropertyChanged));
DataContextProperty.OverrideMetadata(typeof(TaskbarIcon), md);
//register change listener for the ContextMenu property
md = new FrameworkPropertyMetadata(new PropertyChangedCallback(ContextMenuPropertyChanged));
ContextMenuProperty.OverrideMetadata(typeof(TaskbarIcon), md);
}
}
}

View File

@@ -30,6 +30,7 @@ using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Threading;
using Hardcodet.Wpf.TaskbarNotification.Interop;
using Point=Hardcodet.Wpf.TaskbarNotification.Interop.Point;
@@ -182,14 +183,13 @@ namespace Hardcodet.Wpf.TaskbarNotification
{
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
popup.DataContext = DataContext;
UpdateDataContext(popup, null, DataContext);
//don't animate by default - devs can use attached
//events or override
@@ -314,8 +314,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
#region Process Incoming Mouse Events
/// <summary>
@@ -483,11 +481,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
//the ParentTaskbarIcon attached dependency property:
//tt.PlacementTarget = this;
//the tooltip (and implicitly its context) explicitly gets
//the DataContext of this instance. If there is no DataContext,
//the TaskbarIcon sets itself
tt.DataContext = DataContext ?? this;
//make sure the tooltip is invisible
tt.HasDropShadow = false;
tt.BorderThickness = new Thickness(0);
@@ -504,6 +497,13 @@ namespace Hardcodet.Wpf.TaskbarNotification
tt.Content = ToolTipText;
}
//the tooltip explicitly gets the DataContext of this instance.
//If there is no DataContext, the TaskbarIcon assigns itself
if (tt != null)
{
UpdateDataContext(tt, null, DataContext);
}
//store a reference to the used tooltip
SetTrayToolTipResolved(tt);
}
@@ -566,11 +566,9 @@ namespace Hardcodet.Wpf.TaskbarNotification
//events or override
popup.PopupAnimation = PopupAnimation.None;
//the tooltip (and implicitly its context) explicitly gets
//the DataContext of this instance. If there is no DataContext,
//the TaskbarIcon assigns itself
popup.DataContext = DataContext ?? this;
//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);
//do *not* set the placement target, as it causes the popup to become hidden if the
@@ -582,6 +580,13 @@ namespace Hardcodet.Wpf.TaskbarNotification
popup.StaysOpen = false;
}
//the popup explicitly gets the DataContext of this instance.
//If there is no DataContext, the TaskbarIcon assigns itself
if (popup != null)
{
UpdateDataContext(popup, null, DataContext);
}
//store a reference to the used tooltip
SetTrayPopupResolved(popup);
}

View File

@@ -27,6 +27,7 @@ using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Resources;
@@ -99,9 +100,10 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// Updates the taskbar icons with data provided by a given
/// <see cref="NotifyIconData"/> instance.
/// </summary>
/// <param name="data"></param>
/// <param name="command"></param>
/// <returns></returns>
/// <param name="data">Configuration settings for the NotifyIcon.</param>
/// <param name="command">Operation on the icon (e.g. delete the icon).</param>
/// <returns>True if the data was successfully written.</returns>
/// <remarks>See Shell_NotifyIcon documentation on MSDN for details.</remarks>
public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command)
{
return WriteIconData(ref data, command, data.ValidMembers);
@@ -112,10 +114,12 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// Updates the taskbar icons with data provided by a given
/// <see cref="NotifyIconData"/> instance.
/// </summary>
/// <param name="data"></param>
/// <param name="command"></param>
/// <param name="flags"></param>
/// <returns></returns>
/// <param name="data">Configuration settings for the NotifyIcon.</param>
/// <param name="command">Operation on the icon (e.g. delete the icon).</param>
/// <param name="flags">Defines which members of the <paramref name="data"/>
/// structure are set.</param>
/// <returns>True if the data was successfully written.</returns>
/// <remarks>See Shell_NotifyIcon documentation on MSDN for details.</remarks>
public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command, IconDataMembers flags)
{
//do nothing if in design mode
@@ -270,5 +274,21 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
/// <summary>
/// Checks whether the <see cref="FrameworkElement.DataContextProperty"/>
/// is bound or not.
/// </summary>
/// <param name="element">The element to be checked.</param>
/// <returns>True if the data context property is being managed by a
/// binding expression.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="element"/>
/// is a null reference.</exception>
public static bool IsDataContextDataBound(this FrameworkElement element)
{
if (element == null) throw new ArgumentNullException("element");
return element.GetBindingExpression(FrameworkElement.DataContextProperty) != null;
}
}
}