MULTIMEDIA

Building a WPF Application without XAML (part 2)

10/18/2011 9:08:08 AM

3. Interacting with Application Level Data

Recall that the Application class defines a property named Properties, which allows you to define a collection of name/value pairs via a type indexer. Because this indexer has been defined to operate on type System.Object, you are able to store any sort of item within this collection (including your custom classes), to be retrieved at a later time using a friendly moniker. Using this approach, it is simple to share data across all windows in a WPF application.

To illustrate, you will update the current Startup event handler to check the incoming command-line arguments for a value named /GODMODE (a common cheat code for many PC video games). If you find this token, you will establish a bool value set to true within the properties collection of the same name (otherwise you will set the value to false).

Sounds simple enough, but how are you going to pass the incoming command-line arguments (typically obtained from the Main () method) to your Startup event handler? One approach is to call the static Environment.GetCommandLineArgs() method. However, these same arguments are automatically added to the incoming StartupEventArgs parameter and can be accessed via the Args property. That being said, here is the first update to the current code base:

private static void AppStartUp(object sender, StartupEventArgs e)
{
// Check the incoming command-line arguments and see if they
// specified a flag for /GODMODE.
Application.Current.Properties["GodMode"] = false;
foreach(string arg in e.Args)
{
if (arg.ToLower() == "/godmode")
{
Application.Current.Properties["GodMode"] = true;
break;
}
}

// Create a MainWindow object.
MainWindow wnd = new MainWindow("My better WPF App!", 200, 300);
}

Application-wide data can be accessed from anywhere within the WPF application. All you are required to do is obtain an access point to the global application object (via Application.Current) and investigate the collection. For example, you could update the Click event handler of the Button as so:

private void btnExitApp_Clicked(object sender, RoutedEventArgs e)
{
// Did user enable /godmode?
if((bool)Application.Current.Properties["GodMode"])
MessageBox.Show("Cheater!");

this.Close();
}

With this, if the end user launches our program as follows:

WpfAppAllCode.exe /godmode

he or she will see our shameful message box displayed when terminating the application.

NOTE

Recall that you can supply command line arguments within Visual Studio. Simply double click on the Properties icon within Solution Explorer, click the Debug tab from the resulting editor, and enter /godmode within the "Command line arguments" editor.

4. Handling the Closing of a Window Object

End users can shut down a window using numerous built-in system-level techniques (e.g., clicking the "X" close button on the window's frame) or by indirectly calling the Close() method in response to some user interaction element (e.g., File => Exit). In either case, WPF provides two events that you can intercept to determine if the user is truly ready to shut down the window and remove it from memory. The first event to fire is Closing, which works in conjunction with the CancelEventHandler delegate.

This delegate expects target methods to take System.ComponentModel.CancelEventArgs as the second parameter. CancelEventArgs provides the Cancel property, which when set to true will prevent the window from actually closing (this is handy when you have asked the user if he really wants to close the window or if perhaps he would like to save his work first).

If the user did indeed wish to close the window, CancelEventArgs.Cancel can be set to false. This will then cause the Closed event to fire (which works with the System.EventHandler delegate), making it the point at which the window is about to be closed for good.

Update the MainWindow class to handle these two events by adding these code statements to the current constructor:

public MainWindow(string windowTitle, int height, int width)
{
...
this.Closing += MainWindow_Closing;
this.Closed += MainWindow_Closed;
}

Now, implement the corresponding event handlers as so:

private void MainWindow_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
// See if the user really wants to shut down this window.
string msg = "Do you want to close without saving?";
MessageBoxResult result = MessageBox.Show(msg,
"My App", MessageBoxButton.YesNo, MessageBoxImage.Warning);

if (result == MessageBoxResult.No)
{
// If user doesn't want to close, cancel closure.
e.Cancel = true;
}
}

private void MainWindow_Closed(object sender, EventArgs e)

{
MessageBox.Show("See ya!");
}

Now, run your program and attempt to close the window, either by clicking the "X" icon on the upper right of the window or by clicking the button control. You should see the following confirmation dialog (Figure 2).

Figure 2. Trapping the Closing event of a Window

If you click the Yes button, the application will terminate; however, clicking the No button will keep the window in memory.

5. Intercepting Mouse Events

The WPF API provides a number of events you can capture in order to interact with the mouse. Specifically, the UIElement base class defines a number of mouse-centric events such as MouseMove, MouseUp, MouseDown, MouseEnter, MouseLeave, and so forth.

Consider, for example, the act of handling the MouseMove event. This event works in conjunction with the System.Windows.Input.MouseEventHandler delegate, which expects its target to take a System.Windows.Input.MouseEventArgs type as the second parameter. Using MouseEventArgs (like a Windows Forms application), you are able to extract out the (x, y) position of the mouse and other relevant details. Consider the following partial definition:

public class MouseEventArgs : InputEventArgs
{
...
public Point GetPosition(IInputElement relativeTo);
public MouseButtonState LeftButton { get; }
public MouseButtonState MiddleButton { get; }
public MouseDevice MouseDevice { get; }
public MouseButtonState RightButton { get; }
public StylusDevice StylusDevice { get; }
public MouseButtonState XButton1 { get; }
public MouseButtonState XButton2 { get; }
}

NOTE

The XButton1 and XButton2 properties allow you to interact with "extended mouse buttons" (such as the "next" and "previous" buttons found on some mouse controls). These are often used to interact with a browser's history list to navigate between visited pages.

The GetPosition() method allows you to get the (x, y) value relative to a UI element on the window. If you are interested in capturing the position relative to the activated window, simply pass in this. Handle the MouseMove event in the constructor of your MainWindow class like so:

