Flylib.com

Books Software

 
 
 

Developer Secrets


Developer Secrets

So, you want to get your hands on all the juicy secrets, huh? Then step this way, as we prepare to unveil the following gems for creating great Windows forms applications:

  • Making Your Form Transparent

  • Who Stole the ToolTips?

  • Tricks of the Trade: Resizing Made Easy

  • Five Steps to Split Panels, Explorer Style

  • Highlighting Errors with the ErrorProvider

  • Learning the LinkLabel Control

  • Customizing the MonthCalendar Control

  • Creating Your Own Number-Only Textbox

  • Displaying Animated GIFs Without the Browser Control

  • The Two Methods of Changing Tab Order

  • Secrets of Working with the System Tray

  • Save User Time: Add Autocomplete to Combo Boxes

  • The Power of Command-Line Parameters

  • How to Reset a Form

  • How to Snap the Cursor to a Button

  • Capturing the Screen, Quick and Easy

  • Stunning Navigation Bars with a Little-Known Freebie

  • Seven Steps to Taking Advantage of Windows XP Themes

  • The .NET Way of Checking for Previous Instances

  • Converting RTF to HTML

  • Drag and Drop from Windows Explorer

  • Dialog Boxes: What Did the User Click?

  • Text Printing Class That Works!

  • The Secret Rebirth of .PrintForm

  • The Facts on Visual Inheritance

  • Looking at Windows, Performance Counters, and More

  • Protecting Your Code with Obfuscation

  • Best of All Worlds: Creating an Ultra-Thin Client

And that s just the beginning. Later in this book, after we ve covered other project types such as ASP.NET Web sites, we ll move onto database secrets, a hidden .NET language, special file manipulation code, and much more ”all of which you can easily use straight away in your Visual Basic applications.

Making Your Form Transparent

You can give your Windows form a great transparent look by altering its Opacity property. Set this anywhere between 0% (completely transparent) and 100% (regular opaque ) to see the windows underneath your application.

Who Stole the ToolTips?

If you haven t already noticed, someone stole the ToolTip property in .NET. If you want little popup messages to appear when you hover your mouse over a button or whatever, you ll need to figure out the ToolTip control.

Here s how it works. Firstly, you add an instance of the ToolTip control to your form. This is an invisible component that actually displays the message. You can alter its properties through the Property window, such as the popup delay or whether it is Active .

Next , you need to add the actual ToolTip messages. Click on any of your controls and scroll down to the Misc section (presuming you order your property list by category). You ll see a property called something like ToolTip on ToolTip1 : This is a fresh property that your ToolTip instance gave every control on your form.

Simply set this property to your ToolTip message ”and that s a wrap!

Tricks of the Trade: Resizing Made Easy!

When the user resizes your Windows form at runtime, all of your controls will stay in place by default. They will not automatically resize with the form. You can change this behavior by editing the Anchor property of a control.

The Anchor property determines which sides of a form a control will stretch with. After the default, the most common setting for this property is Top, Bottom, Left, Right ”meaning the control will stretch with all sides of your form, behaving like the majority of resizable Windows applications.

The Dock property of a control is also useful when positioning and resizing controls. It allows you to dock a control to a particular side of a form and stick with that side, regardless of how the form is resized. To set this, simply select a new region via the Dock property drop-down menu.

Five Steps to Split Panels, Explorer-Style

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Split Panels folder.

If you re looking to create the split-panel look seen in many modern applications, you ll be happy to learn that a new .NET Splitter control can help you achieve exactly that.

Here are the simple steps to creating the split-panel look in your own programs:

  1. Add the control you want to appear down the left of your screen to your form. This could be a TreeView control, if you re going for the Explorer effect, or a Panel control if you want to add numerous controls (such as a list of icons for a menu, in the style of Outlook).

  2. Set the Dock property of this control to Left .

  3. Add a Splitter control to your form. It should automatically set its Dock property to Left . This is the widget your users will grab to resize the panels.

  4. Add your final control to your form. If you re continuing that elusive Explorer look, this will probably be the ListView control.

  5. Set the Dock property of this control to Fill . (Click on the box in the center.)

And that s it. Try running your application. Your users will be able to drag the Splitter and your two controls/panels will automatically resize. (See Figure 2-1.)

click to expand
Figure 2-1: Our Splitter control in action

Highlighting Errors with the ErrorProvider

The ErrorProvider control provides a great way of providing visual user feedback. If a problem occurs ”say, your user entered an incorrect customer number into a text box ”you can use the ErrorProvider to add a flashing error icon next to the control, along with a ToolTip describing the error.

The following code demonstrates use of the ErrorProvider in its simplest form. The code assumes that you have a Windows form containing an instance of the ErrorProvider control called ErrorProvider1, and a TextBox control called TextBox1:

' Set an error on a control 
ErrorProvider1.SetError(TextBox1, "Invalid Phone Number - Try Again") 
' Remove the error 
ErrorProvider1.SetError(TextBox1, "")

The first line here sets the error. This results in a blinking error icon to the right of the control, which displays the error as its ToolTip. (See Figure 2-2.) By default, the blinking stops as soon as you view the error message. The second line here removes the error flag.

click to expand
Figure 2-2: Neat visual error handling with the ErrorProvider component

It s worth noting that each of these settings ”icon, blink style, and rate ”is completely customizable. Simply edit the ErrorProvider control via the Properties window. Also, the ErrorProvider does a particularly good job working with data bound forms. Check out ErrorProvider component, overview in the help index for more information on this feature.

Learning the LinkLabel Control

The LinkLabel control is a great addition to your programming toolbox. It looks and acts like a Web page hyperlink, however has all the functionality of a regular Button.

