Sunday, January 29, 2006

I just finished reading an article from Hacknot that describes the predicament that developers are in while attempting to define a software product's requirements as specified by the user. Here is a quote from the article that I find quite telling:

In their efforts to be "user focused", guided by simplistic notions of "usability", many managers and programmers uncritically accept whatever users tell them as a mandate. "The customer is always right" makes a nice slogan but a poor substitute for critical thought. If you want to deliver a product that is genuinely useful, it is important to moderate the user feedback you receive with your own knowledge of usability principles, and to seek independent confirmation of the information they relate. For it is a fact seldom acknowledged in the text books that users are frequently uninformed, mistaken or deliberately deceptive.

I work in an organization which is highly user focused, as opposed to product focused. Essentially, whatever the customer says goes.

Here is a little background: My employer has been relatively slow in adopting new technologies. A large portion of the software that runs is what might be called "legacy" code (i.e. COBOL, JCL, and even Assembly); however, there is still new development being written in that environment. Thankfully, much of the front-end has been moved to an intranet base. Quite recently, there has even been a push to Windows Forms development with .NET.

The problems that the users have is that they do their business work in three different environments at a bare minimum: green screens, intranet webpages, and Windows applications. When my company first began the move to intranet pages, the user requirements would basically be, "make it look like what we have." They had no idea of what the capabilities were with webpages. The first few webpages all looked just like their green screen counterparts, except that they were black text on a white background, instead of green text on a black background.

Today, with some of our new development in .NET, we're running into the same problem again. The users know webpages and their capabilities, but not those of Windows applications. There is a fundamental misunderstanding about what these new applications are. They want a button for everything, when a toolbar or menustrip might be more appropriate. I've really been encouraging my users to look at examples like Office applications for ideas while thinking about what they want the new software to do. Some might argue that Office applications aren't the best example, but the truth is that Office is one application that the users really know.

As software developers, I really think that, to be user focused, you can't always give the user exactly what they asked for. Alternatives can, and often are, much better in the end. The difficult part is communicating that. Is this a common scenario for others? (I know it is!)

posted on Sunday, January 29, 2006 7:53:16 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, January 26, 2006

Check this out.

"The A to Z of Programmer Predilictions" is a funny little article that categorizes programmers into various stereotypes, most of which are surprisingly accurate. As I read through the descriptions, I started spotting people in my organization. Though I (and everyone else) can probably fit into a variety of different personas described there, I probably am most like "Zealous Zack." I DO download all of the betas (even at work!) and, when someone blogs about the "Next New Thing," I've got the download started before I've finished reading the post about it.

Speaking of, I need to download the newest WinFX bits...

(via Digg)

posted on Thursday, January 26, 2006 7:41:44 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Tuesday, January 24, 2006

I've been experimenting with the FlowLayoutPanel. There are a few catches to using it that can cause some confusion. In my case, the confusion came from anchoring and/or docking. I was dynamically adding some listviews to my panel to display results of data. There would be one to many listviews depending on the type of report the user requested. Having come from developing in ASP, it is still my first inclination to just create a table for each section and add it. The flow of the HTML would handle the rest. I figured I could get a FlowLayoutPanel and just add controls as needed and get similar functionality.

That was true to a degree; however, anchoring doesn't perform as you might think. If you anchor to the left and right after setting the width, if there is only one control in the panel, it will drop down to a zero width. The actual behavior is that, if you anchor to the left and right, it will assume the width of the widest control already on the FlowLayoutPanel. The same is true if you set the Dock property to Fill. Check out this article on MSDN to see what I mean. The behavior also depends on the FlowDirection property.

Everything works great if your panel isn't going to resize itself. You can just set the first control's width to the width of the panel minus a few pixels and Dock.Fill everything else. If you want it to resize as you resize the form, though, it is looking like you'll have to handle some layout events.

Here's some code to get you started:

