Friday, December 24, 2010

Balloon Pop-ups and Toaster Pops

Recently I was looking into balloons, toaster-pops, and general system tray messages.  There's nothing much WPF gives you out of the box to give you a leg up. So I turned to Google to search for either code samples or control libraries.  I found a fantastic free open-source all-in-one framework for all things balloons, system-tray and toaster-pops.

Check it out here:
http://www.hardcodet.net/projects/wpf-notifyicon



So it looks cool, but how hard is it to make a quick and dirty sample application that shows a custom balloon pop-up out of the system tray? (and yes it follows the tray if you move your task bar).


  1. Create a new WPF project and reference the one Hardcodet.Wpf.TaskbarNotification DLL.
  2. Add this code to the MainWindow.Xaml
    <TextBlock Text="Wait 5 seconds for the ring balloon popup to appear." />
    <tb:TaskbarIcon x:Name="tb" VerticalAlignment="Top" Visibility="Hidden" />
    
    
  3. Add this to the code behind:
    private DispatcherTimer timer;
    
            public MainWindow()
            {
                InitializeComponent();
                Loaded += OnLoaded;
            }
    
            private void OnLoaded(object sender, System.Windows.RoutedEventArgs e)
            {
                this.timer = new DispatcherTimer(new TimeSpan(0, 0, 6), DispatcherPriority.Normal, OnTimerTick, Dispatcher);
                this.timer.Start();
            }
    
            private void OnTimerTick(object sender, EventArgs e)
            {
                var balloon = new FancyBalloon { BalloonText = "Ring Ring" };
                tb.ShowCustomBalloon(balloon, PopupAnimation.Scroll, 3000);
            }
  4. Add a user control to define what you want your custom pop-up to look like.
    <UserControl x:Class="WpfBasicBalloon.FancyBalloon"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 Height="300" Width="300">
        <Grid>
            <Border 
                Opacity="0.5"
                Background="Pink" 
                BorderBrush="Red"
                BorderThickness="2"
                CornerRadius="10"
                Margin="10">
                <Border.BitmapEffect>
                    <DropShadowBitmapEffect />
                </Border.BitmapEffect>
                <StackPanel Margin="10">
                <TextBlock Text="Hello World - in pink just for Jo. :-)" />
                <TextBlock Text="{Binding BalloonText}" />
            </StackPanel>
            </Border>
        </Grid>
    </UserControl>

Easy.


Its so little code, given a few days of my cat walking randomly across my keyboard, there's a good chance he will come up with this code on his own. Better chances of winning the lottery over the holiday break any way!

Happy holidays.

Wednesday, December 22, 2010

Extracting A ControlTemplate From An Existing WPF Control

There are a number of tools to do this, not least of which is Blend.  But sometimes you need some code to do it for you.  There have been a few instances where Blend was unable to extract the control template, and resorting to code is the last line of defense.

Here's a snippet of code from a test application where Group1 is an element (of type RibbonGroup).  This code comes from a code behind from a xaml window.

var template = this.Group1.Template;
            var xmlSettings = new XmlWriterSettings { Indent = true };

            var builder = new StringBuilder();
            XmlWriter writer = XmlWriter.Create(builder, xmlSettings);
            XamlWriter.Save(template, writer);
            Clipboard.SetText(builder.ToString());
            Debug.WriteLine(builder.ToString());
This will output the control template xaml to the Debug output and also copy it to the clipboard.

Tuesday, December 21, 2010

Grrrr TFS Crashed Build Locked Files

Today I had to fix a build issue, which are few and far between these days with TFS 2010.  By and large I love it, it has a huge array of features and advancements over previous version. And sure beats the hell out of Subversion for features and ease of use.

The build crashed after someone managed to checked in the wrong version of / or corrupt DLLs. The result was files locked by the build machine that could not be rolled back or checked out.

When I attempted to check out the files to roll them back obviously being locked I got the error message below..."is locked for check-out by..."
This problem cannot be solved by using the IDE. Only the command line can fix this.  The good news is its totally doable and the above error gives you the workspace name you need.

Jump into a Visual Studio 2010 Command Prompt window and type the following:

TF undo /workspace:<WorkspaceNameGivenInOutput> "<FullPathToFileToUndo>" /collection:<FullHttpUrlIncludingPortNumberAndCollectionProjectName>

Thursday, December 16, 2010

Exception Template

Here's an Exception template that complies with all Code Analysis warnings and best practices:
Writing a custom exception couldn't be more straight forward.  However there are some things that are not obvious that can be useful to comply with.