By default, the link appears in blue, and changes to red when clicked on. These defaults can be altered by changing the LinkColor and ActiveLinkColor properties. The LinkLabel control has a Click event, to which you can add code to respond to a click. If you want the link to change to its default VisitedLinkColor , set the LinkVisited property to True either in code, or through the Properties window.

If you want your LinkLabel to open a Web site, use code similar to the following behind its Click event:

System.Diagnostics.Process.Start("http://www.karlmoore.com/") 
LinkLabel1.LinkVisited = True

Customizing the MonthCalendar Control

Your control toolbox includes an incredibly useful control called the MonthCalendar control. (See Figure 2-3.) However, few programmers really appreciate quite how customizable this widget really is. You can change virtually any aspect of how it works.

click to expand
Figure 2-3: The MonthCalendar control. Is there anything it can t do?

As you d expect, you can alter colors and fonts. The ForeColor , TitleBackColor , TitleForeColor , TrailingForeColor , and Font properties will help you out there. You can also alter the minimum and maximum dates that can be selected, either through the Properties window or in code, with the MinDate and MaxDate properties.

The ScrollChange property determines the number of months the control moves when its scroll buttons are clicked. ShowToday and ShowTodayCircle dictate whether today s date is highlighted and circled.

And there s still more. For example, ShowWeekNumbers displays the week numbers , which is highly useful to true business applications. You can change the FirstDayOfWeek property to another day, too, if necessary. The AnnuallyBoldedDates , BoldedDates , and MonthlyBoldedDates properties allow you to add dates, in code or through the Properties window, that are highlighted perhaps on a monthly or annual basis.

But best of all is probably CalendarDimensions . Use the Properties window to alter the Width and Height values to view multiple months at once, perhaps creating a six-month or full-year view in your desktop application at once. Highly useful!

Creating Your Own Number-Only Text Box

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Number-Only Text Box folder.

In some instances, you ll want to restrict the user to entering only a numeric value in a TextBox control on your Windows form.

To do this, you need to write code to check every key press in that textbox. You need to verify that the ASCII key value of each typed character is not less than 48 (the number 0 key) and not greater than 57 (the 9 key). If it s outside these ranges, you simply tell the TextBox control that you have handled that character and it doesn t get added to the box.

Here is a chunk of sample code you could add underneath a TextBox KeyPress event to do this:

If Asc(e.KeyChar) < 48 Or Asc(e.KeyChar) > 57 Then 
    ' Cancel non-numeric characters 
    e.Handled = True 
End If

Another way to do this would be to create a list of allowable characters. Here, for example, we re allowing numbers, spaces, colons, and dashes:

Dim strAllowableChars As String 
strAllowableChars = "0123456789-: " 
' If the character they have entered isn't in our list... 
If InStr(strAllowableChars, e.KeyChar.ToString) = 0 Then 
    ' Cancel the character 
    e.Handled = True 
End If

Displaying Animated GIFs Without the Browser Control

When it comes to displayed animated images in a Windows application, many developers instantly head off and incorporate the Web browser control. This isn t only going a little overboard for the sort of benefits an animation will provide your application ”but it s completely unnecessary.

You see, the PictureBox control automatically handles animated GIFs for you. Simply set the Image property, and it ll cycle through frame after frame both at design time and at runtime.

Want to create your own animated GIFs? Try checking out Paint Shop Pro and Animation Shop from Jasc Software at www.jasc.com.

The Two Methods of Changing Tab Order

In Windows forms, the tab order determines which controls receive the focus and in what order as your user presses the Tab key.

To stop a control from receiving the focus when your user presses the tab key, set its TabStop property to False . To change its tab order, alter the TabIndex property to a value starting at 0, where 0 is the first control to receive the focus.

However, a simpler way is to select View Tab Order from the menu, and then select your controls in the proposed tab order. The TabIndex property will be automatically set for you. (See Figure 2-4.)

click to expand
Figure 2-4: Visually setting our tab order

Secrets of Working with the System Tray

Working with the Windows system tray was never the easiest of tasks . Officially called the status notification area , it always involved a bundle of API calls and a little too much effort than it actually deserved. In .NET however, it s all about knowing which controls to use.

The heart of the whole process is the NotifyIcon component. Found in the toolbox, you ll need to drag and drop this little beast straight onto your form or component. Then you need to get editing those properties: change the Icon property to the icon you wish to display in the system tray and Text to the name you wish to appear as a ToolTip.

Try running your form or component as it stands so far: exactly zero lines of code later and your application can already display an icon in the system tray. But I m guessing you want to do just a little more than that.

Most applications display a menu when the user selects the icon. For this, you need to add another toolbox component: the ContextMenu. If you ve dropped this straight onto a form, you ll be able to edit it just like a regular menu: add separators, write code to respond to the Click events of the individual menu items, and so on. Then change the ContextMenu property of the NotifyIcon component to point to your new menu. Next, run your application and click on your icon in the system tray ”result achieved!

If, on the other hand, you simply want to run a little code or display a form when your icon is clicked, then check out the useful events supplied by the NotifyIcon property. You have Click , DoubleClick , MouseDown , MouseMove , and MouseUp . Simply use the code window to select one of these, and start writing your code.

And that s it. Two controls, a couple of properties, and a handful of events are all you need to know to master the system tray. (See Figure 2-5.)

click to expand
Figure 2-5: Is that a banana in my system tray?

Save User Time: Add Autocomplete to Combo Boxes

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 AutoComplete folder.

Develop an application in a program such as Microsoft Access and all your combo boxes will by default incorporate autocomplete, that ability to be able to tap a few characters in a drop-down list and have the nearest matching selection picked out for you.

In Visual Basic, however, there s no such intrinsic support. If you want autocomplete, you ve got to do it yourself. And this tip shows you how.

