Archive

Archive for July, 2007

Context Menus, Events and the Keyboard

July 19, 2007 Comments off

I just thought I share with you another example of using an event in the current application I am developing. As I mentioned in the previous post, I like to develop GUI applications that can be entirely managed by the keyboard, especially for data entry applications.

In this current application, I have a tab control. Inside the tab control, I place a UserControl. Inside this UserControl, I place four other UserControls whose designs are all identical, with each one representing a “screen” (and it takes four of these screens to represent an entire data record). Sort of like a Wizard Control (and I may write about this design more in the future) that allows web-like backwards and forward navigation. For now suffice it to say that only one of the four screens may be shown at a time, so I use a combination of the Parent property and Visibility to show the correct screen based on user navigation.

And it is the navigation that I want to discuss. The navigation controls are all on the Parent UserControl (we’ll call it Editor), which handles the switching of screens, etc. To enable keyboard events, I chose the easy route of adding a ContextMenu to Editor, and creating ContextMenuItems for each of the navigation options I wanted. Using the built in options of Visual Studio, I then assigned each of these items the keyboard shortcut I wanted. In this case, I wanted the screens to navigate like a web browser, so I wanted Alt-Left and Alt-Right to switch backwards and forwards between the screens. (I actually have seven of these, but these two will suffice for this example.) I then of course wired the events up in my code to react to the ContextMenuItems being clicked.

This worked great… as long as Editor was the control with Focus(). Once the user clicks inside one of the child UserControls, the Focus() shifts to that UserControl! Now my keyboard navigation ceases to function. But I had to allow the screen to retain Focus(), so that the user could continue to use the Tab key for form navigation. So, what to do?

I did some research and found an option in overriding the UserControl’s ProcessDialogKey method. This is an interesting method, and really opened my eyes to the sheer number of items that can be overridden and taken advantage of. I’m going to have fun examining some of these, but for now let’s get back to this one. The easiest way to use this is to use a Switch statement based on the method’s KeyData parameter to compare this to the values available in the Keys enumeration. I wrote some code to do this, thinking that I had solved my problem, but alas it did not work… or at least did not appear to work. Pressing the Alt-Left or Alt-Right keys did not fire the event. Instead, the Alt key would send Focus() to the Main Menu of the Main Form Window.

Since I was getting no joy, I decided to go another way. I copied Editor‘s ContextMenu onto each of the screens and wired up the items events. Unfortunately I experienced the same problem as above. It turns out that I was not managing Focus() enough. I had to add code to assign Focus() to each screen when it was activated. Doing that began allowing the Events to fire as desired. Because of this, I believe the ProcessDialogKey approach would have worked, but since I had already ripped it all out I did not put it back to test my theory.

So now that the event is firing as desired, the problem is that is is firing within the screen: but all the navigation is in the Editor! So now the task is to notify Editor that a navigation event has been requested. The answer of course is to create my own Event. Following the approach outlined here previously I created an EditorNavRequestedEventHandler delegate in the Editor code. Then in each of the screens I created the actual EditorNavRequestedEvent and fired it whenever the ContextMenuItem event fired. Rather than create a separate event for each of the possible navigations types (again I have 7 of them), I created my own EditorNavRequestedEventArgs class that I used to pass what navigation had been requested.

Now in my Editor code I added the EditorNavRequestedEvent listener and method for each of the four screens. It sounds like a lot of code, but it really isn’t. What it is is another great example of how simple events can make communication between objects very simple. Now I have a Wizard-like control that allows browser-style keyboard navigation. It also has the added benefit of a ContextMenu, which users are fairly comfortable with, thereby increasing flexibility and user options. If you don’t want a ContextMenu, remember you can still use the ProcessDialogKey approach: just make sure your Focus() is in the right place.

EDIT:

There is another way to handle this: you can have ToolStripMenuItems that activate this behavior.? The trick there is to have them percolate downwards to the currently displayed Editor control (remember this was inside a TabControl, so you could have multiple Editors at any given time.)? You would then need to execute public methods on the Editor instance to control navigation from the outside.? While it would work, I preferred all the navigation to be private, which the Event approach allowed me to do.? Also, I would not only need to identify which Editor instance was active, I would also have needed to monitor when and if there were any active so that I could set Enabled statuses for the ToolStripMenuItems.? This would need to be done everytime the TabPages property changed and whenever the SelectedIndex property changed for the TabControl. Overall, I felt like this added too much overhead, but it does offer an alternative approach.