The choice to write a custom exception usually follows the process of checking to see if an existing .NET framework exception adequately explains the issue with enough information.  DO NOT throw System.Exception or System.SystemException, that just reeks of rotten lazy coding.

If you're using the "All Rules" Code Analysis category in your project you'll be walked through a series of changes to comply.  These all make sense when you think about them. I'll walk through them below.  For the sake of expediency here's a template for a standard custom exception to save you the trouble of trying several times to resolve all warnings:


namespace MyNamespace
{
    using System;
    using System.Runtime.Serialization;

    /// <summary>
    /// A Custom exception ...
    /// </summary>
    [Serializable]
    public class CustomException : Exception
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CustomException"/> class.        
        /// </summary>
        public CustomException()
            : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomException"/> class.        
        /// </summary>
        /// <param name="message">The message.</param>
        public CustomException(string message)
            : base(message)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomException"/> class.        
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="innerException">The inner exception.</param>
        public CustomException(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomException"/> class.        
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
        /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null. </exception>
        /// <exception cref="T:System.Runtime.Serialization.SerializationException">The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0). </exception>
        protected CustomException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
 

        /*
         * Use this when adding custom properties to an exception.
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("CustomProperty", CustomProperty);
        }
        */
        }
}


[Serializable]
This is good practice to allow exceptions to be saved in full when persisted, or sent over a communications channel. Although WCF requires that all top level exceptions be FaultExceptions, I believe you can use a serializable exception as an inner exception. Also consider implementing ISerializable.

Inherit from Exception.
There is no real benefit from having deep inheritance hierarchies of exceptions, and this is the recommendation of the Microsoft Framework Design Guidelines.  A good example of excellent use of Exception inheritance with good meaning are the System.IO.IOException types. This allows you to catch all IO related exceptions. Also the name should always be suffixed with "Exception" for clarity and consistency with other .NET framework exceptions.  Its better to have many specific custom exception types than a few overused types that dilute their meaning.  One last thing on this, don't bother deriving from ApplicationException, to quote Jeffrey Richter: "its a class that should not be part of the .NET framework. The original idea was that classes derived from SystemException would indicate CLR exceptions where non-CLR would derive from ApplicationException. However a lot of exception classes didn't follow this guideline...and it lost all meaning." Do not derive from any other exception other than Exception or SystemException (for more information see the Framework Design Guidelines chapter on exceptions).

Constructors.
Because constructors cannot be prescribed in a base-class the three exception constructors that are always expected to be present are the three shown above.  Your custom exception can be passed into .NET framework code and into other code that is consistent with the .NET framework. For this reason you should always have these three constructors available.

The last constructor is to ensure serialization is performed correctly. 

Don't go nuts on overloaded constructors rather prefer properties. 

Don't bother with error codes of any kind, this is what Exception types are for.

Another thing worth considering is using the System.Environment.FailFast method to ensure your process terminates quickly and as cleanly as possible.  In a no-win situation this is the best idea when there is an unhandled exception that leaves the application in an inconsistent state.

Don't be tempted to use custom exception types in "normal" program logic. Ie: catching a FileNotFoundException and then creating a file.  This will result in bad performing code.  Rather check and create (ie File.Exists method).

When making extensive use of logging, don't be caught out by thinking that logging means you don't need exceptions.  When writing reusable frameworks and toolkits, you cannot assume how someone will want to use your library. Log by all means, but throw an exception that the consumer can optionally catch and have the choice of attempting a recovery or failing the process.

Monday, December 13, 2010

Microsoft WPF 2010 Ribbon

I've been looking into implementing a ribbon control, and found that Microsoft have released the source for there 2010 ribbon! (The 2010 Ribbon is also known as a "Scenic" Ribbon style). Great stuff, thanks Microsoft!

Check out these links for more information:

Microsoft also have a page describing what the different components of the ribbon are and when it should be used: Ribbon WPF and also some common application UI patterns and usage recommendations.

There is full support for the MVVM pattern in the ribbon and support for ICommand.

Screenshots:

Here's one I have started to customise to push the ribbon to the right to make space for companion panel I want to sit next to the ribbon.



I've also started reviewing the Fluent Ribbon. See these links:
Looking at it from a code difficulty point of view and amount of code required, Fluent is definitely easier and you write about 30% less code than what is required for the Microsoft WPF Ribbon.  However, the Microsoft offering seems to be faster, and has better support for MVVM.

