Skip to content
Mar 15 11

Stir Trek: Thor Edition

by mattcasto

We’re bringing back the Stir Trek conference on May 6th, 2011! This year’s event is called Stir Trek: Thor Edition and, as the name suggests, we will have a private screening of Thor on it’s opening date!

The new website at stirtrek.com has already launched. This year we’ll have six tracks of content, giving you 30 sessions of the highest quality local and national speakers I’ve ever seen in a single day event. Just check out the schedule for yourself.

Registration opens this Thorsday, March 17th. With the way conferences like CodeMash have been quickly selling out recently, I’d get yours as soon as possible!

Feb 22 11

Downtown Columbus Developers Book Club Meeting 2-24-11

by mattcasto

The next meeting of the Downtown Columbus Developers Book Club is this Thursday, February 24, 2011.

In the last meeting we decided our next book would be “The Pragmatic Programmer: From Journeyman to Master” by Andrew Hunt. This meeting we will discuss the first section of the book, “A Pragmatic Philosophy.” This first section includes the first six chapters of the book.

What: Downtown Columbus Developer Book Club

When: Every other Thursday at 11:30am starting February 10th

Where: Deli Boy’s on the corner of High and Long

Why: To meet new people and learn!

RSVP: http://nerddinner.com/4175

Feb 9 11

Restarting Downtown Columbus Developers Book Club

by mattcasto

Last year I accidentally heard about a book club for developers in downtown Columbus, Ohio and decided to attend. I’m really glad I did because I’ve ended up meeting people and reading books that I wouldn’t have otherwise. Over the holidays and due to CodeMash the club fizzled, but I’d like to start it back up.

Our next meeting will be tomorrow, February 10th at 11:30am where we’ll be discussing plans for the group, including which book to read in 2011. Its short notice and if you miss the meeting, that’s okay. I’ll post here, and in the group, any decisions that are made.

What: Downtown Columbus Developer Book Club

When: Every other Thursday at 11:30am starting February 10th

Where: Deli Boy’s on the corner of High and Long

Why: To meet new people and learn!

RSVP: http://nerddinner.com/4165

Feb 3 11

Accept and Cancel Buttons Behavior in Silverlight

by mattcasto

Let’s say you start an application and a login window pops up asking you for your username and password. Once you enter your name and password, you might reflexively hit Enter and hope to be on your way.

There is an expected behavior with modal windows in client applications. Whether its a login window, a save confirmation, or an error notification, users expect the Enter button to perform some sort of action and close the window. Along with that, it is expected that the Escape key should close the window without performing any action.

Default Actions in Client Applications

In Windows Forms development, there was the AcceptButton and CancelButton properties on the Form class. You’d set these properties to the actual button controls in the Form’s children, and the Form would change it’s behavior. Likewise, WPF has something like this, albeit at bit different. In WPF buttons have the IsDefault and IsCancel boolean properties. Setting this property to True changed the Window’s behavior accordingly.

However, Silverlight’s paired down framework doesn’t include these properties or anything like them. Silverlight does have the ChildWindow class, but it doesn’t have any functionality like this.

Login Window

Recently I was working on a Silverlight 4 Out of Browser application that displays a Login window when it starts out of the browser. The application had several other dialogs in use other places without a problem up to this point, but it was painfully obvious on this one that I needed to implement something.

Sure, you an always handle the KeyPress or KeyUp event on the ChildWindow, but I wanted something a little more reusable than this. I also didn’t want to create a sub-classed ChildWindow or Button control because I wanted to be able to easily apply this to existing code without major modifications.

So I decided to create a behavior.

I started with a behavior class that handles the ChildWindow’s KeyUp event and look for Enter and Cancel keys. I also made AcceptButton and CancelButton dependency properties to have a reference to the buttons so the correct button’s Command when the desired key is pressed.

public class ChildWindowAcceptCancelBehavior : Behavior<ChildWindow>
{
    #region AcceptButton Dependency Property

    public const string AcceptButtonPropertyName = "AcceptButton";

    public ButtonBase AcceptButton
    {
        get { return (ButtonBase)GetValue(AcceptButtonProperty); }
        set { SetValue(AcceptButtonProperty, value); }
    }

    public static readonly DependencyProperty AcceptButtonProperty =
        DependencyProperty.Register(AcceptButtonPropertyName,
        typeof(ButtonBase), typeof(ChildWindowAcceptCancelBehavior), null);

    #endregion

    #region CancelButton Dependency Property

    public const string CancelButtonPropertyName = "CancelButton";

    public ButtonBase CancelButton
    {
        get { return (ButtonBase)GetValue(CancelButtonProperty); }
        set { SetValue(CancelButtonProperty, value); }
    }