Public Class Form1

    Private WithEvents flowPanel As New FlowLayoutPanel()

 

    Public Sub New()

        ' This call is required by the Windows Form Designer.

        InitializeComponent()

 

        ' Add any initialization after the InitializeComponent() call.

 

        ' DON'T FORGET THESE LINES!

        flowPanel.Dock = DockStyle.Fill

        flowPanel.FlowDirection = FlowDirection.TopDown

 

        Me.Controls.Add(flowPanel)

 

        AddListView()

        AddListView()

    End Sub

 

    Private Sub AddListView()

        Dim totalList As New ListView()

        totalList.View = View.Details

 

        ' If first control, then set a width; otherwise, Dock.Fill it.

        If Me.flowPanel.Controls.Count > 0 Then

            totalList.Dock = DockStyle.Fill

        Else

            totalList.Width = Me.flowPanel.Width - 10

        End If

 

        totalList.Columns.AddRange(New ColumnHeader() _

            {GetColumnHeader("Col 1"), _

            GetColumnHeader("Col 2"), _

            GetColumnHeader("Col 3")})

 

        Dim item As ListViewItem = totalList.Items.Add("Row 1")

        item.SubItems.Add("1234.00")

        item.SubItems.Add("5678.00")

 

        item = totalList.Items.Add("Row 1")

        item.SubItems.Add("1234.00")

        item.SubItems.Add("5678.00")

 

        item = totalList.Items.Add("Row 1")

        item.SubItems.Add("1234.00")

        item.SubItems.Add("5678.00")

 

        Me.flowPanel.Controls.Add(totalList)

    End Sub

 

    Private Function GetColumnHeader(ByVal text As String) As ColumnHeader

        Dim col As New ColumnHeader()

        col.Text = text

        Return col

    End Function

 

    Private Sub flow_Layout(ByVal sender As Object, _

        ByVal e As System.Windows.Forms.LayoutEventArgs) _

        Handles flowPanel.Layout

 

        Dim flowLayout As FlowLayoutPanel = DirectCast(sender, FlowLayoutPanel)

        If flowLayout.Controls.Count > 0 Then

            flowLayout.Controls(0).Width = flowLayout.Width - 10

        End If

    End Sub

End Class

Here's MSDN another article that gives some basic information on the FlowLayoutPanel.

posted on Tuesday, January 24, 2006 8:09:23 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Saturday, January 21, 2006
I know many people have seen the excellent Cropper tool for grabbing screenshots. I found another tool very similar to it by the maker of TaskSwitchXP called WinSnap. It gives a few additional features like adding a dropshadow or rotating the image. The good thing is that it is still a very small application. Here is a screenshot I pulled with it:



Cool shadow effect, huh? I'm giving it a try right now. It seems to work well for me so far.

posted on Saturday, January 21, 2006 12:39:15 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, January 20, 2006
I discovered today that the new FtpWebRequest/FtpWebResponse classes might not be the best options for connecting to FTP servers, at least unless you're in control of the FTP server. The classes make some assumptions about the remote FTP server, like the server's file system.

I was working on converting an application that sends files to a customer's FTP server and I thought I would use the FtpWebRequest and FtpWebResponse classes. The problem is that the customer's FTP server is running on VMS! I know, that isn't very common, but the file system in VMS is NOT like Windows or Unix. Folders aren't separated by "\" or "/" so when a change directory (CWD) command is sent, you can't assume that folders are delimited by one of those characters! When you try to list the files in the directory, it sends a CWD command to change directory to the current directory (why, I don't know) with a "/" at the end. Predictably, I get a 550 error back with a "file specification syntax error."

I haven't found a workaround other than coding it myself. If anyone has any suggestions, let me know.


NOTE: I used Ethereal to observe what was being sent and received. It is a great utility for watching network traffic.
posted on Friday, January 20, 2006 4:03:07 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
Here is a little gotcha with some changes to the way that .NET 2.0 handles COM interoperability. You probably won't run into this problem unless you're converting a project from 1.1 to 2.0.

We had a little command line application that took did some background processing for us that was written in 1.1. We moved it over to 2.0 because we wanted to take advantage of the new FTP capabilities in the 2.0 framework (see FtpWebRequest/FtpWebResponse). If you add a reference to a COM object in .NET 1.1, the typical object that you'll create on the managed side will have a "Class" suffix (i.e. MyCoolComObjectClass as opposed to MyCoolComObject). If you convert to 2.0, it doesn't change that code either...

However, you might see an exception like this if you start using it:

"System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to class type 'ComLibrary.MyCoolComObjectClass'. COM components that enter the CLR and do not support IProvideClassInfo or that do not have any interop assembly registered will be wrapped in the __ComObject type. Instances of this type cannot be cast to any other class; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface."

