Archive for the ‘.NET’ Category

MSDN Redesign Number Ten Million

Wednesday, June 30th, 2010

Microsoft has updated MSDN again for what feels like the third or fourth time in a couple of years.  Microsoft is really concerned with making sure it’s easy for new developers to pick up .NET, but I don’t think this redesign does it.

I think they missed the mark for true beginners.  I’ve never met anyone that started coding by saying, “I think I’d like to learn fundamentals of a programming language, then fundamentals of a GUI, then read a few dozen HOWTO articles that don’t have a lot to do with real applications.”  Instead, we start by saying something like, “I want to make my own Minesweeper” or “I want to make a Pac-Man clone”.  This redesign falls flat for this user.

The “desktop” page does a good job right up to step 4, the introduction videos.  “Build your first desktop RIA application with Silverlight?”  Why not just say “Build a smurfing smurf smurfer using SMURF?”  I think the video titles focus too much on what tools are used and not enough on getting the developer excited about what he’ll be doing.  Better titles would be “Build a desktop Twitter client with WPF” and “Build a browser or desktop Twitter client with Silverlight”.  When I was new, I’d expect a “my first application” tutorial to be simple.  If I saw a 30 minute “build your first application with WPF” I’d assume that WPF made it really complicated to build simple things.  If instead I saw “Build a Twitter client with WPF” I’d have understood that the video probably used some advanced features that needed explanation.  (On a side note; why on Earth is the thumbnail for the Silverlight code that sets up a WNDCLASSEX?)

Step 5 falls flat by showing a disorganized bullet list of links that are randomly bolded.  Section headers would be nice, but it’d work if similar concepts were grouped together.  Why is the MS Office developer center there?  Why is there a link to the VB developer center but not the C# developer center?  I’m not really sure what Microsoft was going for with these links.

When I first started, I thought MSDN was the appropriate resource to learn about writing Windows Forms applications from scratch.  This just isn’t the case.  MSDN is written in a technical tone and many of the articles assume at least a moderate familiarity with .NET.  There’s articles that help new developers, but they’re buried very deep in the Library hierarchy.  The “Getting Started with Windows Forms” page is exactly what I needed as a newbie who needed to write a WinForms application in a month.  Getting there is simple:

  1. Go to msdn.microsoft.com
  2. Click “Library”
  3. Click “.NET Development”
  4. Click “.NET 4”
  5. Click “Windows Forms Portal”
  6. Click “Getting Started With Windows Forms” (It doesn’t show up in the sidebar like on all of the other pages!)

If I go through the chain that leads me through WindowsClient.net it’s just as bad.  I get 3 links to help with Windows Forms: blogs (which are 99% about WPF), a guided tour of WPF (WTF?), and a videos page.  The videos page leads with “How Do I: SqlAzureLOB Line of Business” (mandatory “ATM machine” or perhaps “WPF Windows Presentation Foundation GUI Graphical User Interface” joke).  Is that what Microsoft’s research has indicated helps new programmers understand how to write applications?

I understand that WinForms is supposedly on its way out.  I haven’t noticed a decrease in students using it on the forums I frequent.  I gave the WPF links a whirl, and they’re in better shape, but the video page is useless to a newbie.  The first video on the page is “Create WPF Master-Detail UI Using Data Sources Window Object DataSource”.  I’ve used WPF for nearly 2 years now and I don’t know what “Data Sources Window Object DataSource” is supposed to mean.  Why isn’t MS leading with “Creating a Standard WPF Application” and listing the videos in increasing difficulty order?  Why isn’t there a “difficulty” or “intended audience” field that can sort the videos?

What I would change

The MSDN Library is most useful for a professional developer, but I feel like newbies should be made aware of the documentation.  The “How Do I” videos look nice, but they run the gamut from beginner to advanced with no indication as to their level.  They should have a level and they should be sortable by this level.  Users should be allowed to rate them and comment on them as well so newbies can be warned when someone labels “Write a SharePoint application using AzureCloudSqlSharePointLOBPOSWCF and ham" as a beginner video.