Simply add the following methods to your form. The first is called AutoCompleteKeyUp and accepts a combo box and KeyEventArgs objects as arguments. You need to call this in the KeyUp event of your combo box: it looks at what the user has typed and selects the most appropriate match. The second is called AutoCompleteLeave and should be called when the Leave event of your combo box is fired . This one simply takes whatever you ve finally chosen and cases it properly, as per the matching selection in the combo box.

Let s look at those functions now:

Public Sub AutoCompleteKeyUp(ByVal Combo As ComboBox, _ 
    ByVal e As KeyEventArgs) 
    Dim strTyped As String 
    Dim intFoundIndex As Integer 
    Dim objFoundItem As Object 
    Dim strFoundText As String 
    Dim strAppendText As String 
    ' Ignore basic selection keys 
    Select Case e.KeyCode 
        Case Keys.Back, Keys.Left, Keys.Right, Keys.Up, _ 
            Keys.Delete, Keys.Down, Keys.CapsLock 
            Return 
    End Select 
    ' Find what user has typed in list 
    strTyped = Combo.Text 
    intFoundIndex = Combo.FindString(strTyped) 
    ' If found... 
    If intFoundIndex >= 0 Then 
        ' Get list item (actual type depends on whether data bound) 
        objFoundItem = Combo.Items(intFoundIndex) 
        ' Use control to resolve text - in case data bound 
        strFoundText = Combo.GetItemText(objFoundItem) 
        ' Append the typed text to rest of the found string 
        ' (text is set twice due to a combo box quirk: 
        '  on certain platforms, setting just once ignores casing!) 
        strAppendText = strFoundText.Substring(strTyped.Length) 
        Combo.Text = strTyped & strAppendText 
        Combo.Text = strTyped & strAppendText 
        ' Select the appended text 
        Combo.SelectionStart = strTyped.Length 
        Combo.SelectionLength = strAppendText.Length 
    End If 
End Sub

Public Sub AutoCompleteLeave(ByVal Combo As ComboBox) 
    ' Correct casing when leaving combo 
    Dim intFoundIndex As Integer 
    intFoundIndex = Combo.FindStringExact(Combo.Text) 
    Combo.SelectedIndex = -1 
    Combo.SelectedIndex = intFoundIndex 
End Sub

And here s how you may call these functions from your combo box:

Private Sub ComboBox1_KeyUp(ByVal sender As Object, _ 
    ByVal e As System.Windows.Forms.KeyEventArgs) Handles ComboBox1.KeyUp 
    AutoCompleteKeyUp(ComboBox1, e) 
End Sub 
Private Sub ComboBox1_Leave(ByVal sender As Object, _ 
    ByVal e As System.EventArgs) Handles ComboBox1.Leave 
    AutoCompleteLeave(ComboBox1) 
End Sub

That s all you need to create your own autocomplete combo boxes. (See Figure 2-6.) And if you re feeling really adventurous, you might even want to wrap all of this up into a neat little user control. But we ll leave that for another tip.

click to expand
Figure 2-6: Our autocomplete combo box strutting its stuff

The Power of Command-Line Parameters

Command-line parameters can be incredibly useful because they allow users or other applications to pass startup information to your program. For example, if your program was called myapp.exe, they might run the following:

myapp.exe /nodialogs

Here, we have one command-line parameter, /nodialogs . In VB6, we could read this using the Command property. In VB .NET, this has been replaced with the System.Environment.GetCommandLineArgs function, which returns an array of any passed startup parameters.

And here s a chunk of code to show you just how to read them:

Dim MyStartupArguments() As String, intCount As Integer 
MyStartupArguments = System.Environment.GetCommandLineArgs 

For intCount = 0 To UBound(MyStartupArguments) 
    MessageBox.Show(MyStartupArguments(intCount).ToString) 
Next

How to Reset a Form

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Reset Form folder.

If you ve created a data entry “style Windows form that needs resetting with each addition, all the necessary code to clear the TextBox controls, uncheck the CheckBox controls, deselect combo boxes, ad infinitum , can all get a little repetitive ”particularly if you have to write it for multiple forms.

This is where the following method could prove useful. Simply pass in a form as a parameter, and it ll reset the main data entry controls: TextBox, CheckBox, and ComboBox, even those hidden away inside Tab Pages and other container controls. You might want to extend this routine to cater for RadioButton, ListBox, CheckedListBox, DomainUpDown, NumericUpDown, MonthCalendar, and DateTimePicker controls, too, depending on your requirements: simply edit the ClearControl method. To ensure flexibility, this subroutine automatically bypasses all controls with skip somewhere in the Tag property.

Here s the code:

Public Sub ResetForm(ByVal FormToReset As Form) 
    ' Resets the main data entry controls on the passed FormToReset 
    Dim objControl As Control 
    ' Loop around every control on the form and run the reset method 
    For Each objControl In FormToReset.Controls 
        ResetControl(objControl) 
    Next 
End Sub 

Public Sub ResetControl(ByVal ControlToReset As Control) 
    ' Resets the core control, then loops and 
    ' resets any sub controls, such as Tab pages 
    Dim intCount As Integer 
    ClearControl(ControlToReset) 
    If ControlToReset.Controls.Count > 0 Then 
        For intCount = 1 To ControlToReset.Controls.Count 
            ResetControl(ControlToReset.Controls(intCount - 1)) 
        Next 
    End If 
End Sub 

Public Sub ClearControl(ByVal ControlToClear As Control) 
    ' Clears the value of a particular control -
    ' you may wish to extend this to suit your exact needs 
    If InStr(ControlToClear.Tag, "skip", CompareMethod.Text) = 0 Then 
        If TypeOf (ControlToClear) Is System.Windows.Forms.TextBox Then 
            ControlToClear.Text = ""  ' Clear TextBox 
        ElseIf TypeOf (ControlToClear) Is System.Windows.Forms.CheckBox Then 
            Dim objCheckBox As System.Windows.Forms.CheckBox = ControlToClear 
            objCheckBox.Checked = False ' Uncheck CheckBox 
        ElseIf TypeOf (ControlToClear) Is System.Windows.Forms.ComboBox Then 
            Dim objComboBox As System.Windows.Forms.ComboBox = ControlToClear 
            objComboBox.SelectedIndex = -1 ' Deselect any ComboBox entry 
        End If 
    End If 