I am also looking into Infragistics .NetAdvantage 2010 Volume 3, which includes (as of October 2010 I believe) a "Scenic" Office 2010 style Ribbon Bar.  It also has a cool feature called ColorWash which allows a measure of control over the color scheme of the bar.

It looks pretty cool on the surface but it doesn't give you exact control over the resulting color.  Which I found quite annoying.  I found it far too hard to restyle this to be a black scheme.  Infragistics have only given one simple Office 2010 style, the blue "Scenic" style. Seems like the Henry Ford approach, "...you can have any color as long as its black...".  Finally the overall style does not seem to match closely the Office 2010 style, it seems a little over simplified.

The above example has set the Wash Color to Black. But this is still no where near dark enough, and doesn't give me the control I want. For example changing the background to a different gradient fill. You can also see the style is subtly simpler than the Fluent and Microsoft images above.

My verdict?  In my humble opinion Infragistics comes in at third on this one.  It feels like they have retro fitted 2010 styling rather than considering all the features in the Office 2010 Ribbon.  Fluent is very good and seems easy to use but there are a few tiny things missing in comparison to Microsoft.  However, if ease of implementation is what you're after Fluent looks like the way to go. Me personally, I need all the features Microsoft has over Fluent (Keyboard Navigation, full control of Backstage, and mini-toolbar and shortcut tool-bar).

Sunday, December 12, 2010

Global Hotkeys and User Activity Detection

Back in July I posted about a job where I have to be able to detect user input (or absence of it) through keyboard or mouse.  In addition, how do you go about setting up global OS level hot keys?

Detecting Keyboard and Mouse Input
The main ingredient is the Win32 function

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(
        int idHook,
        LowLevelInputProc lpfn,
        IntPtr hMod,
        uint dwThreadId);

This function is used to hook into many Windows events.  The first parameter is the type of hook you would like to "hook" into. The term hook is used basically to describe subscribing to a Windows event.
The second parameter Lpfn is the callback delegate Windows should invoke when an event occurs. The third is your application's module handle, and finally the last is the thread id in your application Windows should use to invoke the callback.

To set the hook, is reasonably straight forward. I've written a wrapper method to simplify the invocation of the Win32 call:


private static IntPtr SetHook(LowLevelInputProc proc, int hookTypeConstant)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            {
                using (ProcessModule curModule = curProcess.MainModule)
                {
                    if (curModule == null)
                    {
                        return IntPtr.Zero;
                    }

                    return SetWindowsHookEx(
                        hookTypeConstant,
                        proc,
                        GetModuleHandle(curModule.ModuleName),
                        0);
                }
            }
        }

The two hook types I am interested in are Keyboard keypress events (int WH_KEYBOARD_LL = 13) and Mouse keypress events (int WH_MOUSE_LL = 14).  The callback has the signature:


private delegate IntPtr LowLevelInputProc(int nCode, IntPtr wParam, IntPtr lParam);

The three parameters are then used to give you information about the event and vary between different hook types.

My end goal is to be able to wrap these two hooks into a reusable class. The idea is to make a static class that has standard .NET events that are easier to subscribe to and deal with.