MSDN should really promote their forums as well.  I learned at least 80% of my WinForms knowledge from forums.  As part of promoting the forums, MSDN should also clean up the forums; my suggestions for that could easily fill another post.  The summary: you don’t need to dazzle newbies with over 100 different forums.  Make a “Basic .NET development” page with five forums: WinForms, WPF, Silverlight, Native, and ASP .NET.  Don’t make newbies have to decide if their question should go in “C# general”, “VS 2010 Development”, “WPF Development” or “.NET 4 WPF Beta Discussion”!

I think Microsoft should find a way to promote their blogs better as well.  It’d be nice if they kept an archive of the good blog articles rather than using an RSS feed of the most recent ones.  Pete Brown’s blog is chock full of great Silverlight tips ranging from beginner to advanced, but you wouldn’t know this from browsing MSDN.

A redesign won’t solve these problems.  Video in and of itself is not a solution to the problems that need to be addressed.  If Microsoft wants to help newbies, they need to buckle down and put some effort into designing an intuitive and accessible experience.  Right now the only reason I can find anything at all is my 8 years of experience with wrestling MSDN.

I Joined a Project for Fun

Monday, September 7th, 2009

User ident on the XVBT forums asked for some comments and suggestions about his project.  I took a look at it and decided the number of comments sans explanation of fixes would exceed the size of a post, so I asked if I could join the project and make some lessons out of refactoring it.

The project itself is a neat little tool for automating ImageShack uploads.  On the outside, it looks pretty slick.  After inspecting the code, I have a bad feeling that it doesn’t work anywhere near as slick as it looks, but that’s why I’m here.  The application is an almost perfect example of what WinForms development does to suppress good software design practices.  In this ~3,000 line application, 2,600 of the lines are in ClientForm, the main form of the application.  This form is responsible not only for UI interaction, but all of the application logic.  There’s many other errors that are more related to what happens when a novice writes a large application than what WinForms inflicts upon you: dozens of unused methods, huge areas of commented-out code, no consistent naming conventions, etc. There’s at least a wrapper for the ImageShack API, but I haven’t peeked inside of that yet to see if it needs some work as well.  It looks like it was lifted from a blog by a man named Bryce Thomas, then converted from C# to VB.  I’m not sure if any attribution is currently given; if not I’m going to fix that.

The code for the client itself is not under any particular license, but does happen to have this notice at the top of each file:

'*************************************************************
'
' DX Coders
' Copyright 2001-2009 Simple Coders
' All Rights Reserved
' Created: 09/06/09                                   *
' Author: Johnson

' NOTICE:  DX permits you to use, modify, and
' distribute this file in any way.
' DX takes no responsibility on how you use
' this code. It is strictly for learning purposes
' and is not designed to cause harm. If you decide
' to reuse this code please give credit where credit is due.
'*************************************************************

I have entertained the notion that the developer(s) (there is a mysterious second developer involved that I’ve never met) are planning to have me clean up their application for free, then cut off my access to the project and sell it.  I would be disappointed if this happens, but I decided that the experience of reworking an application from a mess into something polished is something I need more than defense against my hobby work benefiting someone else.  The comments above mean that, should they excommunicate me from the project I have no qualms about releasing the source code and promoting the project as a fork under a very permissive copyleft license.

I have one concern at this point.  The code looks like it’s either a mishmash of examples gathered from various forums or the result of running a C# to VB .NET converter on a pre-existing project.  Indeed, one of the more questionable bits of code is admitted to be the result of running a converter on some C# example code.  If you have written an ImageShack upload client and this one looks familiar, please contact me with proof.  I will not work on a project that was ripped off.

As I work on refactoring, I’m going to make posts that detail why I did what I did.  I’m going to cross-post these onto the blog.  I’d normally do them *only* on the blog, but I don’t want to undermine XVBT’s policies by taking discussion of a user problem offsite.  I’m already skirting the edge by taking on a full project.

UI Control Naming Conventions

Thursday, September 3rd, 2009

Microsoft was kind enough to publish a set of design guidelines for developing class libraries, and I use the book Framework Design Guidelines that is based on them daily. This has settled many disputes over the conventions our team should follow, and it’s the basis of many suggestions I make on the XVBT forums. The guidelines help lay out the practices that the .NET Framework team tries to adhere to when designing types. If everyone adheres to the guidelines, then the entirety of the .NET ecosystem will be consistent and usability will increase.

