This is the archived version of Roland Weigelt's weblog that ran from 2003 to 2023 at weblogs.asp.net

Contents tagged with VS.Net 2003

  • When your WinForms UserControl drives you nuts

    WinForms user controls are that kind of technology that seems to be very easy at first, works pretty well for quite a while and then BAM! explodes in your face. I spent some hours today solving problems with a control that was working fine in the running application, but just didn't like the IDE's designer anymore, throwing exceptions all over the place. Here are some tips based on what I learned today searching Google's Usenet archive:

    1. Don't take things for granted
    Just because you can create a simple user control (e.g. combining a text field and a buttons) without reading any documentation, doesn't mean that knowledge about what is actually happening in design mode isn't necessary. Always keep in mind that there's some code running even though your program is not started. When a form or a control is displayed in the IDE in designer view, its (default) constructor is called which in turn calls InitializeComponent(). And this is where the trouble usually starts when this form/control contains a badly written user control.

    2. Take care of your control's public properties
    If you don't add any attributes to a read/write property (huh, what attributes?), two things happen:

    • The property is shown in the "properties" window in the IDE.
    • The initial value of the property is explicitly set in the InitializeComponent() method.

    Hiding properties that don't make sense to be edited at design time is a good idea even if you don't intend to write a commercial-quality, bullet-proof control ready to be shipped to thousands of developers. It's simply a matter of good style and it's done with very little effort using the Browsable attribute:

    [ Browsable(false) ]
    public string MyProperty
    {
    ...
    }

    The second point can be quite a shocker in some cases, because it means that

    • the property is read when the control is placed on a form (quick experiment: throw an exception in the "getter" and see what happens in the designer).
    • the property is set at the time the control is created, not when it is actually added to the form.

    As long as your control only gets or sets the value of a private member variable, things are fine. But if e.g. reading the property triggers some actions that require the control to be completely initialized, you can get into trouble (remember the "Add any initialization after the InitializeComponent call" comment in the constructor?). And setting the property "too early" can be a problem e.g. if your property's setter contains code that performs some kind of automatic update.

    This behavior of the designer can be avoided by using the DefaultValue attribute:

    private string m_strMyText="";
    ...
    [ DefaultValue("") ]
    public string MyText
    {
    ...
    }

    The MyText property will not be set to "" in the InitializeComponent() method (which would be the case without the attribute).

    While you're at it, consider adding the Description and Category attributes as well. Adds a nice touch.

    3. Don't rely on this.DesignMode too much
    The inherited DesignMode property of your user control is an easy way to find out whether the control's code is running in the designer. Two problems:

    • You can't use it in the control's constructor. At that point, the control's "Site" (which provides the actual value of DesignMode) hasn't been set, thus DesignMode is always false in the constructor.
    • "Running in the designer" is a matter of point of view:
      • When editing a control MyControl in the designer, MyControl is obviously in "design mode".
      • When using MyControl on a form being edited in the designer, MyControl is still in "design mode".
      • But if MyControl is placed on MyOtherControl, and MyOtherControl is placed on a form, MyControl is no longer in design mode (but MyOtherControl still is)
      Strange? Not really, if you think about it (if... I didn't)

    4. If you just don't know what to do: debug!
    Open the project's properties dialog, set the debug mode to "program", apply, set "start application" to your VS.Net IDE ("devenv.exe" in the "Common7\IDE" directory). Set some breakpoints in your code (e.g. in property getters/setters) and start debugging. A new IDE will open (empty), you load the project and then open the control in designer view. Cool stuff.

  • Intellisense (Wouldn't it be cool, part 2)

    Everytime I use intellisense on a class with a lot of members (e.g. a class derived from System.Windows.Forms.Form) I get overwhelmed by the huge dropdown list if I'm looking for a member I don't know the exact name of.

    So wouldn't it be cool if instead of this...

    ... VS.Net 200x would offer something like this:

    Hiding inherited members...

    ...or showing only certain kinds of members (e.g. only events)...

    ...would be really nice features (and of course something like this is still on my wish list).

  • C# Project File Quote Madness

    When looking at a ".csproj" file, a developer's first reaction is "cool, it's XML, I can read and write it". It may be missing the "<?xml ... ?>" processing instruction, but who cares - you can Load() it into a System.Xml.XmlDocument, so everything's fine.

    As long as you intend to only read from the ".csproj" file, that's the end of the story. But if you actually want to write to a ".csproj" file (not such a good idea anyway), be prepared for an unpleasant surprise. If you Save() the XmlDocument to a file, and one of the of the gazillion attributes contains a quote, the saved project file is virtually worthless.

    An example: Say you put echo "Hello World!" into your post build step. If you load the resulting project file into an XmlDocument and save it back to a file, you'll get something like this:

    ... PostBuildEvent="echo &quot;Hello World!&quot;" ...

    Looks ok, right? Not to VS.Net. Because VS.Net expects this:

    ... PostBuildEvent = 'echo "Hello World!"' ...

    So when you load the project file back into VS.Net, the next time the post build step is performed, VS.Net tries to execute "echo &quot;Hello World&quot;", which of course fails with a lot of noise (remember that "&" has a special meaning on the command line). XmlDocument is smart enough to read and understand the original ".csproj" file (the single quotes are valid XML after all), but when saving to a file, it uses double quotes to surround the attribute value and escapes quote characters inside the value if necessary.

    The fact that VS.Net is not able to read the file makes me wonder what kind of XML parser is used inside VS.Net - did somebody have the stupid idea to write one from scratch?

    Before somebody tells me that I'm not supposed to manipulate the ".csproj" file, here's a way to make VS.Net look stupid without any external help:

    Put echo "Hello 'World'!" into the post build step. VS.Net writes the following into the project file:

    ... PostBuildEvent = "echo &quot;Hello 'World'!&quot;" ...

    Oops... attribute value surrounded by double quotes, escaped quotes inside... that means trouble. If you build the project immediately after setting the post build step, "Hello 'World'!" appears in the build output as expected. But if you (re)load the project, BLAM! "Post-Build Event failed".

  • One Reason why I use Hungarian Notation

    While I adjusted my coding style for "everything publicly visible" to match the Microsoft guidelines, I still use Hungarian Notation internally (in a simplified form). One can argue whether or not it increases the readability of code, resulting in long discussions about the pros and cons.

    One feature of HN I really love (and just don't want to give up) is how well it works in conjunction with Intellisense.

    Example 1: Member variables ("m_" prefix):

    Example 2: Controls ("c_" prefix):

  • VS.Net and the FontSize Parameter

    Roy wrote about the /fs (FontSize) command line parameter of devenv.exe and using it for presentations: cool feature I didn't know about. Maybe I should have called "devenv.exe /?" before, but it just didn't came to my mind at all, not expecting any interesting parameters. Anyway, first thing I did was to try a large number (/fs 20), but I also tried "/fs 7" which is smaller than the default (/fs 8).

    Font size 7 is kind of interesting. While I use 1280x1024 on a 19" screen both at home and at work, and feeling ok with it (more would be nice, but it's ok), I'm sometimes forced to work with 1024x768 on another computer connected to the same 19" monitor. This is where every pixel in VS.Net counts, especially because you cannot make every window fly-in/out without going mad after some time.

    With "/fs 7" dialogs and helper windows are a bit smaller, while the text editor font remains unchanged. The following screenshots show the solution explorer:

    Default:

    With /fs 7:

    As you can see, you can save a few pixels - with 1280x1024 not really a reason to switch. But if you're in the strange situation of viewing low resolution on a big screen, "/fs 7" is worth giving a try.

  • Wouldn't it be cool...?

    This is what XML comments look like on my computer today:

    Wouldn't it be cool if someday (in VS.Net 200x?) we could have something like this:

    It's kind of frustrating to see computers rendering 3D worlds with 100s of frames/sec, but source code editors advancing only in very small steps...

  • Better Keyboard Support for #region ... #endregion

    Of all the elements that can be collapsed in the VS.Net editor (namespaces, classes, methods, comments, ...), only regions are of actual importance for my everyday work. Here are three macros that are quite helpful for expanding/collapsing regions using the keyboard:

    Macro Purpose Keys (example)
    ExpandAllRegions expands all regions in the current document Ctrl-Shift-'+'
    CollapseAllRegions collapses all regions in the current document Ctrl-Shift-'-'
    ToggleParentRegion expands/collapses the region the cursor is currently in Ctrl-'+' and Ctrl-'-'

    Installation:

     

    • Open the Macros IDE (Tools - Macros - Macros IDE)
    • Create a new module, replace the code in the created file with the posted source code, save file
    • Assign keys in VS.Net (Tools - Options - Keyboard).

    Tip: type "Region" in the input field under "show commands containing" to limit the list of shown commands (don't laugh, it's always amazing how many people don't know about this rather obvious feature...)

    Known Issues

    • The code runs fine in VS.Net 2003; in VS.Net 2002 I had strange problems with "Selection.FindText" not working reliably.
    • ToggleParentRegion is "good enough", but not perfect; e.g. when the cursor is at the end of a line containing a collapsed region, it won't expand the region, but move the cursor to the start of the line (where a second invocation will work fine).
    • (Update 14.08.2003): The macro ExpandAllRegions does not work in VB.Net (see comments below). The macro ToggleParentRegion didn't work correctly, either, but could be fixed (see "Updated" comment in code).

    Source Code

    Imports EnvDTE
    Imports System.Diagnostics
    ' Macros for improving keyboard support for "#region ... #endregion"
    Public Module RegionTools
    ' Toggles the current region surrounding the cursor
    Sub ToggleParentRegion()
    Dim objSelection As TextSelection
    objSelection = DTE.ActiveDocument.Selection
    Dim objPosition As EnvDTE.TextPoint
    objPosition = objSelection.AnchorPoint
    objSelection.SelectLine()
    If (InStr(objSelection.Text.ToLower(), "#region") <> 0) Then ' Updated 14.08.2003
    objSelection.MoveToPoint(objPosition)
    DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
    ElseIf (objSelection.FindText("#region", vsFindOptions.vsFindOptionsBackwards)) Then
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
    DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
    Else
    objSelection.MoveToPoint(objPosition)
    End If
    End Sub

    ' Expands all regions in the current document
    Sub ExpandAllRegions()
    DTE.ExecuteCommand("Edit.StopOutlining")
    DTE.ExecuteCommand("Edit.StartAutomaticOutlining")
    End Sub

    ' Collapses all regions in the current document
    Sub CollapseAllRegions()
    ExpandAllRegions()
    Dim objSelection As TextSelection
    objSelection = DTE.ActiveDocument.Selection
    objSelection.StartOfDocument()
    While (objSelection.FindText("#region"))
    objSelection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
    DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
    objSelection.StartOfDocument()
    End While
    DTE.ActiveDocument.Selection.StartOfDocument()
    End Sub
    End Module
  • Personal VS.Net Editor Color/Font Settings

    Roy writes "I was fiddling with the color settings for visual studio, and was trying to get a less eye-straining environment for coding in". What he came up with is definitely a matter of personal taste ;-)

     

    Speaking of personal taste, here's a screenshot of my settings:

    Most important for me (and my eyes) is the use of a proportional-width font. Verdana 8pt is the font I use for virtually every editor I use. Unlike e.g. Arial, you can call it a "developer's font", because you can distinguish the small "L" and the large "i".

    Switching to a proportional font wasn't easy for me at first, because I had to break with old habits; most of them (e.g. aligning end-of-line comments running over several lines) were a waste of time, anyway.