Categories: .NET 2.0, C# 2.0

DataGridView control and the Enter key.

July 16, 2007 26 comments

Because of the type of data processing applications I develop, I try to design them in such a fashion that the mouse is largely unnecessary. This means a lot of function keys, hidden context menus, tab order, and Focus() manipulation. For the most part, with just a little forethought and effort, you can make an app very responsive to a “heads down” data processor.

Here is one example I ran in to today while working on a fairly typical database management type utility. The user performs a database search and is presented with a list of records that match the query. These records are listed in a DataGridView control and immediately after the search is complete, Focus() is transferred to the DataGridView control. Now the user can easily use the Up and Down arrow keys to navigate the Grid, but record selection becomes a little tricky.

Intuitively, the user should be able to highlight the desired record and press the Enter key on the keyboard to select the record. Unfortunately, the default behavior of the DataGridView control does not function this way: when the Grid has the focus, pressing the Enter key will advance the highlighted cell to the one immediately below it, just like pressing the down arrow key. This is not intuitive and in my case is down right unacceptable.

To bypass this behavior, we need to monitor the KeyDown event, and if the Enter key has been pressed, instruct the Grid control to ignore its default behavior. We do so by setting the KeyEventArgs Handled property to true:

private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyData == Keys.Enter)
    {
        e.Handled = true;
    }
}

Now the highlighted cell will not change when the Enter key is pressed. Then in the KeyUp event, we can instruct the Grid to perform whatever task we wish to process the record:

private void dataGridView1_KeyUp(object sender, KeyEventArgs e)
{
    if (e.KeyData == Keys.Enter)
    {
        this.RecordSelected();
    }
}

And now we have a DataGridView control that handles the Enter key in a more intuitive fashion.

Categories: .NET 2.0, C# 2.0

Event Handling made easy

July 11, 2007 Comments off

UPDATE NOTICE: This approach has been updated to take advantage of the EventHandler<T> generic delegate. The updated approach is covered in Event Handling made even easier.


If you have been around .NET for a while, you have probably already discovered Events and may be confident in their application. For me, it is something that I inherently understand but have not used enough to remember all the pieces from scratch. As a result, every time I find a situation that I think calls for Event Handling, I have to relearn the physical process of it from scratch. So I decided to write this little tutorial so I would always have it laid out for myself in one place.

Why Events are so Cool

Events have some really cool benefits. First of all, they can be wired into any object. This lets any object report information about itself. Secondly, the events can be monitored or ignored – it is up to other objects to listen or not. The uses of this are pretty much infinite, but I’ll lay out two that I think will make sense for just about all of us.

State Reporting – Imagine a class that encapsulates a record in a database. Now imagine you have a method that reports whether or not the data is “dirty” (meaning that some data has changed and needs to be updated to the database). In order to make use of this information, you need to run the CheckForIsDirty method whenever the object may have changed. This could be a lot of calls to the same method, and could easily be missed in your code. Events offer us another approach. If the object did the checking itself (say in the Set property for each data element) it could fire an IsDirtyChangedEvent whenever the state changes from true to false or vice-versa. Now in the object that needs the information – say to set the Enabled state of a Save button – you simply wire up a single event handling method to the object.

Error Reporting – Similarly, you could use an Event to report errors, rather than the typical Exceptions being thrown and caught. For one thing, while Exception handling is robust, it is also kind of a pain. In order to do it well, you should handle every Exception that could be thrown. This is tedious and the Exception code you write is rarely used. Consider also that a lot of us (myself included) get lazy with Exceptions and just use a standard catch (System.Exception ex) style catch block. This of course defeats the purpose of the robust exception handling offered by .NET. But in those cases, what we really want to know is if any error occurred at all. But the worst of all is when the unexpected exception occurs… you know, the one you don’t plan for that never shows up in testing but that the user seems to find no problem.