The guidelines have an odd position in the documentation. I find that most of MSDN and Microsoft’s other efforts are focused on helping the application developer. Contrary to this position, the design guidelines are focused on developers of class libraries that will be used by other developers. This is what I do, so it’s great for my demographic to get some love. However, the lack of focus on application development means there’s some important guidelines that aren’t there.

Chapter 3 of the books involves naming guidelines; you can follow along sans commentary online. The chapter discusses capitalization conventions and how to name every aspect of your code from variables to interfaces to DLLs. One omission that would be useful for application developers is guidelines for naming controls in applications.  I’ve experimented with 3 approaches, and each has its disadvantages.

1. Hungarian Notation

If this is established as the guideline, Hungarian notation is used for each control. That is, a text box for name input becomes txtName and a list view for displaying songs becomes lvSongs. Optionally, a leading underscore might be used, but I tend away from it when using this convention.

One benefit of this notation is familiarity. VB6 developers in particular favor this approach because it follows the generally agreed-upon conventions used in that language.  Another benefit is an increase in understandability.  When you see txtName, it is clear that this is a text box that represents a name.

One drawback is the naming guidelines use a strong DO NOT guideline to condemn Hungarian notation.  Nothing else in .NET uses Hungarian notation. This makes applications that follow the notation seem like they stick out a little. Another drawback is the notations aren’t really standardized. Most people agree on txt for TextBox and lv for ListView because these controls have been around forever, but newer controls like TableLayoutPanel and Grid don’t have agreed-upon prefixes. Also, how do you decide what to use for custom controls, UserControls, and third-party controls? One more drawback is this notation makes code brittle to change. Maybe your prototype starts with a TextBox but later you switch it to a NumericUpDown. This means you not only have to change the control and any logic that worked with it, but you also have to hunt down all references to the variable and rename it. (I admit I’m a relative newbie to Presentation Model patterns and it could be that this is less of a concern in those since the form would likely expose the control as a typed property, but I think it’s still applicable.)

To me, the drawbacks seem to outweigh the benefits. However, this is the convention I learned when I started in VB .NET many years ago, so I find it most comfortable and no matter how many arguments against it I hear, it’s the one I use.

2. Suffixes

When following this guideline, you pick a descriptive name and suffix it with the type of control. Use of camelCase or PascalCase is disputed, but generally I see PascalCase used more prominently with this technique. Using the examples from above, we’d use NameTextBox and SongsListView.

This has practically the same benefits and drawbacks as Hungarian Notation. You’re still identifying the type of control alongside the purpose, and it’s still clear from the variable name what kind of control is at work. This is technically a form of Hungarian notation, so still falls afoul of the DO NOT guideline. The problem with standardized prefixes is eliminated: since you use the full type name there’s no need for agreement. This technique is still brittle if you change controls.

Personally, I find that this convention clashes with how I think about the UI and makes it harder for me to find the variable, but this is probably because I’ve used Hungarian notation for years. I’ve tried following this guideline, but I find that more often than not I forget if I chose ItemCount or NumberOfItems as the prefix, and knowing the suffix doesn’t help me find it. Perhaps my naming discipline is poor, but it just doesn’t work for me.

3. Plain Old Variable Names

When following this guideline, you pick a descriptive name for the control and use that. There’s dispute over casing and usage of a leading underscore, but I find that two camps are the most prominent: PascalCase and _camelCase. Using our example controls, we would use Name and Songs or _name and _songs.

There are a few benefits to this approach.  One benefit is you aren’t using Hungarian notation so you aren’t falling afoul of any DO NOT guidelines. Another benefit is the code is no longer brittle if you change the control type.