End Sub

You could use this function behind your form, as so:

ResetForm(Me)

How to Snap the Cursor to a Button

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Snap to Control folder.

If you re attempting to create that foolproof Windows application, one great technique to use is that of snapping the cursor to a particular control, thus anticipating the user s next click.

The following neat little function does exactly that. Simply pass in a control to get it started: it ll calculate the exact bottom middle location of the control and then snap the cursor to that position. (See Figure 2-7.) Here s the code:

click to expand
Figure 2-7: A simple little application, this time snapping to a LinkLabel control

Public Sub SnapToControl(ByVal Control As Control) 
    ' Snaps the cursor to the bottom middle of the passed control 
    Dim objPoint As Point = Control.PointToScreen(New Point(0, 0)) 
    objPoint.X += (Control.Width / 2                                ) 
    objPoint.Y += ((Control.Height / 4) * 3) 
    Cursor.Position = objPoint 
End Sub

And here s how you might use this to snap to, say, a Button control:

SnapToControl(Button1)

Capturing the Screen, Quick and Easy

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Screen Capture folder.

When it comes to showing people how to capture the screen, most VB .NET authors I ve seen tend to revert to the old school method of working: using the API. There is, however, a better way.

The following function is called GetScreenCapture and returns an Image object. It captures the screen by running a series of Print Screen key presses, which puts a screen grab on the clipboard, ready for my nifty little function to devour and return. (See Figure 2-8.) My function accepts a FullScreen argument, too: pass True to capture the whole screen, and False to capture just the active window.

click to expand
Figure 2-8: My sample application, capturing the active window (again and again)

Here s the code:

Public Function GetScreenCapture( _ 
    Optional ByVal FullScreen As Boolean = False) As Image 
    ' Captures the current screen and returns as an Image object 
    Dim objSK As SendKeys 
    Dim imgCapture As Image 
    If FullScreen = True Then 
        ' Print Screen pressed twice here as some systems 
        ' grab active window "accidentally" on first run 
        objSK.SendWait("{PRTSC 2}") 
    Else 
        objSK.SendWait("%{PRTSC}") 
    End If 
    Dim objData As IDataObject = Clipboard.GetDataObject() 
    Return objData.GetData(DataFormats.Bitmap) 
End Function

And here are a couple of examples demonstrating how to use that Image object ”firstly, saving it as a file, and, secondly, using it to set the Image property of a PictureBox control:

GetScreenCapture(True).Save("c:\screengrab.bmp", _ 
   System.Drawing.Imaging.ImageFormat.Bmp)) 
PictureBox1.Image = GetScreenCapture()

Stunning Navigation Bars with a Little-Known Freebie

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 XtraNavBar folder.

If you ve ever used Office, Windows XP, or Visual Studio .NET (which I m hoping you have), you may have admired the user interface and wondered how you could implement something like that yourself.

Well, unless you re willing to invest weeks into creating your own superior user interface components , you d typically have to shell out a thousand bucks on some fancy third-party solution. Unless, of course, you read on.

Software group Developer Express has developed a whole range of user interface components and, to promote its new .NET range, is giving away their $99 XtraNavBar Suite to anyone who knows the download address.

The component has been written from the ground up for .NET and allows you to add that professional Microsoft feel to any application within seconds, with support for taskbars, sidebars, XP themes, and more. (See Figure 2-9.) All for the grand price of zero.

click to expand
Figure 2-9: Sample navigation bars, all created with the free XtraNavBar Suite

To download and register your copy, simply head to www.devexpress.com/free. This backdoor will remain open indefinitely, but, just in case you miss the offer, I ve arranged to bundle the installation with the downloadable source for this book at www.apress.com. Enjoy!

Seven Steps to Taking Advantage of Windows XP Themes

A lot has been written about how to integrate your application with Windows XP themes, and most of it is complete hogwash. I ve personally wasted hours trying to figure out how to get it working.

All the online tutorials I ve encountered , including MSDN, are critically flawed in their how to description. This top tip, however, hopefully isn t.

So, what are Windows XP themes? Themes are a sort of limited, user-selected skin for the operating system, a make it look nice feature introduced in XP and to be continued and expanded upon in future versions of Windows.

Providing theme support in your application gives it that integrated Windows feel (for example, the default Windows XP theme gives all buttons an orange outline glow as the user hovers over ”something your own applications can inherit) and sets it up for greater customization in later versions of the operating system.

But how? Firstly, we design our application as normal, occasionally setting a button style to System . Then we create a manifest file from a template I ll provide and add it as a resource to our final executable. The contents of this manifest file tell the application to bind itself to ComCtl6, the Windows Common Controls component that will then draw the controls for your application and apply the themes as relevant.