So now, let’s consider the same object from above, an encapsulating database record object. Potentially, we could have all kinds of errors caused by invalid data. Simple issues like code-driven fields or too many decimal places, etc. etc. Or it could even be a complex issue with multiple fields having different rules based on the values of other fields. Do you really want to throw (and therefore need to catch) every possible Exception? Sometimes yes, sometimes no. If you are looking for a simple “catch-all” (forgive the pun please) error reporting system, you could create a simple MyObjectErrorOccurredEvent. Throw this event where ever you would have thrown an Exception. Again, in your parent object, you can define a single method to handle all the errors.

These are just a couple examples of things you can do. In the Sketching tool I wrote about recently, I used an Event to notify the parent when each sketch had been created. The parent then updated a counter and reported the progress back through a delegate to the GUI thread. Very slick, and fairly easy.

Enough already, how do I do it?

Fair enough. This is my simple method of doing Events. There are five steps, which I’ll outline in order.

Step 1: Outside of any Class, create a public delegate to define the EventHandler signature.

public delegate void IsDirtyChangedEventHandler(object sender, IsDirtyChangedEventArgs e);

The (object sender, xxxEventArgs e) signature ought to be very familiar to you. This is the basic signature you get for every button_pressed, on_click, mouse_up, etc. etc. event. In our case, we have a custom xxxEventArgs class that we are going to create. Feel free to replace this with any of the standard EventArgs, so long as they will suit your purpose. I find that they usually don’t so creating our own is the way to go.

Step 2: Create our custom EventHandler class. Again, this is optional if you choose a standard EventArgs class. This class is very simple, just a collection of values, really. In our case, it is just going to be one value: a boolean representing whether or not the object IsDirty.

public class IsDirtyChangedEventArgs
{
    private bool _isDirty;

    private IsDirtyChangedEventArgs()
    {
    }

    public IsDirtyChangedEventArgs(bool isDirty)
    {
        _isDirty = isDirty;
    }

    public bool IsDirty
    {
        get { return _isDirty; }
    }
}

Step 3: In the class that is firing the Event, we need to define the event so that other objects can handle the event.

public event IsDirtyChangedEventHandler IsDirtyChangedEvent;

Note that while the Type is a IsDirtyChangedEventHandler, we traditionally leave Handler off the name.

Step 4: Fire the Event. This is pretty straight-forward: when ever you reach the right place in your code, fire the event.

public bool IsDirty
{
    get
    {
        return _isDirty;
    }
    private set
    {
        if (_isDirty != value)
        {
            if (IsDirtyChangedEvent != null)
            {
                IsDirtyChangedEvent(this, new IsDirtyChangedEventArgs(value));
            }
        }
        _isDirty = value;
    }
}

In this case, I have used a private Set property to set the internal variable. Before I update it, I see whether or not it has changed, and if it has then I Fire the event. This way (as long as I always set it using the Property and not accessing the variable itself), the notification is always sent out regardless of what triggered the change. Note that we check to see if the Event is NULL prior to attempting to fire it. If there is no method registered to handle the event, the Event call itself will result in a NullReferenceException.

Notice that I have also employed our custom IsDirtyChangedEventArgs class when I fired the event. If the EventArgs object was any more complicated, I would probably have put it on its own line first.

Step 5: Wire the event up in the parent. Now that I have the Event defined and firing, all I need to do now is add a listener to capture the event in my parent object. There are two parts to this: first you have to register the Event listener with the object.

MyObj _myObj = new MyObj();
_myObj.IsDirtyChangedEvent += new IsDirtyChangedEventHandler(_myObj_IsDirtyChangedEvent);

Of course, VisualStudio and Intellisense will write most of this very ugly signature for you. Even better, once created it will allow you to press TAB and it will create part 2, the actually handler event.

private void _myObj_IsDirtyChangedEvent(object sender, IsDirtyChangedEventArgs e)
{
    this.SetSaveButton(e.IsDirty);
}

As you can see, I have added the code I want to execute whenever the change event is fired.

Conclusions

And that is my simple Event handling method. They really are quite simple, and the sky’s the limit as far as their usefulness goes. So if you haven’t done it before, do it now! If you have done some Events, feel free to comment on my methods above.

Categories: .NET 2.0, C# 2.0
Follow

Get every new post delivered to your Inbox.