Despite the benefits, there are many drawbacks to this approach no matter what casing convention is followed. If we’re writing a custom control or a custom UserControl that needs a text box for a name, then Name is already taken by Control.Name and we’ll have to pick a different name like PersonName. This can lead to accidental use of Name when you meant PersonName, and since you aren’t using the first name that came to mind you’re going to spend a lot of time trying to remember what your 2nd choice was. In addition, this casing convention is used for properties and constants, so if you have many of those your controls will be buried among them in Intellisense. This is why many people use the _camelCase convention, but since this is a very common convention for private fields it’s not much better.  Is _songs the control that represents the songs or a collection of songs? If you’ve got 20 properties with backing fields and 5 controls on the form, you have to wade through 25 entries if you can’t quite remember the correct name. _camelCase also looks particularly heinous when used as the x:Name attribute in XAML.

I find I am always dissatisfied when I use this convention.  If I use PascalCase then I almost always get the controls mixed up with constants. Even more often, the control name clashes with a property that I want to have and I end up having to pick a worse name for the control.  Then, I can’t remember the name I picked.  If I use _camelCase then the controls are buried among property backing fields, and name clashes still happen.  If _songs is the control that represents a list of songs and I want a Songs property, what do I do for the backing field? (There’s a few answers that involve generating the property value in different places, but most of them wouldn’t occur to a novice.) No matter how I try to follow this convention, I don’t like it.

I’m sure there’s other approaches I haven’t thought of, but these are the three I’ve tried. All of them have drawbacks that seem to outweigh the benefits, but I side with Hungarian notation out of familiarity. If there were guidelines to follow, I’d settle on the method they suggest, but there are none so I have a hard time deciding if I’m right.  What do *you* do?

Using a TextBox as an Adorner

Friday, March 13th, 2009

I was fooling around with an adorner in a test project and decided to use a TextBox instead of a ComboBox for convenience.  Unfortunately, I found I couldn’t type into the TextBox.  According to Jim Nakashima, this can be addressed by setting the AdornerPanel.IsContentFocusable property, and he’s right.

I am bothered by the “bug” that sends “Delete” input to the designer instead of the TextBox.  I don’t call this a bug, I call it a showstopper.  Oh well, I guess I’ll be able to use TextBox controls in my adorners when we are able to drop VS 2008 support in 5 or 6 years.

Inheriting from ReadOnlyObservableCollection<T> and a Lesson about Constructors

Monday, July 7th, 2008

I had need of returning a bindable collection from one of my classes, but didn’t want to allow the caller to modify the collection.  This generally requires a straightforward pattern in .NET: make a SomethingCollection class that derives from Collection<T> and a ReadOnlySomethingCollection class that derives from ReadOnlyCollection<T>, then only return the read-only version.  Most examples don’t bother with declaring the derived types, but I feel like it makes everything look better and the framework classes seem to favor declaring their own collection types as well.

I was having one heck of a time getting everything to work. I tried the most basic implementation:

public class ReadOnlySomethingCollection : ReadOnlyObservableCollection
{
}

This failed miserably with a fairly archaic error message:

‘full typename’ does not contain a constructor that takes ’0′ arguments

I struggled with this for a while. The error message was clearly correct; if you walk the inheritance tree there’s no base class that defines a parameterless constructor. Still, defining a constructor that matched the signature of the 1-parameter constructor didn’t seem to be solving the problem. I decided it’d be best to step outside my project and make a simple example project in preparation for asking forums and newsgroups what was going on. It turns out switching languages to VB .NET was a good idea, too, because its error message was much more helpful once I had the constructor in place:

First statement of this ‘Sub New’ must be a call to ‘MyBase.New’ or ‘MyClass.New’ becasue base class ‘…’ does not have an accessible ‘Sub New’ that can be called with no arguments.

I saw this, thought about it for a minute, then had a forehead-slap moment. When a class derives from another, it’s almost as if you have two objects glued together. The base class has to be initialized before the derived class can be initialized, because the derived class’s constructor must be able to assume its base members are in a good state. So, in my case, the compiler was trying to initialize a ReadOnlyObservableCollection<T>, but since there was no parameterless constructor it couldn’t. The solution was to do exactly what VB said: make sure I call some base-class constructor as my first action. I finally got it to work in C#:

public class ReadOnlySomethingCollection : ReadOnlyObservableCollection
{
    public ReadOnlySomethingCollection(ObservableCollection list) : base(list)
    {
    }
}

