From 34c589eac41628235dd83a0d019b34233b25cb96 Mon Sep 17 00:00:00 2001 From: Philipp Sumi Date: Tue, 12 May 2009 11:24:22 +0000 Subject: [PATCH] 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 --- .../Diagrams/TaskbarIcon Overview.cd | 17 ++++ Source/NotifyIconWpf/Interop/TrayInfo.cs | 11 ++- Source/NotifyIconWpf/NotifyIconWpf.csproj | 1 + .../NotifyIconWpf/Properties/AssemblyInfo.cs | 4 +- .../NotifyIconWpf/TaskbarIcon.Declarations.cs | 86 +++++++++++++++---- Source/NotifyIconWpf/TaskbarIcon.cs | 33 ++++--- Source/NotifyIconWpf/Util.cs | 34 ++++++-- 7 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 Source/NotifyIconWpf/Diagrams/TaskbarIcon Overview.cd diff --git a/Source/NotifyIconWpf/Diagrams/TaskbarIcon Overview.cd b/Source/NotifyIconWpf/Diagrams/TaskbarIcon Overview.cd new file mode 100644 index 0000000..dc1f29e --- /dev/null +++ b/Source/NotifyIconWpf/Diagrams/TaskbarIcon Overview.cd @@ -0,0 +1,17 @@ + + + + + + + + + + + N6qdVIeUdLmQtSUbiJhEGdYRjvJYXlhbEVBBKuPRO5s= + TaskbarIcon.cs + + + + + \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/TrayInfo.cs b/Source/NotifyIconWpf/Interop/TrayInfo.cs index ded285b..34ea681 100644 --- a/Source/NotifyIconWpf/Interop/TrayInfo.cs +++ b/Source/NotifyIconWpf/Interop/TrayInfo.cs @@ -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 /// public static class TrayInfo { - - [DllImport("user32.dll")] - private static extern Int32 GetWindowLong(IntPtr hWnd, Int32 Offset); - - + /// + /// Gets the position of the system tray. + /// + /// Tray coordinates. public static Point GetTrayLocation() { var info = new AppBarInfo(); diff --git a/Source/NotifyIconWpf/NotifyIconWpf.csproj b/Source/NotifyIconWpf/NotifyIconWpf.csproj index 914b6b0..e7b3757 100644 --- a/Source/NotifyIconWpf/NotifyIconWpf.csproj +++ b/Source/NotifyIconWpf/NotifyIconWpf.csproj @@ -85,6 +85,7 @@ ResXFileCodeGenerator Resources.Designer.cs + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/Source/NotifyIconWpf/Properties/AssemblyInfo.cs b/Source/NotifyIconWpf/Properties/AssemblyInfo.cs index df1277c..e49731a 100644 --- a/Source/NotifyIconWpf/Properties/AssemblyInfo.cs +++ b/Source/NotifyIconWpf/Properties/AssemblyInfo.cs @@ -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")] diff --git a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs index 22fe1d5..0166c6a 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs @@ -557,7 +557,24 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion - #region DataContext dependency property override + #region DataContext dependency property override / target update + + /// + /// Updates the of a given + /// . This method only updates target elements + /// that do not already have a data context of their own, and either assigns + /// the of the NotifyIcon, or the + /// NotifyIcon itself, if no data context was assigned at all. + /// + 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; + } + } /// /// 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 + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void ContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon)d; + owner.OnContextMenuPropertyChanged(e); + } + + + /// + /// Releases the old and updates the new property + /// in order to reflect both the NotifyIcon's + /// property and have the assigned. + /// + /// Provides information about the updated property. + 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); } + } } \ No newline at end of file diff --git a/Source/NotifyIconWpf/TaskbarIcon.cs b/Source/NotifyIconWpf/TaskbarIcon.cs index dd8405a..cd3afa4 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.cs @@ -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 /// @@ -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); } diff --git a/Source/NotifyIconWpf/Util.cs b/Source/NotifyIconWpf/Util.cs index 8691996..c7ef9f6 100644 --- a/Source/NotifyIconWpf/Util.cs +++ b/Source/NotifyIconWpf/Util.cs @@ -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 /// instance. /// - /// - /// - /// + /// Configuration settings for the NotifyIcon. + /// Operation on the icon (e.g. delete the icon). + /// True if the data was successfully written. + /// See Shell_NotifyIcon documentation on MSDN for details. 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 /// instance. /// - /// - /// - /// - /// + /// Configuration settings for the NotifyIcon. + /// Operation on the icon (e.g. delete the icon). + /// Defines which members of the + /// structure are set. + /// True if the data was successfully written. + /// See Shell_NotifyIcon documentation on MSDN for details. 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 + + /// + /// Checks whether the + /// is bound or not. + /// + /// The element to be checked. + /// True if the data context property is being managed by a + /// binding expression. + /// If + /// is a null reference. + public static bool IsDataContextDataBound(this FrameworkElement element) + { + if (element == null) throw new ArgumentNullException("element"); + return element.GetBindingExpression(FrameworkElement.DataContextProperty) != null; + } + } }