Monday, October 27, 2008 #

Learning WPF With BabySmash: Redux. BabySmash KeyBoard Click Events in WPF

Now that I had my Amazing Qwerty KeyBoard, I had to hook it up. BabySmash uses the OnKeyUp event in the MainScreen as its hook to the business logic:

public partial class MainWindow : Window
{
    private readonly Controller controller;
    public Controller Controller { get { return controller; } }

public MainWindow(Controller c)
{
    this.controller = c;
    this.DataContext = controller;
    InitializeComponent();

}

....

protected override void OnKeyUp(KeyEventArgs e)
{
    base.OnKeyUp(e);
    e.Handled = true;
    controller.ProcessKey(this, e);
}

...

}

Since I wanted to mimic the existing behavior as closely as possible, I had to create a corresponding event for my on-screen key board, and then have the MainWindow handle it in the same manner.

My keys are <Button/>s, so I used the "Click" event on all of my keys:

<Button Name="Q" Grid.Column="0" Grid.Row="0" Click="HandleKeyBoardClick" >

<Button Name="W" Grid.Column="1" Grid.Row="0" Click="HandleKeyBoardClick" >

...

<Button Name="M" Grid.Column="2" Grid.Row="6" Click="HandleKeyBoardClick" >

I then implemented the "HandleKeyBoardClick" class in the code behind:

    protected void HandleKeyBoardClick(object sender, RoutedEventArgs e)
    {
        Button b = (Button) sender;
        NewKeyBoardClick(b.Name);
    }

I abstracted the actual logic of the event raising in case I ever wanted to raise the event in a different way. My NewKeyBoardClick method takes the desired key as a plain-text string (ie: "Q"). I chose not to convert back and forth from ascii codes because I really didn't see any value in doing so.

public void NewKeyBoardClick(string keyName)
{
    //Raise a key click event for the specified key
}

 

Now I needed a KeyClick event. After much googling and pain (It was during this activity that my KeyBoard user control briefly became a custom control before it was reincarnated back in it's present form.) I not only needed a custom event, I needed a custom event argument (which key was clicked).

Here is what I came up with:

First, I created my custom argument:

public class KeyBoardClickArgs : EventArgs
{
    public string KeyName { get; set; }
}

 

Then I created the delegate for my event:

public delegate void KeyboardClickHandler(Object sender, KeyBoardClickArgs args);

Then I created my event:

public event KeyboardClickHandler KeyBoardClick;

Now, I needed to raise the event with the proper argument in my NewKeyBoardClick class. First I instantiated my KeyClickArgs and set the keyName property. Then I check for any subscribers to my event, and if I find any, I raise my event:

 

public void NewKeyBoardClick(string keyName)
{
    KeyBoardClickArgs e = new KeyBoardClickArgs();
    e.KeyName = keyName;
    if (KeyBoardClick != null)
    {
        KeyBoardClick(this, e);
    }
}

 

Now my control is ready to go! (I know, I haven't implemented the size property yet - that came later.)

I added the KeyBoard control the the MainPage window:

xmlns:local="clr-namespace:BabySmash"

<Canvas Name="figuresCanvas"><local:Keyboard3 x:Name="KeyBoardC" Visibility="Hidden"/></Canvas>

I set it to "Hidden" because I only want to display it if we are in "drewbie mode".

Now I just needed to hook up the new event:

First I added it to the MainWindow constructor:

KeyBoardC.KeyBoardClick += new Keyboard3.KeyboardClickHandler(KeyBoardC_KeyBoardClick);

Then I used the event in the same way that MainScreen uses its OnKeyUp event:

void KeyBoardC_KeyBoardClick(object sender, KeyBoardClickArgs e)
{
     controller.ProcessKey(this, e.KeyName);
}

Success!! The MainWindow is responding to keyboard clicks from either the real keyboard or my on-screen keyboard control in exactly the same way! Now I can go ahead with modifying BabySmash and implementing new "DrewbieSmash" features without thinking about the input mechanism.

I'll work on getting the source code available. Until then, email me.

posted @ Monday, October 27, 2008 12:09 PM | Feedback (0)

Learning WPF With BabySmash: Redux. My Amazing BabySmash Qwerty Keyboard

The first thing I did after downloading BabySmash was to begin building an on-screen keyboard. I installed Microsoft Expressions, but quickly became frustrated. That was a surprise to me because I am a Photoshop pro!! (On portraits anyway). Despite the admonishing of many, I switched over to Visual Studio and began coding my keyboard by hand. Now I was on much more familiar territory! I do plan to return someday to Expressions when I have a more open mind. But for now, I want to build DrewbieSmash!

The following are my requirements for my keyboard:

1. Appears in the bottom right corner of the BabySmash main window when BabySmash is in "drewbie mode".

2. Is resizable.

3. Sends a key-click event to the main window when a key is clicked on. I wanted to reuse as much code as possible, and hook the on-screen keyboard right into where the real keyboard is hooked in.

I tried many things... first I made my keyboard a <Window />. Mainly because the sample xaml files I was working with were all <window /> s, and I was just trying to do some cut-and-paste development to get my feet wet with WPF. Getting my window to render inside the main window as I wanted it to appear was problematic - probably because I didn't know what I was doing. And probably because putting a window inside another window is probably a dumb idea.

Then there was this challenge... BabySmash is architected so that all logic is executed by a controller class. The controller class requires a FrameworkElement argument and uses this for where it performs the animation. The original BabySmash main window was both the input and the action window, so it just used "this". Since my events were coming from the KeyBoard window, I had to get a reference to the Main window before I could execute the animations in the MainWindow via the controller class. Furthermore, it was apparent to me that BabySmash was really architected around having a single main window, and by continuing down this path I would likely run into additional challenges when trying to work with a second window.

So I took a step back and realized that in a .NET application, I would make the keyboard a <usercontrol />, and give it a "KeyClicked" event.

Since the keyboard object doesn't know anything about the size and real-estate concerns of the containing object, the keyboard was coded to size itself relative to the parent window. There were no pixel values coded into the Keyboard xaml. So originally I thought that the Keyboard size could be entirely controlled by the parent window. However, I soon realized that I have to change the font size as the key board is resized. So I added a "size" property to the KeyBoard control. I didn't want to use explicit pixel values because we still don't know what is going on with the parent. So I made the KeyBoard accept 4 sizes: Large, Medium, Small, Standard. (Standard being the smallest and the size of the on-screen keyboard that ships with the Windows OS. If someone has a better name for that one, please let me know!!!!)

Now, even though I don't know what the actual size of the KeyBoard will be, I can make a guess as to the size the font should be. And this works very well.

 

Here is a screen shot of my Amazing BabySmash keyboard. You can see by the name of the control that this is my third attempt at creating a keyboard. The next blog post will be about how I coded the KeyClicked event and how I modified the BabySmash application to use it.

ScreenHunter_01 Oct. 27 09.50

posted @ Monday, October 27, 2008 11:10 AM | Feedback (0)

Copyright © Kirstin Juhl

Design by Bartosz Brzezinski

Design by Phil Haack Based On A Design By Bartosz Brzezinski