This isn’t the first time that it’s taken VB error messages for me to understand the solution to my problem; I wish there was more consistency between the error messages.

Programmatically Creating Windows File Associations

Monday, June 2nd, 2008

A user on the VB Development Forum I’ve visited for a few years asked a question about creating file associations.  I chuckled and assumed that a Google search would return a wealth of information about the subject.  I was wrong; by my fifth combination of search terms I had found one site that had the minimal steps to get it done, and it wasn’t even correct.  The rest of the results were happy to discuss using the File Types tab of the Folder Options dialog to accomplish the task; this is not programmatic and not what the user wanted.  I remembered having a heck of a time trying to figure out how to make an association for all files (so I could have a right-click->Edit with hex editor option) so I decided I’d write up what I know.  Shame on you, internet, for letting the information get buried.

To start off, my example is technically incorrect.  I’m working with HKCR, but apparently the content here is generated based off of HKCU\Software and HKLM\Software. I assume this is so you can have per-user and global associations; I wrote my example using HKCR directly but you should probably use one of these two; please understand when I say HKCR I mean one of the other two locations.

I found the File Associations documentation, but haven’t had time to look over it properly.  For now, this should do.  In order to make it so that when you double-click a file your application opens you need three things:

  1. Your application should support opening a file from the command-line in some way.
  2. You need a registry key to associate a file type with the extension.
  3. You need a registry key to associate a program with the file type.

The first one is up to you; I can’t tell you how to do it.  The registry key to associate a file type with an extension is pretty easy.  File extensions are listed in HKCR under keys named after the extension (including the period!) The value of these keys is a string that defines the file type associated with the extension.  I tend to not use spaces in these, but I have no idea if spaces are unsupported; it will end up being the name of a subkey later and I’m too lazy to check if spaces are supported in subkey names.  For example, text files have the extension txt, and the key that specifies the file type is HKCR\.txt.  It’s default value is txtfile.

Now that there’s a file type associated with the extension, you need a registry key that tells the shell how to handle operations on the file type.  There’s lots of things to implement here, but I only know the basics.  File types are declared in subkeys of HKCR named after the file type; their subkeys and default values vary based on exactly how the file type is to be handled.  All I’m going to discuss is handling the "open" verb, which is generally invoked by double-clicking a file in the shell.  To add support for this verb, a tree of subkeys need to be created under HKCR\filetype. Working with our Notepad example above, we see HKCR\txtfile\shell\open\command.  It has a value that represents the path to Notepad and the replacement string %1 for the file that was selected.

Example time

Suppose we want to associate Notepad with the file extension .zzz.  We’ll start by defining a key for the file type:

HKCR\.zzz
Value="MyEditorFile"

Now, we create the file type key and associate the "Open" verb with notepad:

HKCR\MyEditorFile\open\shell\command
Value="%SystemRoot%\system32\NOTEPAD.EXE %1"

Note that though there are no quotes in the value for the open key, I've had trouble in the past when this string didn't have anything that could possibly contain a space quoted. For example, I'm used to using:
"C:\Program Files\My Program\program.exe" "%1"
I'm not sure why Notepad's special and doesn't require this.   That's really all I have to say on this topic for now; I am still looking over the documentation.

VS Project Templates are Aggravating

Friday, April 18th, 2008

I love Visual Studio, but some of the work I have to do to make things work the way I want is aggravating.  I just started a new WPF solution to follow along with a chapter in the book, and decided to give it a descriptive name so the directories would look all nice: "Chapter 8 – Styles".  Visual Studio assumes you want to use the solution name as the default namespace for all projects (unless you specify different project and solution names; then it uses the project name).  Of course, the namespace naming rules are more strict than file naming rules, so I end up with the lovely namespace name "Chapter_8___Styles".  Lame.

What’s worse is even if I go into project settings and change this, it only affects new files added to the project, so I have to go through the solution and change every namespace reference.  For the WPF application project template, that’s changes to 3 files before I can build and run.  It sure would be nice if I had an easy way to change this.

"Out of Memory" Errors with GDI+ TextureBrush

Saturday, March 29th, 2008