And here are the seven easy steps to get it all up and running:

  1. Design your application as normal. Where possible, set the FlatStyle property of your controls to System . This is very important.

  2. When you re ready to roll out the final version of your application, compile your program. Open the Bin folder through Windows Explorer, right-click and select the Version tab. Make a note of the file version exactly as it is displayed here.

  3. Create a new file on your machine and call it MyManifest.manifest . Using Notepad, or a similar text editor, add the following text to this file:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
        manifestVersion="1.0"> 
    <assemblyIdentity 
        version="Insert Your Exact Version Number Here" 
        processorArchitecture="X86" 
        name="Name of Application" 
        type="win32" 
    /> 
    <description>Description of Application</description> 
    <dependency> 
        <dependentAssembly> 
           <assemblyIdentity 
               type="win32" 
               name="Microsoft.Windows.Common-Controls" 
               version="6.0.0.0" 
               processorArchitecture="X86" 
               publicKeyToken="6595b64144ccf1df" 
               language="*" 
           /> 
        </dependentAssembly> 
    </dependency> 
    </assembly>
    

  4. Back in Visual Studio .NET, open your actual assembly, which is typically the executable file you just compiled. Click on File Open File, and then select your assembly.

  5. Right-click anywhere on the new window and select Add Resource. Choose Import and select your MANIFEST file. You will be asked for a resource type. Type RT_MANIFEST and click on OK.

  6. In the Properties window, change the ID of the strange screen that has appeared from 101 to 1. Click on File Close to close this window, and accept any changes. Do the same with the next window, displaying details about your executable file, again ensuring that you save changes.

  7. Go back to Windows Explorer and give your application a test run, then uncork the champagne ! (See Figure 2-10.) If you get an error message, it s likely you either typed out the MANIFEST file incorrectly, or used the wrong version number ”in which case go back and try again.

click to expand
Figure 2-10: Two applications: one supporting the default XP theme, the other theme-less

The .NET Way of Checking for Previous Instances

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Previous Instances folder.

It s often useful to check whether another instance of your application is already running. The prime use for this is to ensure that only one instance of your program is active at any one time by checking during startup, and, if one is already running, providing that instance with the focus, then quitting.

In Visual Basic 6, you had the App.PrevInstance property to check. In VB .NET, we need to check whether the current process name is already running. That s what our code does here, encapsulated in the PrevInstance function. It returns a True if your application is already running on the same machine:

Public Function PrevInstance() As Boolean 
   If Diagnostics.Process.GetProcessesByName _ 
      (Diagnostics.Process.GetCurrentProcess.ProcessName).Length > 1 Then 
      Return True 
   Else 
      Return False 
   End If 
End Function

You might use this code as so:

If PrevInstance() = True Then 
     ' Get all previous instances 
     Dim Processes() As Process 
     Processes = Diagnostics.Process.GetProcessesByName( _ 
         Diagnostics.Process.GetCurrentProcess.ProcessName) 
    ' Activate the first instance 
    AppActivate(Processes(0).Id) 
    ' Exit the current instance 
    Application.Exit() 
End If

TOP TIP  

There s a little bug you may run into when using this code that turns the hair of most developers a funny shade of gray. If your application name is greater than fifteen characters, and running on either Windows NT or 2000, your code won t be able to tell whether a previous instance is running. It s weird, but true. The solution is to upgrade to XP or higher, or change your application name (Project Properties).

Converting RTF to HTML

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 RTF to HTML folder.

One amazingly common developer request is a method of converting the contents of a RichTextBox control to HTML. But, unless you re willing to spend hundreds on a third-party text editing control, you re out of luck. Even in VB .NET, Microsoft has chosen to remain ignorant to this much-desired feature.

So, you need to do it yourself, and the following chunk of code I ve put together should get you started. Just pass it a RichTextBox control as a parameter, and it ll return a string of HTML, ready for you to perhaps save to a file. (See Figure 2-11.)

click to expand
Figure 2-11: Our code doing its stuff: translating RTF into HTML

It doesn t handle more-complicated features, such as images or tables, but it will easily cope with fonts, sizes, colors, bold, italic, and paragraphs. Of course, you re more than welcome to expand it to exactly suit your needs.

You can call this code, as so:

strHTML = ConvertToHtml(RichTextBox1)

And here s the actual ConvertToHTML function:

Public Function ConvertToHTML(ByVal Box As RichTextBox) As String 
    ' Takes a RichTextBox control and returns a 
    ' simple HTML-formatted version of its contents 
    Dim strHTML As String 
    Dim strColour As String 
    Dim blnBold As Boolean 
    Dim blnItalic As Boolean 
    Dim strFont As String 
    Dim shtSize As Short 
    Dim lngOriginalStart As Long 
    Dim lngOriginalLength As Long 
    Dim intCount As Integer 
    ' If nothing in the box, exit 
    If Box.Text.Length = 0 Then Exit Function 
    ' Store original selections, then select first character 
    lngOriginalStart = 0 
    lngOriginalLength = Box.TextLength 
    Box.Select(0, 1) 
    ' Add HTML header 
    strHTML = "<html>" 
    ' Setup initial parameters 
    strColour = Box.SelectionColor.ToKnownColor.ToString 
    blnBold = Box.SelectionFont.Bold 
    blnItalic = Box.SelectionFont.Italic 
    strFont = Box.SelectionFont.FontFamily.Name 
    shtSize = Box.SelectionFont.Size 
    ' Include first 'style' parameters in the HTML 
    strHTML += "<span style=""font-family: " & strFont & _ 
      "; font-size: " & shtSize & "pt; color: " & strColour & """>" 
    ' Include bold tag, if required 
    If blnBold = True Then 
        strHTML += "<b>" 
    End If 
    ' Include italic tag, if required 
    If blnItalic = True Then 
        strHTML += "<i>" 
    End If 
    ' Finally, add our first character 
    strHTML += Box.Text.Substring(0, 1) 
    ' Loop around all remaining characters 
    For intCount = 2 To Box.Text.Length 
        ' Select current character 
        Box.Select(intCount - 1, 1) 
        ' If this is a line break, add HTML tag 
        If Box.Text.Substring(intCount - 1, 1) = Convert.ToChar(10) Then 
            strHTML += "<br>" 
        End If 
        ' Check/implement any changes in style 
        If Box.SelectionColor.ToKnownColor.ToString <> strColour _

Or Box.SelectionFont.FontFamily.Name <> strFont Or _ 
            Box.SelectionFont.Size <> shtSize Then 
            strHTML += "</span><span style=""font-family: " _ 
              & Box.SelectionFont.FontFamily.Name & _ 
              "; font-size: " & Box.SelectionFont.Size & _ 
              "pt; color: " & _ 
              Box.SelectionColor.ToKnownColor.ToString & """>" 
      End If 
      ' Check for bold changes 
      If Box.SelectionFont.Bold <> blnBold Then 
          If Box.SelectionFont.Bold = False Then 
              strHTML += "</b>" 
          Else 
              strHTML += "<b>" 
          End If 
      End If 
      ' Check for italic changes 
      If Box.SelectionFont.Italic <> blnItalic Then 
          If Box.SelectionFont.Italic = False Then 
              strHTML += "</i>" 
          Else 
              strHTML += "<i>" 
          End If 
      End If 
      ' Add the actual character 
      strHTML += Mid(Box.Text, intCount, 1) 
      ' Update variables with current style 
      strColour = Box.SelectionColor.ToKnownColor.ToString 
      blnBold = Box.SelectionFont.Bold 
      blnItalic = Box.SelectionFont.Italic 
      strFont = Box.SelectionFont.FontFamily.Name 
      shtSize = Box.SelectionFont.Size 
    Next 
    ' Close off any open bold/italic tags 
    If blnBold = True Then strHTML += "</b>" 
    If blnItalic = True Then strHTML += "</i>" 
    ' Terminate outstanding HTML tags 
    strHTML += "</span></html>" 
    ' Restore original RichTextBox selection 
    Box.Select(lngOriginalStart, lngOriginalLength) 
    ' Return HTML 
    Return strHTML 