public MainWindow(string windowTitle, int height, int width)
{
...
this.MouseMove += MainWindow_MouseMove;
}

Here is an event handler for MouseMove that will display the location of the mouse in the window's title area (notice you are translating the returned Point type into a text value via ToString()):

private void MainWindow_MouseMove(object sender,
System.Windows.Input.MouseEventArgs e)
{
// Set the title of the window to the current X,Y of the mouse.
this.Title = e.GetPosition(this).ToString();
}

6. Intercepting Keyboard Events

Processing keyboard input is also very straightforward. UIElement defines a number of events that you can capture to intercept keypresses from the keyboard on the active element (e.g., KeyUp, KeyDown). The KeyUp and KeyDown events both work with the System.Windows.Input.KeyEventHandler delegate, which expects the target's second event handler to be of type KeyEventArgs, which defines several public properties of interest:

public class KeyEventArgs : KeyboardEventArgs
{
...
public bool IsDown { get; }
public bool IsRepeat { get; }
public bool IsToggled { get; }
public bool IsUp { get; }
public Key Key { get; }
public KeyStates KeyStates { get; }
public Key SystemKey { get; }
}

To illustrate handling the KeyDown event in the constructor of MainWindow (just like you did for the previous events) and implement the following event handler that changes the content of the button with the currently pressed key, use this code:

private void MainWindow_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
// Display key press on the button.
btnExitApp.Content = e.Key.ToString();
}


As a finishing touch, double click on the Properties icon of the Solution Explorer, and under the Application tab, set the Output Type setting to Windows Application. This will prevent the console window from launching in the background of your WPF application. Figure 3 shows the final product of your first WPF program.

Figure 3. Your first WPF program, 100% XAML free

Other  
  •  Building a WPF Application without XAML (part 1)
  •  iPhone 3D Programming : Drawing an FPS Counter (part 2) - Rendering the FPS Text
  •  iPhone 3D Programming : Drawing an FPS Counter (part 1) - Generating a Glyphs Texture with Python
  •  Programming with DirectX : Game Input - XInput
  •  Programming with DirectX : Game Input - Win32 Input
  •  Introducing Windows Presentation Foundation and XAML : Investigating the WPF Assemblies
  •  Introducing Windows Presentation Foundation and XAML : The Motivation Behind WPF & The Various Flavors of WPF
  •  Microsoft XNA Game Studio 3.0 : Getting the Date and Time
  •  Microsoft XNA Game Studio 3.0 : Text and Computers
  •  Silverlight Recipes : Managing Embedded Resources
  •  Silverlight Recipes : Managing XAML Resources
  •  Silverlight Recipes : Updating the UI from a Background Thread
  •  Programming with DirectX : Sound in DirectX - XAudio2
  •  Programming with DirectX : Sound in DirectX - XACT3 (part 2) - XACT3 Demo
  •  Programming with DirectX : Sound in DirectX - XACT3 (part 1) - XACT3 Tools
  •  iPhone 3D Programming : Image-Processing Example: Bloom
  •  iPhone 3D Programming : Anisotropic Filtering: Textures on Steroids
  •  iPhone 3D Programming : Reflections with Cube Maps
  •  Silverlight Recipes : Networking and Web Service Integration - Accessing Resources over HTTP
  •  Silverlight Recipes : Networking and Web Service Integration - Using JSON Serialization over HTTP
  •  
    Top 10
    Algorithms for Compiler Design: THE MACHINE MODEL
    Personalizing Windows 7 (part 4) - Choosing Your System Sounds
    Sharepoint 2007: Add a Column to a List or Document Library
    SQL Server 2008 : Multiple Sources with SQL Server Integration Services
    WCF Services : Data Contract - Equivalence
    Algorithms for Compiler Design: IMPLEMENTATION in Bottom-up Parsing
    Windows Server : Branch Office Deployment - Branch Office Services (part 2)
    Windows 7 : Using Windows Defender (part 3) - Using Windows Defender Tools & Troubleshooting Windows Defender
    SQL Azure: Building a Shard (part 1) - Designing the Shard Library Object
    sharepoint 2010 : Utilizing Security Templates to Secure a SharePoint Server
    Most View
    iPhone 3D Programming : Holodeck Sample (part 1) - Application Skeleton
    Android Security : Services
    The Hello-World Midlet
    Developing an SEO-Friendly Website : Optimizing Flash (part 1)
    Architecting Applications for the Enterprise : UML Diagrams (part 2) - Class Diagrams
    Programming Microsoft SQL Server 2005 : The XML Data Type (part 3) - XML Indexes
    Server 2008 : Hardening Server Security
    jQuery 1.3 : Developing plugins - Adding jQuery Object Methods
    The Art of SEO : How Links Influence Search Engine Rankings (part 2) - Additional Factors That Influence Link Value
    Building Android Apps : Submitting Your App to the Android Market - Preparing a Release Version of Your App
    SQL Server 2005 : Transactions and Exceptions
    Deploying the Client for Microsoft Exchange Server 2010 : Planning Considerations and Best Practices
    Microsoft XNA Game Studio 3.0 : Getting Player Input - Adding Vibration
    Programming with DirectX : Sound in DirectX - XACT3 (part 1) - XACT3 Tools
    Exchanging XML Messages over HTTP
    Caching User Controls
    Mobile Application Security : The Apple iPhone - Push Notifications, Copy/Paste, and Other IPC
    Randomized Stack Space
    Server-Side Browser Detection and Content Delivery : Mobile Detection (part 4) - Device Libraries
    IIS 7 Authentication