WPF NotifyIcon : Public Release

-------------------------------
CHG   Common cleanup, copyright etc.
DEL   Removed unused dependency property change handlers.
ADD   Added ResetBalloonCloseTimer method which keeps custom balloons open.
ADD   Provided implementation for attached BalloonClosingEvent.

git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@94 9f600761-6f11-4665-b6dc-0185e9171623
This commit is contained in:
Philipp Sumi
2009-05-04 19:36:22 +00:00
parent 2b05ff7bd7
commit 98a0017687
15 changed files with 568 additions and 135 deletions

View File

@@ -1,7 +1,26 @@
using System; // hardcodet.net NotifyIcon for WPF
using System.Collections.Generic; // Copyright (c) 2009 Philipp Sumi
using System.Linq; // Contact and Information: http://www.hardcodet.net
using System.Text; //
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
namespace Hardcodet.Wpf.TaskbarNotification namespace Hardcodet.Wpf.TaskbarNotification
{ {
@@ -27,4 +46,4 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// </summary> /// </summary>
Error Error
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Hardcodet.Wpf.TaskbarNotification.Interop
namespace Hardcodet.Wpf.TaskbarNotification.Interop
{ {
/// <summary> /// <summary>
/// Event flags for clicked events. /// Event flags for clicked events.

View File

@@ -1,4 +1,4 @@
using System;
namespace Hardcodet.Wpf.TaskbarNotification.Interop namespace Hardcodet.Wpf.TaskbarNotification.Interop
{ {

View File

@@ -0,0 +1,172 @@
// Interop code taken from Mike Marshall's AnyForm
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace Hardcodet.Wpf.TaskbarNotification.Interop
{
/// <summary>
/// Resolves the current tray position.
/// </summary>
public static class TrayInfo
{
[DllImport("user32.dll")]
private static extern Int32 GetWindowLong(IntPtr hWnd, Int32 Offset);
public static Point GetTrayLocation()
{
var info = new AppBarInfo();
info.GetSystemTaskBarPosition();
Rectangle rcWorkArea = info.WorkArea;
int x = 0, y = 0;
if (info.Edge == AppBarInfo.ScreenEdge.Left)
{
x = rcWorkArea.Left + 2;
y = rcWorkArea.Bottom;
}
else if (info.Edge == AppBarInfo.ScreenEdge.Bottom)
{
x = rcWorkArea.Right;
y = rcWorkArea.Bottom;
}
else if (info.Edge == AppBarInfo.ScreenEdge.Top)
{
x = rcWorkArea.Right;
y = rcWorkArea.Top;
}
else if (info.Edge == AppBarInfo.ScreenEdge.Right)
{
x = rcWorkArea.Right;
y = rcWorkArea.Bottom;
}
return new Point { X = x, Y = y};
}
}
internal class AppBarInfo
{
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("shell32.dll")]
private static extern UInt32 SHAppBarMessage(UInt32 dwMessage, ref APPBARDATA data);
[DllImport("user32.dll")]
private static extern Int32 SystemParametersInfo(UInt32 uiAction, UInt32 uiParam,
IntPtr pvParam, UInt32 fWinIni);
private const int ABE_BOTTOM = 3;
private const int ABE_LEFT = 0;
private const int ABE_RIGHT = 2;
private const int ABE_TOP = 1;
private const int ABM_GETTASKBARPOS = 0x00000005;
// SystemParametersInfo constants
private const UInt32 SPI_GETWORKAREA = 0x0030;
private APPBARDATA m_data;
public ScreenEdge Edge
{
get { return (ScreenEdge) m_data.uEdge; }
}
public Rectangle WorkArea
{
get
{
Int32 bResult = 0;
var rc = new RECT();
IntPtr rawRect = Marshal.AllocHGlobal(Marshal.SizeOf(rc));
bResult = SystemParametersInfo(SPI_GETWORKAREA, 0, rawRect, 0);
rc = (RECT) Marshal.PtrToStructure(rawRect, rc.GetType());
if (bResult == 1)
{
Marshal.FreeHGlobal(rawRect);
return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
}
return new Rectangle(0, 0, 0, 0);
}
}
public void GetPosition(string strClassName, string strWindowName)
{
m_data = new APPBARDATA();
m_data.cbSize = (UInt32) Marshal.SizeOf(m_data.GetType());
IntPtr hWnd = FindWindow(strClassName, strWindowName);
if (hWnd != IntPtr.Zero)
{
UInt32 uResult = SHAppBarMessage(ABM_GETTASKBARPOS, ref m_data);
if (uResult != 1)
{
throw new Exception("Failed to communicate with the given AppBar");
}
}
else
{
throw new Exception("Failed to find an AppBar that matched the given criteria");
}
}
public void GetSystemTaskBarPosition()
{
GetPosition("Shell_TrayWnd", null);
}
public enum ScreenEdge
{
Undefined = -1,
Left = ABE_LEFT,
Top = ABE_TOP,
Right = ABE_RIGHT,
Bottom = ABE_BOTTOM
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public UInt32 cbSize;
public IntPtr hWnd;
public UInt32 uCallbackMessage;
public UInt32 uEdge;
public RECT rc;
public Int32 lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
}
}

View File

@@ -1,4 +1,29 @@
using System; // hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;

View File

@@ -40,14 +40,18 @@
<Reference Include="System.Core"> <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BalloonIcon.cs" /> <Compile Include="BalloonIcon.cs" />
<Compile Include="Interop\AnyForm.cs" /> <Compile Include="Interop\TrayInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Interop\Point.cs" /> <Compile Include="Interop\Point.cs" />
<Compile Include="Interop\WindowClass.cs" /> <Compile Include="Interop\WindowClass.cs" />
<Compile Include="PopupActivationMode.cs" /> <Compile Include="PopupActivationMode.cs" />

View File

@@ -1,4 +1,28 @@
namespace Hardcodet.Wpf.TaskbarNotification // hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
namespace Hardcodet.Wpf.TaskbarNotification
{ {
/// <summary> /// <summary>
/// Defines flags that define when a popup /// Defines flags that define when a popup

View File

@@ -1,6 +1,4 @@
using System.Reflection; using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows; using System.Windows;
using System.Windows.Markup; using System.Windows.Markup;
@@ -8,16 +6,16 @@ using System.Windows.Markup;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("NotifyIconWpf")] [assembly: AssemblyTitle("NotifyIcon for WPF")]
[assembly: AssemblyDescription("NotifyIcon Implementation for WPF.")] [assembly: AssemblyDescription("NotifyIcon Implementation for the WPF platform.")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("hardcodet.net")] [assembly: AssemblyCompany("hardcodet.net")]
[assembly: AssemblyProduct("NotifyIconWpf")] [assembly: AssemblyProduct("NotifyIcon WPF")]
[assembly: AssemblyCopyright("Copyright © Philipp Sumi 2009")] [assembly: AssemblyCopyright("Copyright © Philipp Sumi 2009")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
//provides simplified declaration
[assembly: XmlnsDefinition("http://www.hardcodet.net/taskbar", "Hardcodet.Wpf.TaskbarNotification")] [assembly: XmlnsDefinition("http://www.hardcodet.net/taskbar", "Hardcodet.Wpf.TaskbarNotification")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible

View File

@@ -1,4 +1,29 @@
using System; // hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Windows; using System.Windows;
@@ -33,6 +58,11 @@ namespace Hardcodet.Wpf.TaskbarNotification
= DependencyProperty.RegisterReadOnly("TrayPopupResolved", typeof(Popup), typeof(TaskbarIcon), = DependencyProperty.RegisterReadOnly("TrayPopupResolved", typeof(Popup), typeof(TaskbarIcon),
new FrameworkPropertyMetadata(null)); new FrameworkPropertyMetadata(null));
/// <summary>
/// A read-only dependency property that returns the <see cref="Popup"/>
/// that is being displayed in the taskbar area based on a user action.
/// </summary>
public static readonly DependencyProperty TrayPopupResolvedProperty public static readonly DependencyProperty TrayPopupResolvedProperty
= TrayPopupResolvedPropertyKey.DependencyProperty; = TrayPopupResolvedPropertyKey.DependencyProperty;
@@ -70,6 +100,11 @@ namespace Hardcodet.Wpf.TaskbarNotification
= DependencyProperty.RegisterReadOnly("TrayToolTipResolved", typeof(ToolTip), typeof(TaskbarIcon), = DependencyProperty.RegisterReadOnly("TrayToolTipResolved", typeof(ToolTip), typeof(TaskbarIcon),
new FrameworkPropertyMetadata(null )); new FrameworkPropertyMetadata(null ));
/// <summary>
/// A read-only dependency property that returns the <see cref="ToolTip"/>
/// that is being displayed.
/// </summary>
public static readonly DependencyProperty TrayToolTipResolvedProperty public static readonly DependencyProperty TrayToolTipResolvedProperty
= TrayToolTipResolvedPropertyKey.DependencyProperty; = TrayToolTipResolvedPropertyKey.DependencyProperty;
@@ -336,7 +371,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
if (e.OldValue != null) if (e.OldValue != null)
{ {
//remove the taskbar icon reference from the previously used element //remove the taskbar icon reference from the previously used element
SetParentTaskbarIcon((DependencyObject) e.OldValue, this); SetParentTaskbarIcon((DependencyObject) e.OldValue, null);
} }
@@ -407,7 +442,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
if (e.OldValue != null) if (e.OldValue != null)
{ {
//remove the taskbar icon reference from the previously used element //remove the taskbar icon reference from the previously used element
SetParentTaskbarIcon((DependencyObject)e.OldValue, this); SetParentTaskbarIcon((DependencyObject)e.OldValue, null);
} }
@@ -434,7 +469,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
DependencyProperty.Register("MenuActivation", DependencyProperty.Register("MenuActivation",
typeof (PopupActivationMode), typeof (PopupActivationMode),
typeof (TaskbarIcon), typeof (TaskbarIcon),
new FrameworkPropertyMetadata(PopupActivationMode.RightClick, MenuActivationPropertyChanged)); new FrameworkPropertyMetadata(PopupActivationMode.RightClick));
/// <summary> /// <summary>
/// A property wrapper for the <see cref="MenuActivationProperty"/> /// A property wrapper for the <see cref="MenuActivationProperty"/>
@@ -450,34 +485,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
set { SetValue(MenuActivationProperty, value); } set { SetValue(MenuActivationProperty, value); }
} }
/// <summary>
/// A static callback listener which is being invoked if the
/// <see cref="MenuActivationProperty"/> dependency property has
/// been changed. Invokes the <see cref="OnMenuActivationPropertyChanged"/>
/// 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 MenuActivationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TaskbarIcon owner = (TaskbarIcon) d;
owner.OnMenuActivationPropertyChanged(e);
}
/// <summary>
/// Handles changes of the <see cref="MenuActivationProperty"/> dependency property. As
/// WPF internally uses the dependency property system and bypasses the
/// <see cref="MenuActivation"/> property wrapper, updates of the property's value
/// should be handled here.
/// </summary
/// <param name="e">Provides information about the updated property.</param>
private void OnMenuActivationPropertyChanged(DependencyPropertyChangedEventArgs e)
{
//currently not needed
}
#endregion #endregion
#region PopupActivation dependency property #region PopupActivation dependency property
@@ -490,7 +497,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
DependencyProperty.Register("PopupActivation", DependencyProperty.Register("PopupActivation",
typeof (PopupActivationMode), typeof (PopupActivationMode),
typeof (TaskbarIcon), typeof (TaskbarIcon),
new FrameworkPropertyMetadata(PopupActivationMode.LeftClick, PopupActivationPropertyChanged)); new FrameworkPropertyMetadata(PopupActivationMode.LeftClick));
/// <summary> /// <summary>
/// A property wrapper for the <see cref="PopupActivationProperty"/> /// A property wrapper for the <see cref="PopupActivationProperty"/>
@@ -506,34 +513,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
set { SetValue(PopupActivationProperty, value); } set { SetValue(PopupActivationProperty, value); }
} }
/// <summary>
/// A static callback listener which is being invoked if the
/// <see cref="PopupActivationProperty"/> dependency property has
/// been changed. Invokes the <see cref="OnPopupActivationPropertyChanged"/>
/// 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 PopupActivationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TaskbarIcon owner = (TaskbarIcon) d;
owner.OnPopupActivationPropertyChanged(e);
}
/// <summary>
/// Handles changes of the <see cref="PopupActivationProperty"/> dependency property. As
/// WPF internally uses the dependency property system and bypasses the
/// <see cref="PopupActivation"/> property wrapper, updates of the property's value
/// should be handled here.
/// </summary
/// <param name="e">Provides information about the updated property.</param>
private void OnPopupActivationPropertyChanged(DependencyPropertyChangedEventArgs e)
{
//currently not needed
}
#endregion #endregion
@@ -726,21 +705,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion #endregion
/// <summary>
/// Executes a given command.
/// </summary>
/// <param name="command">The command to be executed.</param>
/// <param name="commandParameter">An optional parameter that was associated with
/// the command.</param>
private static void RunCommand(ICommand command, object commandParameter)
{
if (command == null) return;
if (command.CanExecute(commandParameter))
{
command.Execute(commandParameter);
}
}
//EVENTS //EVENTS
@@ -769,7 +733,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
{ {
//first raise event, then command //first raise event, then command
RoutedEventArgs args = RaiseTrayLeftMouseDownEvent(this); RoutedEventArgs args = RaiseTrayLeftMouseDownEvent(this);
RunCommand(LeftClickCommand, LeftClickCommandParameter); LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter);
return args; return args;
} }
@@ -1019,7 +983,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
protected RoutedEventArgs RaiseTrayMouseDoubleClickEvent() protected RoutedEventArgs RaiseTrayMouseDoubleClickEvent()
{ {
RoutedEventArgs args = RaiseTrayMouseDoubleClickEvent(this); RoutedEventArgs args = RaiseTrayMouseDoubleClickEvent(this);
RunCommand(DoubleClickCommand, DoubleClickCommandParameter); DoubleClickCommand.ExecuteIfEnabled(DoubleClickCommandParameter);
return args; return args;
} }
@@ -1685,21 +1649,66 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// A static helper method to raise the BalloonShowing event on a target element. /// A static helper method to raise the BalloonShowing event on a target element.
/// </summary> /// </summary>
/// <param name="target">UIElement or ContentElement on which to raise the event</param> /// <param name="target">UIElement or ContentElement on which to raise the event</param>
internal static RoutedEventArgs RaiseBalloonShowingEvent(DependencyObject target) /// <param name="source">The <see cref="TaskbarIcon"/> instance that manages the balloon.</param>
internal static RoutedEventArgs RaiseBalloonShowingEvent(DependencyObject target, TaskbarIcon source)
{ {
if (target == null) return null; if (target == null) return null;
RoutedEventArgs args = new RoutedEventArgs(); RoutedEventArgs args = new RoutedEventArgs(BalloonShowingEvent, source);
args.RoutedEvent = BalloonShowingEvent; RoutedEventHelper.RaiseEvent(target, args);
return args;
}
#endregion
#region BalloonClosing
/// <summary>
/// BalloonClosing Attached Routed Event
/// </summary>
public static readonly RoutedEvent BalloonClosingEvent = EventManager.RegisterRoutedEvent("BalloonClosing",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon));
/// <summary>
/// Adds a handler for the BalloonClosing attached event
/// </summary>
/// <param name="element">UIElement or ContentElement that listens to the event</param>
/// <param name="handler">Event handler to be added</param>
public static void AddBalloonClosingHandler(DependencyObject element, RoutedEventHandler handler)
{
RoutedEventHelper.AddHandler(element, BalloonClosingEvent, handler);
}
/// <summary>
/// Removes a handler for the BalloonClosing attached event
/// </summary>
/// <param name="element">UIElement or ContentElement that listens to the event</param>
/// <param name="handler">Event handler to be removed</param>
public static void RemoveBalloonClosingHandler(DependencyObject element, RoutedEventHandler handler)
{
RoutedEventHelper.RemoveHandler(element, BalloonClosingEvent, handler);
}
/// <summary>
/// A static helper method to raise the BalloonClosing event on a target element.
/// </summary>
/// <param name="target">UIElement or ContentElement on which to raise the event</param>
/// <param name="source">The <see cref="TaskbarIcon"/> instance that manages the balloon.</param>
internal static RoutedEventArgs RaiseBalloonClosingEvent(DependencyObject target, TaskbarIcon source)
{
if (target == null) return null;
RoutedEventArgs args = new RoutedEventArgs(BalloonClosingEvent, source);
RoutedEventHelper.RaiseEvent(target, args); RoutedEventHelper.RaiseEvent(target, args);
return args; return args;
} }
#endregion #endregion
//ATTACHED PROPERTIES //ATTACHED PROPERTIES
//TODO put into use
#region ParentTaskbarIcon #region ParentTaskbarIcon
/// <summary> /// <summary>
@@ -1729,9 +1738,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion #endregion
//BASE CLASS PROPERTY OVERRIDES //BASE CLASS PROPERTY OVERRIDES
/// <summary> /// <summary>

View File

@@ -1,4 +1,28 @@
using System; // hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
@@ -6,6 +30,7 @@ using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Hardcodet.Wpf.TaskbarNotification.Interop; using Hardcodet.Wpf.TaskbarNotification.Interop;
using Point=Hardcodet.Wpf.TaskbarNotification.Interop.Point; using Point=Hardcodet.Wpf.TaskbarNotification.Interop.Point;
@@ -122,6 +147,8 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion #endregion
#region Custom Balloons
/// <summary> /// <summary>
/// Shows a custom control as a tooltip in the tray location. /// Shows a custom control as a tooltip in the tray location.
/// </summary> /// </summary>
@@ -133,6 +160,13 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// is a null reference.</exception> /// is a null reference.</exception>
public void ShowCustomBalloon(UIElement balloon, PopupAnimation animation, int? timeout) public void ShowCustomBalloon(UIElement balloon, PopupAnimation animation, int? timeout)
{ {
if (!Application.Current.Dispatcher.CheckAccess())
{
var action = new Action(() => ShowCustomBalloon(balloon, animation, timeout));
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, action);
return;
}
if (balloon == null) throw new ArgumentNullException("balloon"); if (balloon == null) throw new ArgumentNullException("balloon");
if (timeout.HasValue && timeout < 500) if (timeout.HasValue && timeout < 500)
{ {
@@ -184,7 +218,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
SetParentTaskbarIcon(balloon, this); SetParentTaskbarIcon(balloon, this);
//fire attached event //fire attached event
RaiseBalloonShowingEvent(balloon); RaiseBalloonShowingEvent(balloon, this);
//display item //display item
popup.IsOpen = true; popup.IsOpen = true;
@@ -200,12 +234,39 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// <summary> /// <summary>
/// Closes the current <see cref="CustomBalloon"/>, if it's set. /// Resets the closing timeout, which effectively
/// keeps a displayed balloon message open until
/// it is either closed programmatically through
/// <see cref="CloseBalloon"/> or due to a new
/// message being displayed.
/// </summary>
public void ResetBalloonCloseTimer()
{
if (IsDisposed) return;
lock (this)
{
//reset timer in any case
balloonCloseTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
/// <summary>
/// Closes the current <see cref="CustomBalloon"/>, if the
/// property is set.
/// </summary> /// </summary>
public void CloseBalloon() public void CloseBalloon()
{ {
if (IsDisposed) return; if (IsDisposed) return;
if (!Application.Current.Dispatcher.CheckAccess())
{
Action action = CloseBalloon;
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, action);
return;
}
lock (this) lock (this)
{ {
//reset timer in any case //reset timer in any case
@@ -215,13 +276,23 @@ namespace Hardcodet.Wpf.TaskbarNotification
Popup popup = CustomBalloon; Popup popup = CustomBalloon;
if (popup != null) if (popup != null)
{ {
//if a balloon message is already displayed, close it immediately
popup.IsOpen = false;
//reset attached property
UIElement element = popup.Child; UIElement element = popup.Child;
if (element != null) SetParentTaskbarIcon(element, null);
//announce closing
RoutedEventArgs eventArgs = RaiseBalloonClosingEvent(element, this);
if (!eventArgs.Handled)
{
//if the event was handled, clear the reference to the popup,
//but don't close it - the handling code has to manage this stuff now
//close the popup
popup.IsOpen = false;
//reset attached property
if (element != null) SetParentTaskbarIcon(element, null);
}
//remove custom balloon anyway
SetCustomBalloon(null); SetCustomBalloon(null);
} }
} }
@@ -241,6 +312,9 @@ namespace Hardcodet.Wpf.TaskbarNotification
Application.Current.Dispatcher.Invoke(action); Application.Current.Dispatcher.Invoke(action);
} }
#endregion
#region Process Incoming Mouse Events #region Process Incoming Mouse Events
@@ -405,7 +479,8 @@ namespace Hardcodet.Wpf.TaskbarNotification
tt.Placement = PlacementMode.Mouse; tt.Placement = PlacementMode.Mouse;
//do *not* set the placement target, as it causes the popup to become hidden if the //do *not* set the placement target, as it causes the popup to become hidden if the
//TaskbarIcon's parent is hidden, too. //TaskbarIcon's parent is hidden, too. At runtime, the parent can be resolved through
//the ParentTaskbarIcon attached dependency property:
//tt.PlacementTarget = this; //tt.PlacementTarget = this;
//the tooltip (and implicitly its context) explicitly gets //the tooltip (and implicitly its context) explicitly gets
@@ -478,13 +553,10 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// property which prevents this issue.</remarks> /// property which prevents this issue.</remarks>
private void CreatePopup() private void CreatePopup()
{ {
//no popup is available
if (TrayPopup == null) return;
//check if the item itself is a popup //check if the item itself is a popup
Popup popup = TrayPopup as Popup; Popup popup = TrayPopup as Popup;
if (popup == null) if (popup == null && TrayPopup != null)
{ {
//create an invisible popup that hosts the UIElement //create an invisible popup that hosts the UIElement
popup = new Popup(); popup = new Popup();
@@ -501,8 +573,9 @@ namespace Hardcodet.Wpf.TaskbarNotification
Popup.CreateRootPopup(popup, TrayPopup); Popup.CreateRootPopup(popup, TrayPopup);
//TODO we don't really need this and it causes the popup to become hidden if the //do *not* set the placement target, as it causes the popup to become hidden if the
//TaskbarIcon's parent is hidden, too. //TaskbarIcon's parent is hidden, too. At runtime, the parent can be resolved through
//the ParentTaskbarIcon attached dependency property:
//popup.PlacementTarget = this; //popup.PlacementTarget = this;
popup.Placement = PlacementMode.AbsolutePoint; popup.Placement = PlacementMode.AbsolutePoint;

View File

@@ -1,7 +1,33 @@
using System; // hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Windows; using System.Windows;
using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Resources; using System.Windows.Resources;
using Hardcodet.Wpf.TaskbarNotification.Interop; using Hardcodet.Wpf.TaskbarNotification.Interop;
@@ -222,5 +248,27 @@ namespace Hardcodet.Wpf.TaskbarNotification
} }
#endregion #endregion
#region execute command
/// <summary>
/// Executes a given command if its <see cref="ICommand.CanExecute"/> method
/// indicates it can run.
/// </summary>
/// <param name="command">The command to be executed, or a null reference.</param>
/// <param name="commandParameter">An optional parameter that is associated with
/// the command.</param>
public static void ExecuteIfEnabled(this ICommand command, object commandParameter)
{
if (command == null) return;
if (command.CanExecute(commandParameter))
{
command.Execute(commandParameter);
}
}
#endregion
} }
} }

View File

@@ -12,7 +12,7 @@
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.95"/> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.95"/>
<SplineDoubleKeyFrame KeyTime="00:00:03" Value="0.95"/> <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0.95"/>
<SplineDoubleKeyFrame KeyTime="00:00:05" Value="0"/> <!-- <SplineDoubleKeyFrame KeyTime="00:00:05" Value="0"/>-->
</DoubleAnimationUsingKeyFrames> </DoubleAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
<Storyboard x:Key="HighlightCloseButton"> <Storyboard x:Key="HighlightCloseButton">
@@ -27,10 +27,22 @@
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.4"/> <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.4"/>
</DoubleAnimationUsingKeyFrames> </DoubleAnimationUsingKeyFrames>
</Storyboard> </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.Resources>
<UserControl.Triggers> <UserControl.Triggers>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonShowing"> <EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonShowing">
<BeginStoryboard Storyboard="{StaticResource FadeIn}"/> <BeginStoryboard Storyboard="{StaticResource FadeIn}" x:Name="FadeIn_BeginStoryboard"/>
</EventTrigger> </EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="imgClose"> <EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="imgClose">
<BeginStoryboard Storyboard="{StaticResource HighlightCloseButton}" x:Name="HighlightCloseButton_BeginStoryboard"/> <BeginStoryboard Storyboard="{StaticResource HighlightCloseButton}" x:Name="HighlightCloseButton_BeginStoryboard"/>
@@ -38,8 +50,15 @@
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="imgClose"> <EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="imgClose">
<BeginStoryboard Storyboard="{StaticResource FadeCloseButton}" x:Name="FadeCloseButton_BeginStoryboard"/> <BeginStoryboard Storyboard="{StaticResource FadeCloseButton}" x:Name="FadeCloseButton_BeginStoryboard"/>
</EventTrigger> </EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeIn_BeginStoryboard"/>
<BeginStoryboard x:Name="FadeBack_BeginStoryboard1" Storyboard="{StaticResource FadeBack}"/>
</EventTrigger>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonClosing">
<BeginStoryboard Storyboard="{StaticResource FadeOut}" x:Name="FadeOut_BeginStoryboard"/>
</EventTrigger>
</UserControl.Triggers> </UserControl.Triggers>
<Grid x:Name="grid"> <Grid x:Name="grid" MouseEnter="grid_MouseEnter">
<Border <Border
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="5,5,5,5" Margin="5,5,5,5"
@@ -93,7 +112,7 @@
</Path.Stroke> </Path.Stroke>
</Path> </Path>
<TextBlock Margin="72,10,10,0" VerticalAlignment="Top" Height="23.2" Text="{Binding Path=BalloonText, ElementName=me, Mode=Default}" TextWrapping="Wrap" Foreground="#FFECAD25" FontWeight="Bold"/> <TextBlock Margin="72,10,10,0" VerticalAlignment="Top" Height="23.2" Text="{Binding Path=BalloonText, ElementName=me, Mode=Default}" TextWrapping="Wrap" Foreground="#FFECAD25" FontWeight="Bold"/>
<Image HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Width="16" Height="16" Source="Images\Close.png" Stretch="Fill" Opacity="0.4" ToolTip="Close Immediately" x:Name="imgClose" MouseDown="imgClose_MouseDown"/> <Image HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Width="16" Height="16" Source="Images\Close.png" Stretch="Fill" Opacity="0.4" ToolTip="Close Balloon" x:Name="imgClose" MouseDown="imgClose_MouseDown"/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -4,10 +4,12 @@ using System.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
@@ -20,6 +22,8 @@ namespace Sample_Project
/// </summary> /// </summary>
public partial class FancyBalloon : UserControl public partial class FancyBalloon : UserControl
{ {
private bool isClosing = false;
#region BalloonText dependency property #region BalloonText dependency property
/// <summary> /// <summary>
@@ -48,6 +52,19 @@ namespace Sample_Project
public FancyBalloon() public FancyBalloon()
{ {
InitializeComponent(); 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 fade-out animation.
/// </summary>
private void OnBalloonClosing(object sender, RoutedEventArgs e)
{
e.Handled = true;
isClosing = true;
} }
@@ -61,5 +78,31 @@ namespace Sample_Project
TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this); TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon(); 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;
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

View File

@@ -11,7 +11,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:local="clr-namespace:Sample_Project" xmlns:local="clr-namespace:Sample_Project"
xmlns:Commands="clr-namespace:Sample_Project.Commands" MinWidth="750" MinHeight="800"> xmlns:Commands="clr-namespace:Sample_Project.Commands" MinWidth="750" MinHeight="800" ResizeMode="NoResize">
<Window.Resources> <Window.Resources>
<BooleanToVisibilityConverter <BooleanToVisibilityConverter
@@ -93,7 +93,7 @@
<CheckBox <CheckBox
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="12,64.04,0,0" Margin="10,38.62,0,0"
VerticalAlignment="Top" VerticalAlignment="Top"
Width="155.42" Width="155.42"
Content="NotifyIcon Visible" Content="NotifyIcon Visible"
@@ -101,7 +101,7 @@
IsChecked="True" /> IsChecked="True" />
<ListBox <ListBox
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="12,122,0,0" Margin="12,86.58,0,0"
Width="123" Width="123"
IsSynchronizedWithCurrentItem="True" IsSynchronizedWithCurrentItem="True"
Height="51" Height="51"
@@ -122,7 +122,7 @@
</ListBox> </ListBox>
<TextBlock <TextBlock
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="12,100,0,0" Margin="12,64.58,0,0"
VerticalAlignment="Top" VerticalAlignment="Top"
Width="Auto" Width="Auto"
Height="22" Height="22"
@@ -250,25 +250,25 @@
Language="de-ch" /></TextBlock> Language="de-ch" /></TextBlock>
</Grid> </Grid>
<Grid <Grid
Margin="0,166.38,10,0" Margin="0,156.38,10,0"
Width="358" Width="358"
HorizontalAlignment="Right" HorizontalAlignment="Right"
x:Name="ToolTips" Height="271" VerticalAlignment="Top"> x:Name="ToolTips" Height="233" VerticalAlignment="Top" d:LayoutOverrides="VerticalAlignment">
<Border <Border
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="#FF000000" BorderBrush="#FF000000"
BorderThickness="2,2,2,2" /> BorderThickness="2,2,2,2" />
<TextBox <TextBox
Margin="10,0,25,86" Margin="10,0,25,70"
x:Name="txtToolTipText" x:Name="txtToolTipText"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Height="23" Height="23"
Text="THIS IS A SAMPLE TEXT...." Text="THIS IS A SAMPLE TEXT...."
Foreground="#FFFF0000" /> Foreground="#FFFF0000" />
<TextBlock <TextBlock
Margin="10,132,25,119" Margin="10,0,25,93"
TextWrapping="Wrap"><Run TextWrapping="Wrap" Height="21" VerticalAlignment="Bottom"><Run
Text="ToolTipText:" Text="ToolTipText:"
Language="de-ch" /></TextBlock> Language="de-ch" /></TextBlock>
<TextBlock <TextBlock
@@ -300,7 +300,7 @@
Language="de-ch" /></TextBlock> Language="de-ch" /></TextBlock>
<Button <Button
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="10,0,0,20" Margin="10,0,0,10"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Width="147" Width="147"
Height="24" Height="24"
@@ -308,7 +308,7 @@
Name="removeToolTip" Name="removeToolTip"
Click="removeToolTip_Click" /> Click="removeToolTip_Click" />
<TextBlock <TextBlock
Margin="10,0,25,53" Margin="10,0,25,44"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Height="16" Height="16"
TextWrapping="Wrap"><Run TextWrapping="Wrap"><Run
@@ -378,13 +378,13 @@
Foreground="#FF303030" Foreground="#FF303030"
TextWrapping="Wrap"><Run Text="This is my first shot at this control and a think a few additions might make sense." Language="de-ch"/><LineBreak/><Run Text="Play around and feel free to commit critical feedback - it's appreciated :)" Language="de-ch"/><LineBreak/><Run Text="" Language="de-ch"/><LineBreak/><Run Foreground="#FF067EBD" Text="Feedback and Updates:" Language="de-ch"/><LineBreak/><Run Foreground="#FF067EBD" Text="http://www.hardcodet.net " Language="de-ch"/><Run FontSize="10" Foreground="#FF067EBD" Text="(watch the spelling" Language="de-ch"/><Run Foreground="#FF067EBD" Text=")" Language="de-ch"/></TextBlock> TextWrapping="Wrap"><Run Text="This is my first shot at this control and a think a few additions might make sense." Language="de-ch"/><LineBreak/><Run Text="Play around and feel free to commit critical feedback - it's appreciated :)" Language="de-ch"/><LineBreak/><Run Text="" Language="de-ch"/><LineBreak/><Run Foreground="#FF067EBD" Text="Feedback and Updates:" Language="de-ch"/><LineBreak/><Run Foreground="#FF067EBD" Text="http://www.hardcodet.net " Language="de-ch"/><Run FontSize="10" Foreground="#FF067EBD" Text="(watch the spelling" Language="de-ch"/><Run Foreground="#FF067EBD" Text=")" Language="de-ch"/></TextBlock>
<TextBlock Margin="12,0,10,10" VerticalAlignment="Bottom" Height="22.42" TextWrapping="Wrap" FontWeight="Bold" Foreground="#FFFFA051"><Run Text="Copyright (c) 2009 Philipp Sumi. This is prerelease software, realeased under the CodeProject Open License (CPOL)" Language="de-ch"/></TextBlock> <TextBlock Margin="12,0,10,10" VerticalAlignment="Bottom" Height="22.42" TextWrapping="Wrap" FontWeight="Bold" Foreground="#FFFFA051"><Run Text="Copyright (c) 2009 Philipp Sumi. This is prerelease software, realeased under the CodeProject Open License (CPOL)" Language="de-ch"/></TextBlock>
<Grid HorizontalAlignment="Right" Margin="0,0,10,262.42" VerticalAlignment="Bottom" Width="358" Height="87.2" x:Name="CustomBalloons"> <Grid HorizontalAlignment="Right" Margin="0,399.38,10,272.42" Width="358" x:Name="CustomBalloons">
<Border HorizontalAlignment="Stretch" Width="Auto" BorderThickness="2,2,2,2" BorderBrush="#FF000000"/> <Border HorizontalAlignment="Stretch" Width="Auto" BorderThickness="2,2,2,2" BorderBrush="#FF000000"/>
<Button Content="Show" x:Name="showCustomBalloon" <Button Content="Show" x:Name="showCustomBalloon"
Click="showCustomBalloon_Click" HorizontalAlignment="Right" Margin="0,0,91.377,10" Width="71.623" Height="23" VerticalAlignment="Bottom" /> Click="showCustomBalloon_Click" HorizontalAlignment="Right" Margin="0,0,91.377,10" Width="71.623" Height="23" VerticalAlignment="Bottom" />
<TextBox VerticalAlignment="Bottom" Height="23" Text="WPF Balloon" TextWrapping="Wrap" Margin="10,0,173,10" x:Name="customBalloonTitle"/> <TextBox VerticalAlignment="Bottom" Height="23" Text="WPF Balloon" TextWrapping="Wrap" Margin="10,0,173,10" x:Name="customBalloonTitle"/>
<TextBlock Margin="10,10,24.377,0" VerticalAlignment="Top" TextWrapping="Wrap"><Run Text="Custom Balloon Tips are more flexible then standard tips when it comes to styling..." Language="de-ch"/></TextBlock> <TextBlock Margin="10,10,24.377,0" VerticalAlignment="Top" TextWrapping="Wrap" Height="66.68"><Run Text="Custom Balloon Tips are more flexible then standard tips when it comes to styling and they provide attached properties and events that can be used to control behavior. Hover over the tooltip to suspend the fade-out." Language="de-ch"/></TextBlock>
<Button Content="Hide" x:Name="hideCustomBalloon" <Button Content="Close" x:Name="hideCustomBalloon"
Click="hideCustomBalloon_Click" HorizontalAlignment="Right" Margin="0,0,9.754,10.52" Width="71.623" Height="23" VerticalAlignment="Bottom" /> Click="hideCustomBalloon_Click" HorizontalAlignment="Right" Margin="0,0,9.754,10.52" Width="71.623" Height="23" VerticalAlignment="Bottom" />
</Grid> </Grid>
</Grid> </Grid>