End Function

TOP TIP  

Looking to turn HTML back into text? Check out my Converting HTML to Text, Easily tip in the Working with the Internet section, within Chapter 7.

Drag and Drop from Windows Explorer

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Drag and Drop Explorer folder.

Dragging and dropping within your application is pretty simple stuff. Simply set a couple of properties and you re rocking: look up drag and drop, Windows Forms in the help index for more information. But what if you want to drag and drop from another application, such as Windows Explorer?

One of the most commonly requested , yet infrequently answered Windows form questions is How can I let my users drag and drop files and folders directly into my applications? It s simple; just follow these three easy steps:

  1. Change the AllowDrop property of the control you want users to drop the files onto to True . This could be a ListBox control, a Panel control, or even your form itself.

  2. Add code to the DragOver event of the control, so the typical copy icon is displayed when files are dragged over.

    ' As dragged over, check data is file drop 
      If e.Data.GetDataPresent(DataFormats.FileDrop) Then 
          ' Display the copy (or other) icon 
          e.Effect = DragDropEffects.Copy 
    End If
    

  3. Finally, add code to the DragDrop event of the control, to receive and process information about the dropped files.

    ' Check this is a file drop 
    If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then 
        ' Retrieve list of files and loop through string array 
        Dim strFiles() As String = e.Data.GetData(DataFormats.FileDrop) 
        Dim intCount As Integer 
        For intCount = 0 To strFiles.Length 
            MessageBox.Show(strFiles(intCount)) 
        Next 
    End If
    

And that s it! In three simple steps, your application is ready to interoperate with Windows Explorer or any other application that supports the standard Windows file drag-and-drop routines. (See Figure 2-12.)

click to expand
Figure 2-12: Dragging and dropping from Windows Explorer

Of course, we re simply displaying the dropped files or folders in a message box. However, you could be doing something much more exciting: generating an MP3 play list, processing special work files, loading documents into your own mini word processor, and so forth. The possibilities are endless.

Dialog Boxes: What Did the User Click?

If you work a lot with your own popup dialog boxes rather than simply using the MessageBox class, you might be interested to learn about the new way in which Microsoft has made it easy to pass a response back to the code that displayed the dialog box.

In older versions of Visual Basic, you d typically display the form, setup properties and enumerations to set and retrieve the user response ”and more. Now, it s much easier, with the DialogResult property.

Here s how it works: you create your dialog box as usual. You might be asking the user a simple question, or getting him or her to confirm or cancel an action. For each response, add a button to the dialog box and change its DialogResult property to the result you wish that button to return.

Next, from your calling form, create an instance of your dialog form in code and run .ShowDialog . This opens the form modally and keeps it there until your user clicks on one of the response buttons. As soon as they do, the form closes and the result is passed back as the result of the .ShowDialog function, as so:

Dim objForm As New Form2() 
' If you need set any properties on 
' the form, do it here! 
If objForm.ShowDialog = DialogResult.Yes Then 
   MessageBox.Show("You clicked Yes!") 
Else 
   MessageBox.Show("You did not click Yes!") 
End If

It s pure simplicity!

Text Printing Class That Works!

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Printing folder.

You can print from your program in a number of ways. One option, for example, is to automate Microsoft Word, edit a document in code, and then programmatically print it out.

However, if you re looking to print directly from your application, the .NET Framework provides a number of components to help you in the System.Drawing.Printing namespace.

The core component here is PrintDocument. At its simplest, printing involves instantiating a PrintDocument object, setting its properties, and calling the Print method. With each page to be printed, PrintDocument raises a PrintPage event, to which you need to add your own printing logic. Other key classes in the same Printing namespace include PrinterSettings , PageSettings , and PrintPreviewControl .

As you can imagine, this is a large area and can get relatively complex. The following class attempts to simplify one of the most common uses: the simple printing of text. Simply add the following class code to your project and use as directed.

It s worth noting that this class actually works, as opposed to the less-functional TextFilePrintDocument class bundled by Microsoft in the Windows Forms Quick-Start tutorials, which only reads from text files plus cuts out as soon as a blank line is encountered. This class is also neatly encapsulated and allows you to change its font through a simple property, unlike Microsoft s second attempt with its highly publicized 101 VB .NET Samples.