namespace KeyboardHookConsole
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    public static class InputDeviceInterceptor
    {
        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Reviewed. Maps to Win32.")]
        private const int WH_KEYBOARD_LL = 13;

        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Reviewed. Win32.")]
        private const int WH_MOUSE_LL = 14;

        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Reviewed. Maps to Win32.")]
        private const int WM_KEYDOWN = 0x0100;

        private static IntPtr keyboardHookId = IntPtr.Zero;

        private static IntPtr mouseHookId = IntPtr.Zero;

        private delegate IntPtr LowLevelInputProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static readonly LowLevelInputProc KeyboardCallback = KeyboardHookCallback;

        private static readonly LowLevelInputProc MouseCallback = MouseHookCallback;

        public static event EventHandler<InterceptedKeyEventArgs> KeyboardInput;

        public static event EventHandler<InterceptedMouseEventArgs> MouseInput;

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205
        }

        public static void SetHook()
        {
            keyboardHookId = SetHook(KeyboardCallback, WH_KEYBOARD_LL);
            mouseHookId = SetHook(MouseCallback, WH_MOUSE_LL);
        }

        public static void Unhook()
        {
            UnhookWindowsHookEx(keyboardHookId);
            UnhookWindowsHookEx(mouseHookId);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(
            IntPtr hhk,
            int nCode,
            IntPtr wParam,
            IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        private static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int code = Marshal.ReadInt32(lParam);
                var handler = KeyboardInput;
                if (handler != null)
                {
                    handler(null, new InterceptedKeyEventArgs((Keys)code));
                    //// handler(null, new InterceptedKeyEventArgs());
                }
            }

            return CallNextHookEx(keyboardHookId, nCode, wParam, lParam);
        }

        private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            // MouseButtonState left = MouseButtonState.Released, right = MouseButtonState.Released;
            // var point = new Point();

            bool raiseEvent = false;
            // var hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));

            if (nCode >= 0)
            {
                if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
                {
                    // left = MouseButtonState.Pressed;
                    raiseEvent = true;
                }

                if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
                {
                    // right = MouseButtonState.Pressed;
                    raiseEvent = true;
                }

                if (MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
                {
                    raiseEvent = true;
                    // point.X = hookStruct.pt.x;
                    // point.Y = hookStruct.pt.y;
                }

                if (raiseEvent)
                {
                    var handler = MouseInput;
                    if (handler != null)
                    {
                        // handler(null, new InterceptedMouseEventArgs(left, right, point));
                        handler(null, new InterceptedMouseEventArgs());
                    }
                }
            }

            return CallNextHookEx(mouseHookId, nCode, wParam, lParam);
        }

        private static IntPtr SetHook(LowLevelInputProc proc, int hookTypeConstant)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            {
                using (ProcessModule curModule = curProcess.MainModule)
                {
                    if (curModule == null)
                    {
                        return IntPtr.Zero;
                    }

                    return SetWindowsHookEx(
                        hookTypeConstant,
                        proc,
                        GetModuleHandle(curModule.ModuleName),
                        0);
                }
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(
            int idHook,
            LowLevelInputProc lpfn,
            IntPtr hMod,
            uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
    }
}

Here's a sample console application that tests this class.

namespace ConsoleApplication1
{
    using System;
    using System.Windows.Forms;
    using KeyboardHookConsole;

    public static class Program
    {
        public static void Main()
        {
            InputDeviceInterceptor.SetHook();
            InputDeviceInterceptor.KeyboardInput += (s, e) => Console.WriteLine(e.Key.ToString());

            InputDeviceInterceptor.MouseInput += (s, e) => Console.WriteLine("Mouse moved");

            Application.Run();

            InputDeviceInterceptor.Unhook();
        }
    }
}



Finally more close to my ultimate end goal, here's a WPF application with a check box to indicate if the user has recently typed input, or moved their mouse:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Are you present?" />
            <CheckBox IsChecked="{Binding Present}" />
        </StackPanel>
    </StackPanel>
</Window>

namespace WpfApplication1
{
    using System;
    using System.ComponentModel;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Threading;
    using KeyboardHookConsole;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : INotifyPropertyChanged
    {
        private readonly DispatcherTimer awayTimer = new DispatcherTimer();
        private bool present;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Loaded += OnLoaded;
            Closing += OnClosing;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public bool Present
        {
            get
            {
                return this.present;
            }

            set 
            { 
                if (value)
                {
                    this.awayTimer.Start();
                }

                this.present = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Present"));
            }
        }

        private void OnAwayTimerTick(object sender, EventArgs e)
        {
            Present = false;
            this.awayTimer.Stop();
        }

        private void OnClosing(object sender, CancelEventArgs e)
        {
            InputDeviceInterceptor.KeyboardInput -= OnInterceptorKeyboardInput;
            InputDeviceInterceptor.MouseInput -= OnInterceptorMouseInput;
            InputDeviceInterceptor.Unhook();
        }

        private void OnInterceptorKeyboardInput(object sender, InterceptedKeyEventArgs e)
        {
            // Toggle the user presence property
            Task.Factory.StartNew(() =>
                                  {
                                      if (!Present)
                                      {
                                          Dispatcher.BeginInvoke(new Action(() => Present = true));
                                      }
                                  });
        }

        private void OnInterceptorMouseInput(object sender, InterceptedMouseEventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                if (!Present)
                {
                    Dispatcher.BeginInvoke(new Action(() => Present = true));
                }
            });
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            InputDeviceInterceptor.SetHook();
            InputDeviceInterceptor.KeyboardInput += OnInterceptorKeyboardInput;
            InputDeviceInterceptor.MouseInput += OnInterceptorMouseInput;
            this.awayTimer.Interval = new TimeSpan(0, 0, 5);
            this.awayTimer.Tick += OnAwayTimerTick;
            this.awayTimer.Start();
        }
    }
}

Setting Hot Keys will follow.