The TextureBrush class lets you draw a shape using an image as a texture, giving you some control over the way the texture is tiled.  I encountered a forum post where a user was trying to use TextureBrush to draw sprites to the screen, but he was upset because as he moved the rectangle around it wasn’t moving the texture; the result was similar to having a tiled background covered up and viewed through a small window, then moving the window around.

I looked at the documentation, and saw constructors that let you specify a bounding rectangle.  I misinterpreted what the bounding rectangle did, and thought it specified where the top-left corner of the tiling should start.  The odd thing was introducing this rectangle led to a generic GDI exception with the message "Out of memory."  I found this curious.  At this point I realized that for the problem at hand, there was no reason to use a TextureBrush as opposed to Graphics.DrawImage, so I suggested this course of action.  Of course, I was curious why there was an out of memory exception, so I dug deeper.

Google was no real help; there were basically two forum threads about the problem, and none had a resolution (one suggested a possible accidental recursive paint handler, but that’s ridiculous since the second message wouldn’t be processed until the first completes and it had nothing to do with this case.)  I was about to turn to the MSDN forums when I dug about in the Connect area for any bug reports concerning this.  I found my answer, but it’s somewhat unsatisfactory.

Consider the example image below; it’s a 50×40 image lifted from the VS 2008 image library that represents 2 16×16 images:

Back_Forward

We can use a TextureBrush to draw rectangles using this image; by default the result is a silly tiling of back/forward buttons, and if we move the rectangle around it will look as if we’re sliding a window over a fixed pattern, as described above.  If we specify a bounding rectangle, only the part of the image within the bounding rectangle will be used as the tile.  For example, the following paint handler would tile only the left-facing arrow from the above image:

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        Dim g As Graphics = e.Graphics
        Dim boundRect As Rectangle = New Rectangle(7, 12, 15, 15)
        Dim brush As New TextureBrush(drawImage, boundRect)
        g.FillRectangle(brush, Me.ClientRectangle)
    End Sub

If the bounding rectangle that is specified is outside the bounds of the image that is to be tiled, you get the "Out of Memory" exception.  I don’t understand why there’s no error checking done; it would be trivial to check whether dstRect is outside of the bounds of image, but for whatever reason this was left out of the method and instead you get a cryptic error message that doesn’t really tell you what is wrong.

I hope this ends up on Google and anyone who has the problem in the future finds it helpful.

To demonstrate using TextureBrush in the manner the user wanted, the application below animates a left or right arrow back and forth across the form.  I don’t believe this is the best way to do this, and I’m almost certain that one of the Graphics.DrawImage overloads would be more appropriate, but there could be a use for this I have not forseen:

Public Class Form1

    Dim drawImage As Image
    Dim boundRect As Rectangle
    Dim leftPoint As New Point(7, 12)
    Dim rightPoint As New Point(28, 12)
    Dim tileSize As New Size(15, 15)
    Dim viewRect As New Rectangle(New Point(0, 0), tileSize)
    Dim goingLeft As Boolean = False

    Public Sub New()

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

        drawImage = Image.FromFile("Back_Forward.png")

        Dim timer As New Timer()
        timer.Interval = 100
        AddHandler timer.Tick, AddressOf TimerOnTick
        timer.Start()

        boundRect = New Rectangle(rightPoint, tileSize)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        Dim g As Graphics = e.Graphics

        Dim brush As New TextureBrush(drawImage, boundRect)
        brush.TranslateTransform(viewRect.X, viewRect.Y)
        g.FillRectangle(brush, viewRect)
    End Sub

    Private Sub TimerOnTick(ByVal sender As Object, ByVal e As EventArgs)
        Dim increment As Integer

        If goingLeft Then
            increment = -10
        Else
            increment = 10
        End If

        viewRect.X += increment

        If viewRect.X <= 0 Then
            goingLeft = False
            boundRect = New Rectangle(rightPoint, tileSize)
        ElseIf viewRect.X + viewRect.Width >= Me.ClientRectangle.Width Then
            goingLeft = True
            boundRect = New Rectangle(leftPoint, tileSize)
        End If

        Me.Invalidate()
    End Sub