Here s the code:

Public Class TextPrint 
   ' Inherits all the functionality of a PrintDocument 
   Inherits Printing.PrintDocument 
   ' Private variables to hold default font and text 
   Private fntPrintFont As Font 
   Private strText As String 
   Public Sub New(ByVal Text As String) 
       ' Sets the file stream 
       MyBase.New() 
       strText = Text 
   End Sub 
   Public Property Text() As String 
       Get 
           Return strText 
       End Get 
       Set(ByVal Value As String) 
           strText = Value 
       End Set 
   End Property 
   Protected Overrides Sub OnBeginPrint(ByVal ev As Printing.PrintEventArgs) 
       ' Run base code 
       MyBase.OnBeginPrint(ev) 
       ' Sets the default font 
       If fntPrintFont Is Nothing Then 
           fntPrintFont = New Font("Times New Roman", 12                                            ) 
       End If 
   End Sub 
   Public Property Font() As Font 
       ' Allows the user to override the default font 
       Get 
           Return fntPrintFont 
       End Get 
       Set(ByVal Value As Font) 
           fntPrintFont = Value 
       End Set 
   End Property 
   Protected Overrides Sub OnPrintPage(ByVal ev _ 
       As Printing.PrintPageEventArgs) 
       ' Provides the print logic for our document 

       ' Run base code 
       MyBase.OnPrintPage(ev) 
       ' Variables 
       Static intCurrentChar As Integer 
       Dim intPrintAreaHeight, intPrintAreaWidth, _ 
           intMarginLeft, intMarginTop As Integer 
       ' Set printing area boundaries and margin coordinates 
       With MyBase.DefaultPageSettings 
           intPrintAreaHeight = .PaperSize.Height - .Margins.Top - .Margins.Bottom 
           intPrintAreaWidth = .PaperSize.Width - .Margins.Left - .Margins.Right 
           intMarginLeft = .Margins.Left 'X 
           intMarginTop = .Margins.Top   'Y 
       End With 
       ' If Landscape set, swap printing height/width 
       If MyBase.DefaultPageSettings.Landscape Then 
           Dim intTemp As Integer 
           intTemp = intPrintAreaHeight 
           intPrintAreaHeight = intPrintAreaWidth 
           intPrintAreaWidth = intTemp 
       End If 
          ' Calculate total number of lines 
          Dim intLineCount As Int32= CInt(intPrintAreaHeight / Font.Height) 
          ' Initialize rectangle printing area 
       Dim rectPrintingArea As New RectangleF(intMarginLeft, intMarginTop, _ 
           intPrintAreaWidth, intPrintAreaHeight) 
       ' Initialise StringFormat class, for text layout 
       Dim objSF As New StringFormat(StringFormatFlags.LineLimit) 
       ' Figure out how many lines will fit into rectangle 
       Dim intLinesFilled, intCharsFitted As Int32 
       ev.Graphics.MeasureString(Mid(strText, _ 
                   UpgradeZeros(intCurrentChar)), Font, _ 
                   New SizeF(intPrintAreaWidth, intPrintAreaHeight), objSF, _ 
                   intCharsFitted, intLinesFilled) 
       ' Print the text to the page 
       ev.Graphics.DrawString(Mid(strText, _ 
           UpgradeZeros(intCurrentChar)), Font, _ 
           Brushes.Black, rectPrintingArea, objSF) 
       ' Increase current char count 
       intCurrentChar += intCharsFitted 
       ' Check whether we need to print more 
       If intCurrentChar < strText.Length Then 
           ev.HasMorePages = True 
       Else 
           ev.HasMorePages = False 
           intCurrentChar = 0 
       End If 
   End Sub 
   Public Function UpgradeZeros(ByVal Input As Integer) As Integer 
       ' Upgrades all zeros to ones 
       ' - used as opposed to defunct IIF or messy If statements 
       If Input = 0 Then 
           Return 1 
       Else 
           Return Input 
       End If 
   End Function 
End Class

We could use this class as follows :

' Create object, passing in text 
Dim MyPrintObject As New TextPrint(TextBox1.Text) 
' Set font, if required 
MyPrintObject.Font = New Font("Tahoma", 8) 
' Issue print command 
MyPrintObject.Print()

The Secret Rebirth of .PrintForm

Download supporting files at www.apress.com .

The files for this tip are in the Ch2 Print Form folder.

If you ve managed to print anything in .NET, you ll know it s no mean task. What, you may ask, happened to the old .PrintForm method of VB6 fame? Unfortunately, like a number of older features, it got sold out in the name of standardization.