What do you do? Take the class off the end of your COM object (MyCoolComObjectClass -> MyCoolComObject).

That's it.

AUTOMAGICAL!

(Thanks to this forum posting for the info!)

posted on Friday, January 20, 2006 10:57:24 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Wednesday, January 18, 2006
I was curious if anyone knew if Visual Studio 2005 had the capability to highlight or color "User Types" (i.e. classes you write yourself) while coding in Visual Basic. I know that C# has this functionality built in, but it doesn't seem like VB pays any heed to those settings. Here's a screenshot of the specific setting in Visual Studio:



If anyone knows anything about getting that working for VB, I'd appreciate it.

posted on Wednesday, January 18, 2006 1:04:46 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, January 13, 2006

Do you like C-based languages like C, C++, Java, and C#? Or do you like BASIC instead? It doesn't matter what your answer is, though, because the two links below are hilarious either way.

History of the BASIC family of languages
History of the C family of languages

(via Rockford Lhotka)

posted on Friday, January 13, 2006 2:54:49 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

Continuing on my thoughts from yesterday regarding Windows Forms validation, I first began using Michael Weinhardt's validation library on MSDN after doing a project conversion from 1.1 to 2.0, but there were a few things that I personally wanted to do that weren't currently supported, like validation on controls like the DateTimePicker. It would have been quite easy to add support, but I began developing a picture in my mind of a slightly different approach to validation. His solution was very designer oriented, but I wanted a more code-centric approach.

What I came up with was basically a GroupValidator class that took a control and a delegate to a provided validation function. The class took care of calling the validation function by hooking into the Validating event and then setting the appropriate error message. It added the control and delegate to a collection which could then be looped through so that you could check to see if the entire group was valid. It is pretty basic, but it works quite well so far. I'd like to expand it a little bit to add more automation, but it gets the job done right now.

Here's an example of usage:

Public Class Form1

    Private _validator As New GroupValidator()

 

    Public Sub New()

        ' This call is required by the Windows Form Designer.

        InitializeComponent()

 

        ' Add any initialization after the InitializeComponent() call.

        _validator.AddControl(Me.TextBox1, _

            AddressOf IsTextValid, "Text not valid")

        _validator.AddControl(Me.TextBox2, _

            AddressOf IsTextValid, "Test not valid2")

        _validator.AddControl(Me.TextBox3, _

            AddressOf IsTextValid, "Enter something")

        _validator.AddControl(Me.TextBox4, _

            AddressOf IsTextValid, "Please")

    End Sub

 

    Private Function IsTextValid(ByVal c As Control) As Boolean

        If c.Text.Length > 0 Then

            Return True

        Else

            Return False

        End If

    End Function

 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        MessageBox.Show(_validator.IsValid.ToString)

    End Sub

End Class

It is pretty simple as you can see. If you're interested in the code, drop a line and I'll email it or post it.

posted on Friday, January 13, 2006 8:53:27 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, January 12, 2006

I've been doing some research on best practices for validation in Windows Forms, and I came across a nice set of MSDN articles by Michael Weinhardt where he shows how to create a validation framework that is very similar to the validators found in ASP.NET (i.e. required field validator, regular expression validator, etc). It includes container and form validators that basically manage the control validators already on the form. The link to the articles is to the third article in the series, but the download contains all of the source. His code was apparently incorporated into the Genghis framework as well.

I'm interested in something similar for 2.0, but I haven't found anything. I did see an older post from Chris Sells about moving Genghis over to 2.0, but I haven't seen any updates on that. I'm considering doing it myself, but if anyone has any better ideas, I'm open.

posted on Thursday, January 12, 2006 2:07:18 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

After some severe frustration research, I was able to discover how to repro the crash in Visual Studio 2005 yesterday. It is actually quite simple to do. You need a new VB.NET project and two With statements, one nested within the other. That's it. Then you click on the second With object.

Here, I'll show you how!

