28 July 2010

User Control Design Time Detection

Today, I created a wizard application in WPF/C#.  Each "page" of the wizard is a user control.  Each user control is largely autonomous, each having a pointer to a singleton instance DataManager class that persists data between the controls and parent window.  When a control is made visible, it checks the values of the DataManager class, to ensure the requisite values are available, etc.

The "page" controls are not instantiated on-demand, for reasons I will not discuss here.  When viewing the main window in the WPF designer, all of the user controls that perform data verification in their IsVisibleChanged event handler cause the WPF designer rendering to fail, although I did get several MessageBoxes containing the various validation error messages.  (At least I know those work well!)

The solution is to wrap all validation logic in an IF statement that checks to see if the control is being rendered in a designer.  It we are indeed in design time, the validation logic is stepped over.

private void UserControl_IsVisibleChanged(
    object sender, 
    DependencyPropertyChangedEventArgs e)
{
    if (DesignerProperties.GetIsInDesignMode(this)
        || this.Visibility != Visibility.Visible)
        return;

    // Validation logic here...
}

20 July 2010

Reverse TextIndent (Outdent) for FlowDocument

WPF FlowDocuments are extremely useful and format very nicely, so long as you are writing plain text.  The tag kindly provides a TextIndent attribute, to indent the first line of the paragraph.  But, what about formatting for nested code?

Margin doesn't look kindly upon inserting negative values, but TextIndent gladly accepts them.  Using a combination of a negative TextIndent and a matching, positive left margin, the Paragraph is coaxed into outdenting the first line.  This visually appears that the first line is even with the other paragraphs, and the following lines are indented.

<paragraph 
            margin="25,0,0,0"
            textindent="-25"
            FontFamily="Courier">
    First Line
    Second line
    Third Line
</paragraph>

Produces the following:

First Line
    Second Line
    Third Line

Admittedly, this works well for the first indent, but any further indentations must be handled with a bit of creativity. I will leave you to decide how to best handle that situation.

Passing ORDER BY to SQL Server Stored Procedures as a Parameter

Just about anyone who has worked with legacy code, especially when modifying the functionality of compiled assemblies, has run into this problem.  You can't modify the majority of the business code, and inheriting and overriding the code is not feasible due to impact on the system as a whole.

In the case that inspired this post, I needed to populate a simple ASP.NET 1.1 data repeater with data in a specific order, with minimal impact to the existing code.  The existing code did not contain DataView or DataAdapter ADO.NET objects.  Although I could have composed a SQL statement and switched the SqlCommand object to text query mode, I knew that is a bad idea, because:

  1. Composed text queries are begging for an injection attack, and introduce a massive security hole
  2. Stored procedures are much more efficient, because they are pre-compiled
My solution was to create a stored procedure on SQL Server, that performs the sorting for me.  All I had to do was pass the order by column to a varchar input parameter.  The trouble with using a parameter in an ORDER BY statement is that it simply doesn't work. The following script WILL NOT WORK.

SELECT * FROM Products ORDER BY @orderByColumn

The solution is to use a bit of both worlds.  Pass the column name as a parameter to the stored procedure, and then compose it into a varchar parameter.  Finally, execute the parameter containing the composite query.

DECLARE @orderByColumn    varchar(50) -- (THE INPUT PARAMETER)
        @sql              varchar(255)

-- Be sure to include a space after ORDER BY
SET @sql = 'SELECT * FROM Products ORDER BY ' + @orderByColumn

EXEC(@sql)

Receiving data in the order you need saves you the trouble of adding a DataTable, DataView, DataAdapter, and possibly other objects, and allows you to bind the command results directly to a data repeater.

15 July 2010

Format Document - Run Frustrations

I have been using FlowDocument, to create C# training materials, because it looks nice as a presentation, but can include interactive controls embedded in BlockUIContainer.  It looks great, the interactive portions are perfect for the students, and everything is peaches and cream. That is, until I press Ctrl+E Ctrl+D to clean up the XAML using Visual Studio's Format Document tool.