But don t fret: with just a few lines of extra code, we can bring it back from the dead. How? Simply follow these four easy steps:

  1. Design your form as usual, adding all the required controls you wish to be printed.

  2. From your toolbox, add one PictureBox, PrintDocument, and PrintDialog controls to your form. Your code will use these to support the printing of your form. For this code sample, I ve named my controls MyPictureBox, MyPrintDocument, and MyPrintDialog, respectively. The PictureBox is the only visible control, which you can make invisible if you wish.

  3. Add the following code behind your form. These routines perform the basic function of taking a screenshot and sending the results to the printer. The main method is PrintForm :

    Public Sub PrintForm() 
        ' Takes a screenshot, then initiates the print 
        GrabScreen() 
        MyPrintDialog.Document = MyPrintDocument 
        If MyPrintDialog.ShowDialog = DialogResult.OK Then 
            MyPrintDocument.Print() 
        End If 
    End Sub 
    
    ' API call to help generate final screenshot 
    Private Declare Auto Function BitBlt Lib "gdi32.dll" _ 
        (ByVal hdcDest As IntPtr, ByVal nXDest As Integer, _ 
        ByVal nYDest As Integer, ByVal nWidth As Integer, _ 
        ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, _ 
        ByVal nXSrc As Integer, ByVal nYSrc As Integer, _ 
        ByVal dwRop As System.Int32) As Boolean 
    
    ' Variable to store screenshot 
    Private bmpScreenshot As Bitmap 
    
    Private Sub GrabScreen() 
        ' Performs a screenshot, saving results to bmpScreenshot 
        Dim objGraphics As Graphics = Me.CreateGraphics 
        Dim objSize As Size = Me.Size 
        Const SRCCOPY As Integer = &HCC0020 
    
        bmpScreenshot = New Bitmap(objSize.Width, _ 
           objSize.Height, objGraphics) 
        Dim objGraphics2As Graphics = objGraphics.FromImage _ 
           (bmpScreenshot) 
        Dim deviceContext1 As IntPtr = objGraphics.GetHdc 
        Dim deviceContext2 As IntPtr = objGraphics2.GetHdc 
    
        BitBlt(deviceContext2, 0, 0, Me.ClientRectangle.Width, _ 
            Me.ClientRectangle.Height, deviceContext1, 0, 0, SRCCOPY) 
        objGraphics.ReleaseHdc(deviceContext1) 
        objGraphics2.ReleaseHdc(deviceContext2) 
    End Sub 
     Private Sub MyPrintDocument_PrintPage(ByVal _ 
        sender As System.Object, ByVal e As _ 
        System.Drawing.Printing.PrintPageEventArgs) _ 
        Handles MyPrintDocument.PrintPage 
        ' Method that handles the printing 
        Dim objImageToPrint As Graphics = e.Graphics 
        objImageToPrint.DrawImage(bmpScreenshot, 0, 0) 
        bmpScreenshot.Dispose() 
        objImageToPrint.Dispose() 
        e.HasMorePages = False 
    End Sub
    

  4. Add a Print button to your control, add code to run the PrintForm method, and then simply wait and let our code run its magic. Don t forget, you may wish to make certain controls (such as the Print button itself) invisible before running the PrintForm method, then making it visible again afterward.

And that s quite simply all you need to print your form. Admittedly, it s not as easy as .PrintForm , but when the code is already written and ready to roll, who s complaining?

The Facts on Visual Inheritance

Visual inheritance allows you to create one master form, and then have other Windows forms inherit its layout and code. For example, you might create one master form for your program s wizard, and then add further wizard forms that automatically inherit its appearance and functionality, customizing each as appropriate.

To use visual inheritance, first design and code your master form, then build your application ( Ctrl+Shift+B ). Next, select Project Add Inherited Form from the menu. Enter a name, click on Open, and choose the form you wish to inherit from. Then, further customize this form to meet your needs.

It s worth noting that any changes you make here will not alter your original form; rather, they will just override the original settings inherited from your master form.

Looking at Windows, Performance Counters, and More

Building great applications isn t all about amazing code snippets that can make your programs look great and run like the wind. It s also about being intelligent ” and one big part of that is the ability for your program to look at the world around it (Windows) and figure out exactly what s happening.

Well, as you can imagine, this is one obviously huge area, so I ll be brief and provide just a few core code tips that ll give you a great starting point when trying to find out just what you want.

First off, to find out about your current environment ”such as command-line arguments, the user domain name, tick count, and so on ”simply explore the System.Environment class. There s no need for any sticky API calls. Here s a System.Environment example that retrieves the name of the current version of Windows:

x = System.Environment.OSVersion.ToString

To discover more about the actual system itself ”such as the computer name, number of monitors attached, whether visual aids should be used rather than audio, the default icon size, and so on ”check out the System.Windows.Forms.SystemInformation class. Here s an example that checks whether the computer booted normally (that is, didn t use safe mode):

If System.Windows.Forms.SystemInformation.BootMode = _ 
   BootMode.Normal Then 
      ' Computer booted in normal mode 
End If

Finally, performance counters are an excellent way of tapping into exactly what the system is up to. This is one huge subject on its own, and a mound of books has already been written on the subject. However, in brief, performance counters report on the status of the system and its applications. They re predefined and return a number, which you can look at in a variety of formats (an instantaneous figure, an average, percentage, et cetera).

Examples include the amount of system memory available, a processor s busy time, the number of ASP.NET applications running, or even how many SQL Server connections you have open. (See Figure 2-13.)

click to expand
Figure 2-13: Viewing the available performance counters through the Server Explorer

You can browse the existing performance monitors by using the Server Explorer (View Server Explorer), expanding upon your server and exploring the Performance Counters node. If you see an item you think you ll want to use in your code, you can drag it onto your form and manipulate the newly created PerformanceCounter object in code, or just do it all in code. The following snippet demonstrates the latter, displaying the amount of available memory in a message box:

Dim perfFreeMemory As New PerformanceCounter("Memory", _ 
   "Available MBytes") 
MessageBox.Show("There are " & perfFreeMemory.NextValue & _ 
   "MB of memory available on your system. This program requires more.")

A bundle of .NET-specific performance counters are available, too ”and good system administrators will be more than familiar with these figures, which you can analyze through the PerfMon.exe tool. The .NET revolution also allows you to set up your own custom performance counters with ease, recording data such as the number of sales per second. You can learn more about all of this by looking up performance counters in the help index, and then browsing the subcategories .

TOP TIP  

If you re attempting to use performance monitors in ASP.NET applications, you may initially find yourself experiencing a bundle of Access denied error messages. That s because .NET is picky about exactly who can and can t see this system information. You can resolve this by following the security guidelines at http://aspnet.4guysfromrolla.com/articles/041002-1.aspx, or if you re simply wanting to retrieve data such as how long your Web server has been up, check out my tips in the next chapter.