Building a simple Launchy clone in WPF (Part 1/3: Blend)
As I mentioned in my previous post, I’m quite a fan of Launchy. For fun, I set out to create a simple clone of Launchy in C#/.NET 3.0. In this article, I’ll show what it takes to create a minimal but functional Launchy clone in about 2 hours. After working through this lab, it wouldn’t be too difficult to make the resulting program as functional as Launchy. But that’s outside of what I’m trying to accomplish here, so I’ll leave that to the tinkerers. 🙂
You will need several tools to build this application according to the tutorial. I will also assume you are familiar with the basic concepts of using them. Among the tools are:
- Microsoft Visual Studio 2005
- Microsoft Expression Blend Release Candidate 1
For more information about this software and others needed to get started developing WPF applications today, visit http://www.netfx3.com/.
If you just want to see the result in action, you can download the whole 18.6kb archive here. The file is compressed with 7-zip, one of the greatest FOSS projects of all time IMO. 🙂 Remember this program runs without a window by design. To use it after you run it, press WinKey+Space. To exit, press WinKey+Space, then right click the dialog. Here is a screenshot:
Step 1. Creating the project
In Visual Studio, create a new WPF Windows Application project and solution. This sets up a ready-to-run (and do nothing) framework WPF application. On the plus side, this is one of the biggest time savers in getting started on our clone! To help you see immediate results from your work, whenever you see the symbol, I encourage you to save your work and run the program to check out your latest changes. 🙂
This is a good place to run your program and test out the changes.
Step 2. Use Blend to rapidly create the basic window structure
Open the solution file you just created in Blend. Right off the bat we see Blend has opened the default Window1
window. For the sake of simplicity, we’ll go ahead and work with this window for our clone.
Part 2.1. Get rid of the standard window borders
Since we’re using a completely custom window style for this application, we need to start with a “clean slate.”
- In the Objects and Timeline task pane (on the left side of Blend by default), select
[Window]
. The rest of this part works in the Properties pane (on the right side by default). - Set the
WindowStyle
toNone
. Really cool tip: Type wind in the Search box at the top of the Properties pane. There’s theWindowStyle
option. This gets rid of the title bar. - Set the
ResizeMode
toNoResize
. This gets rid of the remaining thin border used for resizing.
This is a good place to run your program and test out the changes.
Part 2.2. Create our basic styled window
This short part sets up the primary visual style of our clone. At the end of this part, there is no mistaking where our clone is headed.
2.2.1. Create a rectangle for our background
- In the Objects and Timeline pane, select the
[Grid]
item by double clicking it. The resulting yellow border indicates that the objects we’re about to create will be created as children of the[Grid]
. - Select the Rectangle shape from the tool bar on the far left side of Blend (see picture). After the Rectangle tool is selected, proceed to double click the rectangle tool icon (yes the one you just selected). This inserts a default rectangle into the grid.
- In the Properties pane for the rectangle, set the
Width
andHeight
toAuto
, and set theHorizontalAlignment
andVerticalAlignment
toStretch
. Then round the edges by setting theRadiusX
andRadiusY
to6
. - In the Properties pane’s Brushes section, remove the Rectangle’s outline by clicking
Stroke
, then clickingNo Brush
from the options just below. - Similarly, set the
Fill
toGradient brush
. Before setting the colors, we’ll make the gradient vertical. - Select the Brush Transform tool by pressing B or by clicking it in the toolbar: Rotate the gradient orientation by placing the mouse cursor just off the edge of the gradient arrow’s start (note the custom cursor appearance in the following picture), then click and drag to rotate until it points up. Before releasing the mouse, press the Shift key to make the rotation snap to exactly vertical.
- Again the Properties pane, adjust the gradient by changing the white end to a dark bluish-gray (about
R70 G70 B90
). - Now we’ll add the drop shadow. In the Appearance section of the Properties pane, click the downward arrow at the bottom to expand the advanced appearance options. Click the down arrow for
BitmapEffect
and selectDrop Shadow
. Set theShadowDepth
to6
andOpacity
to80%
. - If you click
[Window]
again at this point, you should have a window that looks about like this:
This is a good place to run your program and test out the changes.
2.2.2. Resize the window
- In the Objects and Timeline pane, click to select the
[Window]
- Set the
Height
property to65
and theWidth
to500
This is a good place to run your program and test out the changes.
2.2.3. Add a “3D” highlight effect
- In the Objects and Make sure [Grid] is still the target container (in yellow). Right click on Rectangle and copy it, then right click on [Grid] and paste it.
- Click to select Rectangle_Copy and press Ctrl+Shift+O to convert the rectangle to a Path. Set both the Fill and Stroke of the path to No Brush.
- In the Objects and Timeline pane, select the Border tool just like the you did for the rectangle and double click to insert a default Border object.
- We want the border to lie directly over the rectangle from the previous part, so click to select [Border] and head over to the Properties pane.
- First, bind the Width to the rectangle to force it to match. Do this by clicking the “dot” next to the Width property text box and selecting Data Binding… Select the Element Property tab, then expand the tree in the left panel until you see [System.Windows.Shapes.Rectangle] in the left (Scene elements) pane. Select this item, then select Width from the right (Properties) pane. Click Finish to complete the binding.
- Repeat step 5 to bind the Height, HorizontalAlignment, VerticalAlignment, and Margin properties of the border to those of the rectangle.
- Bind the Clip property of the border to the Data property of the Rectangle_Copy (the Path).
- In the Objects and Timeline pane, double click the [Border] to make it the target for inserting a new object (now it will get the yellow outline).
- Insert a default Ellipse just like you inserted a default rectangle before, then select it so you can work with its properties.
- First, set both the HorizontalAlignment and VerticalAlignment to Center. Set the Height to 60 and the Width to 500 like our window is. Then adjust the Width to make it a bit wider than the background (mine turned out well at 660). Notice that the Clip property of the border keeps the visible part of the Ellipse from going outside the bounds of our rectangle.
- Now this is a cool feature. We will use a “negative margin” to position the ellipse a bit off the top of our window. Adjust the top margin until the bottom of the ellipse crosses through about the center of the background rectangle. (you can click the numerical text box for the top margin in the properties pane and drag up/down to adjust with a live preview). Here is an example of the result when the margin is
-54
. - In the Brushes section of the Properties pane, set the
Stroke
toNo brush
and theFill
toGradient brush
. - Change the fill to use a
Radial gradient
(it’s a little button below the color selector). - Set the color at the center of the gradient (left side of the gradient adjuster) to
R139 G139 B255
(light blueish purple). Set the color at the edges toR32 G32 B70
(a nice dark blue). - Set the
OpacityMask
toSolid Color Brush
, make it black, and set the alpha (A
) channel to35%
.
At this point, you should be able to run your program and see a clean custom window for our clone:
2.3. Text and an icon (err… icon placeholder)
- Add a default TextBox to the
[Grid]
. At the top of the Properties pane for the image, name ittxtSearchBox
. - Set the
BorderBrush
toNo brush
, and set theForeground
to solid white. Set theBackground
to solid black with alpha 0 (R0 G0 B0 A0
) to force a white cursor to appear while typing. - Set the
HorizontalAlignment
toLeft
and theVerticalAlignment
toCenter
. - Set the left margin to
10
. - Set the
Width
to200
and theHeight
toAuto
. - Choose a font and size you like from the Text section. I used Segoe UI size 16.
- Add a default Image to the
[Grid]
and name itactiveItemIcon
. - Set its
HorizontalAlignment
andVerticalAlignment
toCenter
, theWidth
andHeight
to32
. Note: the image will appear off-center until we give it an image later. - Now add a default TextBlock to the
[Grid]
and name itactiveItemTitle
. - Set the
FontFamily
andFontSize
to the same ones you used for the txtSearchBox TextBox. - Set the
TextWrapping
toNoWrap
. - Set the
HorizontalAlignment
toRight
and theVerticalAlignment
toCenter
. - Set the right margin to 10.
- Set the
Width
to200
and theHeight
toAuto
.
This is a good place to run your program and test out the changes.
2.4. A pop-up list of search results
2.4.1. The Popup and ListBox
In this segment, will create a popup window that we can use to display a list of available programs to the user.
- Add a default Popup to the
[Grid]
. - Set the
[Popup]
to open under the text box by binding itsPlacementTarget
property to the TextBox itself (in the binding window, select thetxtSearchBox
scene element and the topTextBox
property) and checking the box for theIsOpen
property. - Double click the child
[Grid]
inside the[Popup]
to select it for placing items. - Insert a default
ListBox
.
2.4.2. Creating a simple SearchResult to represent our list items
Well I said this is the Blend part, but we’re going to take a short detour to Visual Studio that will have a huge impact on the ease and understanding of next steps. Our pop-up is going to be used to display a list of search results. At this point, we’ll create a skeleton class to represent a single search result.
First, we need to add a .NET reference to our project. In Visual Studio, from the Project menu select Add Reference… Then under the .NET tab, find “System.Drawing” and click OK.
Now add a new class to the project called SearchResult
, and fill it in as follows (adds a few properties and 2 constructors):
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public class SearchResult { private string name; private string command; private string shortcut; private System.Windows.Media.ImageSource icon; /// <summary>Gets the display nome of the search result</summary> public string Name { get { return name; } } /// <summary>Gets the search result's actual command</summary> public string Command { get { return command; } } /// <summary>Gets the filename of the shortcut on disk this result was read from</summary> public string Shortcut { get { return shortcut; } } /// <summary>Gets a WPF TmageSource for the icon to display for the search result</summary> public System.Windows.Media.ImageSource Icon { get { if ( icon == null && System.IO.File.Exists( Command ) ) { using ( System.Drawing.Icon sysicon = System.Drawing.Icon.ExtractAssociatedIcon( Command ) ) { // This new call in WPF finally allows us to read/display 32bit Windows file icons! icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon( sysicon.Handle, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions( ) ); } } return icon; } } public SearchResult( string name, string command, string shortcut ) { this.name = name; this.command = command; this.shortcut = shortcut; } public SearchResult( ) : this( "Calculator", System.Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\calc.exe", "" ) { } } |
2.4.3. Use Blend to add SearchResults objects to the pop-up’s list box
Before switching back to working in Blend, make sure to build your solution. This allows Blend to find the class we just created.
- In the properties pane for the
[ListBox]
inside the pop up, find theItems (Collection)
property and click “…” - Click Add another item, select SearchResult from the list, and click OK. Repeat this until there are 4 SearchResult objects. Then click OK to close the Items dialog.
- Set the
[Grid]
inside the pop up to have aWidth
of350
so we can actually see the results, and set itsHeight
toAuto
. - Select the
[ListBox]
again and change theHorizontalScrollBarVisibility
andVerticalScrollBarVisibility
toHidden
. - Set the background brush to solid black and the foreground brush to solid white.
- Hide the border by setting each of the
BorderThickness
values to0
.
When you run your program at this point, the window should look similar to the following:
2.4.4. Creating a template to display meaningful search results
- Create a new ItemTemplate by right clicking
[ListBox]
in the Objects and Timeline window, then selecting Edit Other Templates, Edit Generated Items (ItemTemplate), Create Empty… Set the resource name (key) to the more-meaningfulsearchResultTemplate
and click OK. - Add a default Grid to the ItemTemplate, then select it as the target for new objects.
- Find the
ColumnDefinitions (Collection)
property and click “…” - Click Add another item twice to add 2 columns. Then set the Width of each to
Auto
and press OK. - In a similar fashion, add 2 auto-height rows to the
RowDefinitions (Collection)
. - Set the
Width
andHeight
toAuto
. - Add a default Image to the Grid, then set its
Width
andHeight
to32
,HorizontalAlignment
andVerticalAlignment
toCenter
, andMargin
on all sides to3
. - Set the image to take up both rows in the first column by setting
Row
andColumn
to0
,RowSpan
to2
, andColumnSpan
to1
. - Now add 2 default TextBlocks to the Grid. Name one of them nameText and the other commandText. Set the
HorizontalAlignment
of bothLeft
and theVerticalAlignment
toCenter
- Set the nameText TextBlock’s
Row
to0
,Column
to1
, andRowSpan
andColumnSpan
to1
. - Set the commandText TextBlock’s
Row
to1
,Column
to1
, andRowSpan
andColumnSpan
to1
. - Bind the nameText
Text
by selecting the Explicit Data Context tab in the Create Data Binding dialog, checking the “Use a custom path selection” box, and typingName
, since that is the get property of the SearchResult object we want to show. Click Finish to apply the change. - Similarly, bind the commandText
Text
to the custom pathCommand
. - Now select the
[Image]
and bind theSource
property to the custom pathIcon
. - Change the Foreground of commandText to solid light gray (R200 G200 B200 works well).
- In the Objects and Timeline pane, click the Scope Up () button to return from editing the ItemTemplate.
This is a good place to run your program and test out the changes.
2.4.5. Make the main window display the selected item from the pop-up list
- Set the
SelectedIndex
of the[ListBox]
to2
. - Bind the
DataContext
of bothactiveItemTitle
andactiveItemIcon
to theSelectedItem
property of the[ListBox]
. - Bind the
Text
property ofactiveItemTitle
to the custom pathName
(explicit data context again). - Bind the
Source
property ofactiveItemIcon
to the custom pathIcon
.
This is a good place to run your program and test out the changes.
3. Conclusion
Well, that’s all for this part. In Part 2, I’ll show how to rapidly add the program logic using Visual Studio.
Really appreciate you posting this tutorial.
April 23rd, 2007 at 10:13 pm[…] Building a simple Launchy clone in WPF (Part 1/3: Blend) […]
August 28th, 2007 at 12:49 pmThanks for posting Sam 🙂
June 13th, 2008 at 5:57 amThanks Sam.
January 19th, 2009 at 4:13 amAny chance of posting the XAML?
November 26th, 2009 at 10:02 amThanks a lot.
My xaml output(if anyone wants to compare it or use as reference is a step did goes wrong). I edited a copy of the txtSearchBox template in order to remove the “onFocus” and “MouseHover” ugly blue border. http://pastebin.com/PHRvUT9Z
February 3rd, 2014 at 6:41 pm