Sam's Blog

30 Mar

Building a simple Launchy clone in WPF (Part 3/3: Tying things together)

This part is all about some finishing touches. At this point I’ll assume you’ve finished Part 1 and Part 2, so we’ll go ahead and dive right in! 🙂

1. Window startup location

  1. To make the window start up in the center of the screen, use Blend to set the WindowStartupLocation property of the Window to CenterScreen.

Run This is a good place to run your program and test out the changes.

2. Transparency and Effects

As you’ve surely noticed by now, there is an ugly white background behind our background gradient rectangle. Let’s take care of that and a couple other things.

  1. Select the Window and set the Background to No Brush.
  2. Check the box for AllowsTransparency.
  3. Check the box for Topmost.
  4. Uncheck the box for ShowInTaskbar.
  5. Select the top level [Grid] and find the RenderTransform properties section. Set the Translate Y to have a value of -4. This shifts everything up so the drop shadow doesn’t clip.
  6. Select the [Popup] and check the box for AllowsTransparency and set the PopupAnimation to Slide.

Run This is a good place to run your program and test out the changes.

3. Window dragging

  1. Use Blend to set up an event handler for the MouseLeftButtonDown event of Window. Use this event to initiate the window drag:
1
2
3
4
private void Window_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
{
  DragMove( );
}

Run This is a good place to run your program and test out the changes.

4. A context menu to exit

In order for a context menu for an item in a window, that item needs to be the top most hit-testable object. In the case of our clone, this can quickly lead to a confused developer. The item we want to add a context menu for (the Rectangle) appears to be the top most item! However, sitting quietly (and transparently) on top of it we have a Border, an Image, and a TextBlock.

4.1. Creating the menu in Blend

  1. Uncheck the IsHitTestVisible property of the border, activeItemIcon, and activeItemTitle objects.
  2. Find the ContextMenu property for the Rectangle and click New.
  3. Find the Items (Collection) property listed under the context menu and click “…”
  4. Click the down arrow to the right of Add another item and select MenuItem.
  5. Check the box for the IsEnabled property of the MenuItem, set the Header to Exit, and set the ToolTip to Shuts down the Launchy clone, then click OK.
  6. In the Objects and Timeline pane, right click the Rectangle and select View XAML. Find the line that reads:
<MenuItem IsEnabled="True" ToolTip="Shuts down the Launchy clone" Header="Exit"/>

and replace it with

<MenuItem IsEnabled="True" ToolTip="Shuts down the Launchy clone" Header="Exit" Click="menu_Exit"/>

4.2. Handling the menu action in code

In Visual Studio, add a function to your Window1 class to handle the click.

1
2
3
4
private void menu_Exit( object sender, RoutedEventArgs e )
{
  Application.Current.Shutdown( );
}

Run This is a good place to run your program and test out the changes.

5. Registering the system hotkey

5.1. The ManagedWinapi helper library

The ManagedWinapi includes a convenient wrapper around the functions required for supporting global hotkeys.

  1. Download either the source or binary distribution of ManagedWinapi. If you download the source distribution, you need to add the project to the Launchy Clone solution.
  2. Add a reference to the Launchy Clone project: reference the ManagedWinapi project (source distribution) or Browse to add a reference to the downloaded dll (binary distribution).
  3. Add another reference to the Launchy Clone project: reference the .NET assembly System.Windows.Forms (required for the Keys enumeration).

5.2. Enabling the hotkey

I chose to implement the default hotkey for our clone as WinKey+Space instead of Ctrl+Space (the Launchy default) so they don’t interfere. Open App.xaml.cs file and add the following to the App class (note that the function OnStartup is modified from before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private ManagedWinapi.Hotkey hotkey;
 
protected override void OnStartup( StartupEventArgs e )
{
  cache = new Cache( );
 
  hotkey = new ManagedWinapi.Hotkey( );
  hotkey.WindowsKey = true;
  hotkey.KeyCode = System.Windows.Forms.Keys.Space;
  hotkey.HotkeyPressed += new EventHandler( hotkey_HotkeyPressed );
  try
  {
    hotkey.Enabled = true;
  }
  catch ( ManagedWinapi.HotkeyAlreadyInUseException )
  {
    System.Windows.MessageBox.Show( "Could not register hotkey (already in use).", "Error", MessageBoxButton.OK, MessageBoxImage.Error );
  }
 
  base.OnStartup( e );
}
 
void hotkey_HotkeyPressed( object sender, EventArgs e )
{
  new Window1( ).ShowDialog( );
}
 
protected override void OnExit( ExitEventArgs e )
{
  hotkey.Dispose( );
  base.OnExit( e );
}

5.3. Correcting the application’s ShutdownMode

Right now, when the main window of our application closes, it exits. We want the window to close, but leave the application running in the background waiting for our hotkey.

  1. Open App.xaml and change the ShutdownMode to OnExplicitShutdown
  2. Remove the StartupUri attribute (completely). This caused our clone to launch silently, waiting for the hotkey (just like Launchy does).
  3. To prevent many copies of the program to start running without a way to close them, update the OnStartup function again to handle errors setting up the hotkey:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected override void OnStartup( StartupEventArgs e )
{
  cache = new Cache( );
 
  hotkey = new ManagedWinapi.Hotkey( );
  hotkey.WindowsKey = true;
  hotkey.KeyCode = System.Windows.Forms.Keys.Space;
  hotkey.HotkeyPressed += new EventHandler( hotkey_HotkeyPressed );
  try
  {
    hotkey.Enabled = true;
  }
  catch ( ManagedWinapi.HotkeyAlreadyInUseException )
  {
    System.Windows.MessageBox.Show( "Could not register hotkey (already in use).", "Error", MessageBoxButton.OK, MessageBoxImage.Error );
    // if the hotkey is not enabled, don't let the application run without a visible window
    ShutdownMode = ShutdownMode.OnLastWindowClose;
  }
 
  base.OnStartup( e );
 
  if ( ShutdownMode == ShutdownMode.OnLastWindowClose )
  {
    // if our application is not set to run in the background, show a window immediately
    new Window1( ).ShowDialog( );
  }
}

Run This is a good place to run your program and test out the changes.

6. Conclusion

Well I hope you learned a bit about using WPF in this example. I like this example because it really highlights the ability of WPF to allow you to make a clean, useful application in less time than ever before.

Oh, and one final sidenote: the application and libraries (aside from the .NET framework of course) end up only ~100kb. 🙂

4 Responses to “Building a simple Launchy clone in WPF (Part 3/3: Tying things together)”

  1. 1
    Mark Monster Says:

    Hmm. I really like this three part article. How come you stopped blogging? Keep up this work, and your blog may become more read.

  2. 2
    steve Says:

    How to i trap the click on the x button that closes an application? What is the source code for this in a WPF window? Thanks

  3. 3
    Patrick Says:

    Hi,

    Great tutorial. I am writing an application that does something similar, but I’m having a problem where sometimes my window appears but doesn’t actually become the foreground window and gain focus. Any idea where I could be going wrong?

  4. 4
    Twitter / Gerry Heidenreich: Windows system hotkey in wp... Says:

    […] Windows system hotkey in wpf: http://blog.280z28.org/arch… […]

Leave a Reply

© 2024 Sam's Blog | Entries (RSS) and Comments (RSS)

Your Index Web Directorywordpress logo