Public Class Form1

    Public Shared Sub Main()

        Application.Run(New Form1)

    End Sub

 

    Public Sub New()

        ' This call is required by the Windows Form Designer.

        InitializeComponent()

 

        ' Add any initialization after the InitializeComponent() call.

        Dim p As New Person()

        With p

            ' CRASHHERE: If you want to crash, take your mouse and

            ' click inside .StreetAddress.... BOOM!

            With .StreetAddress

                .Street = "123 BOOM"

            End With

        End With

    End Sub

 

    Public Class Person

        Private _streetAddress As New Address

        Public ReadOnly Property StreetAddress() As Address

            Get

                Return _streetAddress

            End Get

        End Property

    End Class

 

    Public Class Address

        Private _street As String

        Public Property Street() As String

            Get

                Return _street

            End Get

            Set(ByVal value As String)

                _street = value

            End Set

        End Property

    End Class

End Class

 

There, that was easy, huh? (In case you're not seeing it, look for the CRASHHERE note.)

Kids, don't try this at home.

Chances are, it will work for you anyway. I just checked with one of my co-workers and it works just fine for him. Great.

posted on Thursday, January 12, 2006 8:07:54 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Wednesday, January 11, 2006

5+ crashes in a row is a little much.

And I wasn't even in the designer. And yes, I rebooted. It still happened.

I don't know exactly what I did to fix it, though. Let's just say heavy use of the save button and having another instance of devenv up and attached to the other process. Just in case I needed to copy and paste a call stack somewhere. Of course, that's when it began working.

Oh well.

NOTE: Don't take this the wrong way. I still love VS2005 as an IDE. However, it still has quite a few issues. The most annoying of which is the flickering while in the designer. And no, I never got that one up in the debugger either.

UPDATE: For more information on this, see my new post here.

posted on Wednesday, January 11, 2006 2:50:25 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Monday, January 09, 2006

How about FOUR?!?

Dell just announced at CES 2006 their XPS Renegade gaming system that utilizes FOUR GPUs with a 4.26 GHz processor. Here is the link at IGN and here is the direct link to Dell's Flash site.

That's just insane.

And for just over $2,000, you can purchase their 30-inch monitor, too.

Can we say overkill?

posted on Monday, January 09, 2006 1:06:01 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Saturday, January 07, 2006

This may seem a little weird, but I was checking my webstats for the blog and I saw that one of the Googlebots came across it. That prompted curiosity on my part to see if I could find this page by searching for it. I didn't find my site yet, but I sure found some other stuff.

I got into the web around 1996 or 1997, right when the internet was becoming more mainstream. I remember using Netscape Composer to build websites back then (I wasn't cool enough to be one of those guys who coded their entire site in Notepad) and the coolest webpages had billions of animated GIFs, a tiled background, and a guestbook. Most of the search results I found were my signing of other people's guestbooks in hopes of having them come and sign MY guestbook. 

The other cool thing to do was to display all of the "awards" that your site had won. They were basically just cooked up images that ANYONE could create. All anyone had to do to get the award was ask for it and then link back to your site. It was all pretty ridiculous, but then again, no one knew better. I actually found one of my awards still being displayed on some site that I have no recollection of ever visiting.

Wow, memories.

posted on Saturday, January 07, 2006 2:17:33 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, January 05, 2006

My post earlier today referred to the FileHelpers library by Marcos Meli.

Wow, do I wish I had known about this earlier. This thing is great! With a few easy attributes, you can use a business object as a layout for fixed-length files! Okay, I guess an example would help. Here's some VB code I whipped up to test this library out.

Imports FileHelpers

Public Class MainForm
    Private Sub MainForm_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Me.Load

        Dim engine As New FileHelperEngine(GetType(Record))

        Dim records() As Record = _
            DirectCast(engine.ReadString("123456789  2005-01-01"), _
            Record())

        Debugger.Break()

        Dim res As String = engine.WriteString(records)

        Debugger.Break()
    End Sub
End
Class

<FixedLengthRecord()> _
Public
Class Record
    <FieldFixedLength(11)> _
    Public BillNumber As String

    <FieldFixedLength(10), _
    FieldConverter(ConverterKind.Date, "yyyy-MM-dd")> _
    Public BillDate As DateTime
End
Class

At each Debugger.Break line, I checked my values and this thing is great. records(0).BillNumber is "123456789  " and records(0).BillDate is a converted DateTime set to 1/1/2005.

If you deal with fixed-length or delimited files on a regular basis, this library might be a great option for you.

Thanks Marcos!

UPDATE: Version 1.3.1 was released recently, so I have updated my example code to use the new ReadString and WriteString methods.

posted on Thursday, January 05, 2006 1:53:42 PM (Central Standard Time, UTC-06:00)  #    Comments [1]

By way of Larkware, I came across the FileHelpers library written by Marcos Meli. It looks like a great solution for dealing with delimited or fixed length file formats. The majority of the output formats from our backend systems at work are fixed length file formats, which can be a pain to parse. I had actually written a library to parse them and reference portions of each line by name (sort of a named index to portions of a line), but this looks like a much cleaner approach. I'll dig into this some more and post my thoughts on it.

posted on Thursday, January 05, 2006 1:21:07 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Wednesday, January 04, 2006

Over a year ago, I built a new PC from the ground up. This wasn't my first PC to build mind you. I hand-picked all of the parts and had a great time putting it together. If you've ever built a PC, you know the feeling of hitting the power button after hooking the final jumpers and hearing that familiar whirr as the PC boots up. Boy, is it a scare when it doesn't do that! That's what happened with this PC... absolutely nothing. Long story short, I got the PC to come on finally, but everytime I shut it down, it wouldn't come back on without my resetting the CMOS by using a jumper on the motherboard. I tried replacing the CMOS battery, changing out the memory... everything. I finally found some people online in a hardware forum with a similar problem. Their solution? RMA. *sigh* What was MY solution? Get a Dell. Okay, okay, I'll admit it. I chickened out. I just didn't want to get burned again. I'm still using that Dell, too.

Flash forward to the present. I wanted to build a PC for my wife and I still had that motherboard sitting around. I ended up RMA'ing it and I got a replacement back. I hooked it all up a few nights ago and, miraculously, it worked! I had a few strange lock-ups, but they seemed to stop. Tonight, it won't turn on. I took the case off and reset the CMOS to see if anything would happen. Something did happen, but only for a second. I heard the whirr for about a second and then silence. I would guess a short, but I don't know. Building a PC is a lot of fun, but it can be great source of frustration when it doesn't work.

My recommendation for building PC's (ha, like you'll listen to me now) is do a Google search on some of the hardware you're interested in, but put words in there like "problem" or "this piece of crap won't work" or something. If you find a lot of results, check them. It can save you some stress later on.

posted on Wednesday, January 04, 2006 10:06:58 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

I ran into that error today while attempting to install Infragistics NetAdvantage 2005. I'm not exactly sure about why the error occurred, but I was able to find a fix here (via this MSDN forum thread). Some of the other solutions appear to be reinstalling VS2005, but luckily, the registry fixes from Chetan's blog worked fine.

posted on Wednesday, January 04, 2006 4:47:57 PM (Central Standard Time, UTC-06:00)  #    Comments [0]

Has anyone noticed a new menu item under the Build menu in Visual Studio 2005? There is a Clean Solution item that will remove all of the compiled binaries that live under your bin folder. It is relatively similar to Clean Sources Plus, except that it keeps the folders.

Note: I'm not sure if it shows up in every VS configuration. I'm using the default view (see Tools -> Import and Export Settings).

posted on Wednesday, January 04, 2006 1:11:43 PM (Central Standard Time, UTC-06:00)  #    Comments [1]
 Tuesday, January 03, 2006

I ran into a strange situation this afternoon with visual inheritance and threading. I've created a master form for my area at work so that we can have a common menu in all of our applications. It also provides a public readonly property that has information about the currently logged on user. The user's information is pulled using a BackgroundWorker so that the form can display promptly.

Here's the situation, a few days ago, I added a ManualResetEvent so that, if access to the User property was attempted before the thread completed, a NullReferenceException wouldn't occur. I had already put code in so that the initial loading of the user's information would not fire in design mode. The problem that began happening was that, every time I tried to open a form that inherited from my master form, Visual Studio would lock up. I pulled up a second devenv to see where the hanging was occurring and, sure enough, it was sitting on my ManualResetEvent inside my user property. Why in the world was the designer accessing my user property?

The more astute (or experienced) of you will realize that I hadn't marked my property with that oh so important System.ComponentModel.Browsable(False) attribute! The designer was accessing the property so that it could display it in the Properties Window!

Let me be an example of what perils can occur when you play with visual inheritance and the designer!