I am using , etc., to reflect the default text colors seen in Visual Studio.  Format Document insists on inserting a white space following every tag.  This does a great job of reducing readability of things like array declarations:

This:  string[] s = new string[]{...}
Becomes: string [] s = new string []{...}

I know one line of code doesn't look like a big deal, but when you have something more complex, such as the following tuple declaration, everything gets jumbled together in a hurry.  This is not good for my students, who are unfamiliar with syntax and proper code formatting.

Tuple &lt; int , string , double , Tuple &lt; string [], int []> > t =
    new Tuple &lt; int , string , double , Tuple &lt; string [], int []> >(
        13, "Friday the 13th", 65.0185d,
        Tuple .Create&lt; string [], int []>(
            new string [] {"Jason", "Freddy", "Michael"},
            new int []{13, 14, 152}
        )
    );

If you have any solutions, please post them here, and I will include it in this post!

01 July 2010

How To Find USB Device Vendor and Product ID Codes

Creating an interface for the Missile Launcher USB device (manufactured by Dream Cheeky, available at ThinkGeek.com $25 USD) is a great way to entice programming students to develop their UI skills.  (I will post links to the .NET 4.0 device command API, when I get it working.)  Because similar devices have been made by several manufacturers, since about 2004 to present, most of the USB human interface device (HID) interface code available on the Web was targeted at older models from other vendors.

When interfacing with a USB HID, you must first locate the device -- the HID could be plugged into any USB port, or could have been moved to another port during use.  We locate the correct HID by iterating through attached USB devices, until we find one that matches the Vendor ID (VID) and Product ID (PID) in which we are interested.

How do I find the USB HID Vendor ID and Product ID?
  1. Open the Windows Device Manager
  2. Expand the Human Interface Devices node
  3. Double-click the device of interest -- the USB Human Interface Device Properties window appears
  4. Click the Details tab
  5. In the Property drop-down box, select Hardware Ids
The USB HID VID and PID are displayed.  My Cheeky USB Missile Launcher displays:
USB\VID_0A81&PID_0701&REV_0001
USB\VID_0A81&PID_0701

The hexadecimal Vendor ID is highlighted in red, Product ID in green, and product revision number in blue.  Other details obtained through a USB monitoring application:

DEVICE DESCRIPTOR
  • USB Version 1.10
  • Device class; 0x0 (defined at interface level)
  • Device subclass: 0x0 (Unknown)
  • Device protocol: 0x0 (Unknown)
  • Control pipe max size: 8 bytes
  • Vendor ID: 0xA81 (Chesen Electronics Corp.)
  • Product ID: 0x701 (USB Missile Launcher)
  • Product version: 0.1
  • Manufacturer: Dream Link
  • Product: USB Missile Launcher v1.0
  • Serial number: not specified
  • Configurations: 1
CONFIGURATION DESCRIPTOR
  • Number of interfaces: 1
  • Configuration value: 0x1
  • Attributes: Remote wakeup, Bus powered
  • Max power: 100 mA
INTERFACE DESCRIPTORL0, ALTERNATE SETTING: 0
  • Number of endpoints: 1
  • Interface class: 0x3 (Human Interface Device)
  • Interface subclass: 0x0 (None)
  • Interface protocol: 0x0 (None)
  • Endpont address 0x1, Input, Interrupt, max packet size: 1 bytes, update interval: 16 1-millisecond frames

Masking Drag & Drop Hit Test in WPF

While creating WPF demonstration code, I decided to use the classic DoDrag() method, to perform drag and drop functions.  When dropping objects on both a Canvas and Border object, DoDrag() unexpectedly and consistently returned Effects.None.

The solution was easy: the Canvas and Border objects had no Fill.  DoDrag's hit test was failing, because there was no physical rendering of the Canvas or Border at the mouse position!  The object must be filled, to return a positive hit test.  Using the "Transparent" brush  or a solid color with 00 alpha works, too.

Note, borders are considered filled areas, and return a positive hit.  You can use this to your advantage, but I'm not sure why you would ever drag and drop something on a border, unless it is very thick.  You could use a border as a drop mask, to return different Effects result, over certain areas of an object.