    public static readonly DependencyProperty CancelButtonProperty =
        DependencyProperty.Register(CancelButtonPropertyName,
        typeof(ButtonBase), typeof(ChildWindowAcceptCancelBehavior), null);

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.KeyUp += ChildWindow_KeyUp;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.KeyUp -= ChildWindow_KeyUp;

        base.OnDetaching();
    }

    private void ChildWindow_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            ActivateButton(AcceptButton);
        }
        else if (e.Key == Key.Escape)
        {
            ActivateButton(CancelButton);
        }
    }

    private void ActivateButton(ButtonBase button)
    {
        if (button != null && button.Command != null
            && button.Command.CanExecute(button.CommandParameter))
        {
            button.Command.Execute(button.CommandParameter);
        }
    }
}

 

When the behavior is attached to a ChildWindow the KeyUp event is handled. In the event handler, if the key pressed is Key.Enter we execute the AcceptButton’s Command. Notice that I’m using ButtonBase for the AcceptButton and CancelButton dependency property’s type. This is because ButtonBase is the base class that contains the Command property. Using ButtonBase instead of Button allows us to use this behavior with HyperlinkButtons, ToggleButtons, and RepeatButtons as well.

This works pretty well, but when I was testing it on my Login window, my logins were failing. I use the Model-View-ViewModel pattern in this application, and so my controls are databound to a ViewModel class. It turned out that my failed logins were because the Password property of my LoginWindowViewModel was still blank. I was hitting the Enter key after typing in my password, but the password TextBox still had focus, so data binding never executed and my ViewModel didn’t know there was a new password! While WPF has the UpdateSourceTrigger binding property, Silverlight 4 doesn’t have an easy way to do this.

Focus, Young Grasshopper

To fix this situation, I decided to change focus to the button being activated when the key is pressed. This proved trickier than I thought it would – just calling Focus() on the button then moving on was causing the command to execute before data binding finished.

AcceptButton.Focus();
ActivateButton(AcceptButton);

 

After some trial & error, I settled on dynamically handing the GotFocus event on the button, calling Focus(), and not executing the button’s command until the button actually received focus.

private void ChildWindow_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        AcceptButton.GotFocus += AcceptButton_GotFocus;
        AcceptButton.Focus();
    }
    else if (e.Key == Key.Escape)
    {
        CancelButton.GotFocus += CancelButton_GotFocus;
        CancelButton.Focus();
    }
}

private void AcceptButton_GotFocus(object sender, RoutedEventArgs e)
{
    AcceptButton.GotFocus -= AcceptButton_GotFocus;
    ActivateButton(AcceptButton);
}

private void CancelButton_GotFocus(object sender, RoutedEventArgs e)
{
    CancelButton.GotFocus -= CancelButton_GotFocus;
    ActivateButton(CancelButton);
}

 

What About Clicks?

I was happy with this and promptly applied it to all of the ChildWindow classes in my application. A day or so later I ran across a dialog window that wasn’t doing anything when I hit Enter. What the heck? Oh … it turns out that this window doesn’t have an associated ViewModel so it handles the Click event of the buttons instead of using Commands. Fixing this should be as simple as raising the Click event on the button, right? Well, not exactly. You can’t actually raise an event of another class like that. Also, there appeared to be limitations in Silverlight that made it so you can’t use reflection to raise events on classes. I was about to give up on this and then I remembered the UI automation support in Silverlight. This functionality was added to improve accessibility in Silverlight, but also was useful for UI testing. Turns out it was useful for me here too.

var automationPeer = new ButtonAutomationPeer(AcceptButton);
var invokeProvider = (IInvokeProvider)automationPeer;
invokeProvider.Invoke();

 

The ButtonAutomationPeer class lets you call Invoke to "invoke" the Click event on a button. There’s also an abstract ButtonBaseAutomationPeer class, but in order to use it you need to check the type of the button, and return the correct sub-class – ButtonAutomationPeer, HyperlinkButtonAutomationPeer, RepeatButtonAutomationPeer, or ToggleButtonAutomationPeer.

Final Product

At this point I’m very pleased with this behavior. I can easily apply it to a ChildWindow like this …

<controls:ChildWindow x:Class="MyApp.Views.LoginWindow"
                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
                      xmlns:datainput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
                      xmlns:common="clr-namespace:MyApp.Common"
                      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
    <i:Interaction.Behaviors>
        <common:ChildWindowAcceptCancelBehavior
            AcceptButton="{Binding ElementName=LoginButton}" />
    </i:Interaction.Behaviors>
    <Button x:Name="LoginButton" Content="Login" Command="{Binding LoginCommand}" />