End Class

Managed DirectX Documentation is Installed in VS 2005 Help

Wednesday, March 26th, 2008

I’m working on some research that involves managed DirectX, and after I read a few chapters of the book Managed DirectX 9 Graphics and Game Programming (which I really recommend — more later) I wanted to look at the documentation for some of the classes that were discussed to get a more in-depth understanding. Try as I might, I couldn’t find the documentation, so I turned to Google for answers. The only reasonably decent hits I found were forum posts dated in 2005 discussing how it was still beta and there was no documentation. Useless. I had almost completed an email to Tom Miller, the author of the book, when I noticed the sentence, “I have looked in the VS 2008, VS 2005, and Windows SDK help…” was false. I had looked in VS 2008 and Windows SDK, but not VS 2005.

So, I’m continuing my series of posts inspired by things I tried to use Google to find but failed. If you want to know where the Managed DirectX documentation is in the March 2008 DirectX SDK, look in the Visual Studio 2005 help.

It’s possible I overlooked an installer option but I’m too lazy to check again.

Making a control that accepts arrow keys

Friday, February 1st, 2008

I’m working on a project with a custom control on which I want to implement the arrow keys.  I was completely mystified as to why I wasn’t getting KeyUp events when I pressed the arrow keys. I started using Reflector to hunt for where the event was raised in controls I was familiar with, and I stumbled upon the Control.IsInputKey method.  What does this method do? The remarks told me what I needed to know.

Call the IsInputKey method to determine whether the key specified by the keyData parameter is an input key that the control wants. This method is called during window message preprocessing to determine whether the specified input key should be preprocessed or sent directly to the control. If IsInputKey returns true, the specified key is sent directly to the control. If IsInputKey returns false, the specified key is preprocessed and only sent to the control if it is not consumed by the preprocessing phase. Keys that are preprocessed include the TAB, RETURN, ESCAPE, and the UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW keys.

I had no idea this existed. It turns out to get events from these keys in any control, you have to override this method and return true. Here’s an example control in C# 3.0 to demonstrate (conversion to C# 2.0 is trivial; just make a backing field for the property). When it has focus (you’ll know because it is white instead of gray), if you push an arrow key the text name of the key you pressed is displayed. If you press any other key, you’ll get “Unknown”. The documentation does seem a little off, as I’m not doing anything special for Esc or Return but they still cause the events to be raised. Still, if you set CustomInput to false, you won’t ever get an arrow key message displayed (the arrow keys seem to move the focus).

using System;
using System.Windows.Forms;
using System.Drawing;

namespace TestIsInput
{
    public class InputField : Control
    {
        public InputField()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.Selectable, true);
        }

        public bool CustomInput
        {
            get;
            set;
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if (CustomInput)
            {
                bool isInput = false;

                switch (keyData)
                {
                    case Keys.Up:
                    case Keys.Down:
                    case Keys.Left:
                    case Keys.Right:
                        isInput = true;
                        break;
                    default:
                        isInput = false;
                        break;
                }
                return isInput;
            }
            else
            {
                return base.IsInputKey(keyData);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Brush backBrush = this.Focused ? Brushes.White : Brushes.LightGray;

            e.Graphics.FillRectangle(backBrush, this.ClientRectangle);
            ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle, Border3DStyle.Sunken);
            StringFormat f = new StringFormat(StringFormatFlags.NoWrap);
            f.Alignment = StringAlignment.Center;
            f.LineAlignment = StringAlignment.Center;

            e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, (RectangleF)this.ClientRectangle, f);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            switch(e.KeyCode)
            {
                case Keys.Up :
                    this.Text = "Up";
                    break;
                case Keys.Down :
                    this.Text = "Down";
                    break;
                case Keys.Left :
                    this.Text = "Left";
                    break;
                case Keys.Right :
                    this.Text = "Right";
                    break;
                default :
                    this.Text = "Unknown";
                    break;
            }

            this.Invalidate();
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            this.Invalidate();
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            this.Invalidate();
        }
    }
}

So, if you need to accept input from one of those keys, keep in mind that you need to override IsInputKey. This was completely new to me!