</controls:ChildWindow>

 

Here’s the final behavior class that I’m currently using.

public class ChildWindowAcceptCancelBehavior : Behavior<ChildWindow>
{
    #region AcceptButton Dependency Property

    public const string AcceptButtonPropertyName = "AcceptButton";

    public ButtonBase AcceptButton
    {
        get { return (ButtonBase)GetValue(AcceptButtonProperty); }
        set { SetValue(AcceptButtonProperty, value); }
    }

    public static readonly DependencyProperty AcceptButtonProperty =
        DependencyProperty.Register(AcceptButtonPropertyName,
        typeof(ButtonBase), typeof(ChildWindowAcceptCancelBehavior), null);

    #endregion

    #region CancelButton Dependency Property

    public const string CancelButtonPropertyName = "CancelButton";

    public ButtonBase CancelButton
    {
        get { return (ButtonBase)GetValue(CancelButtonProperty); }
        set { SetValue(CancelButtonProperty, value); }
    }

    public static readonly DependencyProperty CancelButtonProperty =
        DependencyProperty.Register(CancelButtonPropertyName,
        typeof(ButtonBase), typeof(ChildWindowAcceptCancelBehavior), null);

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.KeyUp += ChildWindow_KeyUp;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.KeyUp -= ChildWindow_KeyUp;
        base.OnDetaching();
    }

    private void ChildWindow_KeyUp(object sender, KeyEventArgs e)
{
    var controlWithFocus = FocusManager.GetFocusedElement();
    if (e.Key == Key.Enter)
    {
        // we don't want to do anything if the currently focused control uses the enter key
        if (controlWithFocus is TextBox &amp;&amp; ((TextBox)controlWithFocus).AcceptsReturn)
            return;

        e.Handled = true;
        AcceptButton.GotFocus += AcceptButton_GotFocus;
        AcceptButton.Focus();
    }
    else if (e.Key == Key.Escape)
    {
        CancelButton.GotFocus += CancelButton_GotFocus;
        CancelButton.Focus();
    }
}

    private void AcceptButton_GotFocus(object sender, RoutedEventArgs e)
    {
        AcceptButton.GotFocus -= AcceptButton_GotFocus;
        ActivateButton(AcceptButton);
    }

    private void CancelButton_GotFocus(object sender, RoutedEventArgs e)
    {
        CancelButton.GotFocus -= CancelButton_GotFocus;
        ActivateButton(CancelButton);
    }

    private void ActivateButton(ButtonBase button)
    {
        var automationPeer = GetAutomationPeerForButton(button);
        var invokeProvider = (IInvokeProvider)automationPeer;
        invokeProvider.Invoke();

        if (button != null && button.Command != null
            && button.Command.CanExecute(button.CommandParameter))
        {
            button.Command.Execute(button.CommandParameter);
        }
    }

    private ButtonBaseAutomationPeer GetAutomationPeerForButton(ButtonBase button)
    {
        if (button is Button)
            return new ButtonAutomationPeer((Button)button);
        if (button is HyperlinkButton)
            return new HyperlinkButtonAutomationPeer((HyperlinkButton)button);
        if (button is RepeatButton)
            return new RepeatButtonAutomationPeer((RepeatButton)button);
        if (button is ToggleButton)
            return new ToggleButtonAutomationPeer((ToggleButton)button);
        return null;
    }
}

 

Gotchas

Keep in mind that if your button’s Command or Click event tries to do something that requires user action, like close the application or access the webcam, you’ll get a SecurityException thrown. This is because of Silverlight’s sandboxed environment. There’s really no way around this except to keep it in mind and not use the behavior if you need functionality like this.

Also, there are a few things that this behavior doesn’t do that Windows Forms and WPF functionality does do. In Windows Forms and WPF the default/accept button has a different style that makes it apparent to the user what the default button is. This behavior could definitely be improved to apply a style change on the Accept button, but I didn’t take it that far.

Also, in WPF and Windows Forms if the Default button doesn’t close the window you’ll notice focus doesn’t actually change. I don’t know why you’d ever have a situation like this, but its possible. Just like the above situation, if you need your application to work this way you should probably just avoid using this behavior.

Jan 25 11

CodeMash 2011 Presentation

by mattcasto

Last week I had the honor to give my “Taking Web Applications Offline” presentation at CodeMash v2.0.1.1. The talk went into various technologies you might use if you wanted to take a web application and make it available for use with no network connection, including some features of HTML5, Silverlight and AIR.

My slides are available through SlideShare.

The demo code is available on my SkyDrive.

If you went to the presentation, let me know what you thought by giving it a rating at SpeakerRate.