Archive for June, 2007

MDI PDF File Viewer with Tabbed Header

Tabbed FolderThis code example attempts to answer two common questions. First, how can I load PDF files into my VB.NET application for viewing? And, second, how can I create a MDI application that has tabbed browsing like Visual Studio, MSDN, and IE7? While there are more complex, and perhaps more elegant, ways to accomplish these tasks, this example offers a simple, easy-to-use way to implement these features. Plus it shows how to deal with some quirks you might encounter.

First, let’s look at what we need to do to load PDF files. In our case, we’re going to assume that potential users have Internet Explorer and Adobe Acrobat Reader already installed on their systems and configured correctly. If you can’t safely make this assumption, then you’ll need to look into more complex methods to accomplish this task. But, in most organizations, this code should work fine.

Having these ready made components reduces our work to a few simple commands. First, we need to place the Web Browser component on a form. In the case of this example, this will be a MDI child form. We will dock it so that it fills the entire form. Next, what do we need to do to load the PDF? That’s simple too. All we need to do is use the Navigate method to load the PDF into the browser, like so:

wbDocument.Navigate(filename)

So, we can achieve this seemingly daunting task with a control and one command, pretty easy. However, unloading is where we have to use a little trick to avoid a potential problem.

The problem that can be encountered is that Acrobat, under some circumstances, will hang onto a reference to a PDF file, thus keeping it locked. This may not be a big deal in some cases since the lock will be released eventually or at least when the application ends. But in other cases, the locked file may prevent actions your program or users need to take. To insure that Acrobat releases the file, we have to clear it out of our web browser completely. We can use the following code to do this:

wbDocument.Hide()
wbDocument.Navigate("about:blank")
Do Until wbDocument.ReadyState = WebBrowserReadyState.Complete
    Application.DoEvents()
    System.Threading.Thread.Sleep(100)
Loop
wbDocument.Dispose()
System.Threading.Thread.Sleep(100)

Here we hide the browser control, navigate to the default blank page, wait for the browser to completely load this page, then dispose of the browser control and wait a brief moment before continuing. You may not need all this waiting/sleeping but I’ve found that having these short waits helped insure that the PDF file is completely freed. Try it with and without this code and see how it performs for you.

Now, let’s create a tabbed browser for our MDI app. This too is rather easy to do as well. First, we add a tab control to our MDI parent form and dock it to the top and size it appropriately. We don’t need to add any tabs to it at this point. Next, in our MDI parent’s code, we want to include loading to the tab with the loading of a child form, like so:

Private Sub LoadNewViewer(ByVal fileName As String)
    If My.Computer.FileSystem.GetFileInfo(fileName).Extension.ToUpper = ".PDF" Then
        For Each frm As Form In Me.MdiChildren
            If frm.Text = fileName Then
                frm.BringToFront()
                tabWindows.SelectedTab = tabWindows.TabPages(fileName)
                Exit Sub
            End If
        Next
        Dim ChildForm As New frmViewer
        ChildForm.MdiParent = Me
        ChildForm.Text = fileName
        ChildForm.wbDocument.Navigate(fileName)
        tabWindows.TabPages.Insert(0, fileName, fileName)
        tabWindows.SelectTab(0)
        For Each frm As Form In Me.MdiChildren
            frm.WindowState = FormWindowState.Normal
        Next
        ChildForm.Show()
        ChildForm.WindowState = FormWindowState.Maximized
    End If
End Sub

That may seem like a lot of code but it performs a number of functions that improve how the app performs and looks.

After making sure the file type is what we want to load, we first check to see if the document is already loaded into our application. If it is, we bring it to the front and select the corresponding tab. In our case, we’re making it simple by using the filename as the key and display text for the child form and the tab but you could add a more complex method if you needed to do so. If the form is already loaded, we’re done, but if it isn’t we need to create a new child form in the next steps of the application. Finally we load in the PDF document and show it and we’re done, or are we?

If you don’t mind showing the default MDI child icon, then yes, you’re done. But, if you want to show your own icon, there is a little trickery involved here. To get your icon to show you have to normalize each of the child forms before showing your new form. This does cause a bit of flicker so you have to decide if you want icons or a little flicker in your app. In addition to this step, you will also need to load the icons in the New method of the MDI parent and child forms. This is a little quirk in the MDI interface and this is the easiest way to work around it.

ยป Click here to download VB.NET source code for this article.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

3 comments June 23rd, 2007

Enlarge Combobox Dropdown Area

An early version of the dropdown comboboxIn this second part of my series revisiting functions I wrote for VB6 I decided to see how one might implement a VB.NET routine to enlarge a combobox’s dropdown area to the size of the largest string that’s loaded into it. In VB6, as you can see in my original article, Enlarge Dropdown Area, it required a few API calls to pull it off. Has this gotten any easier to do in VB.NET?

The answer is, yes, it is much easier.

The combobox now has a property called DropDownWidth that you can use to set the width of the dropdown area. It couldn’t be much easier. Just put in the width you want in the properties box at design time and you’re set.

But, what if you don’t know how long the values will be at design time? You could estimate or you could use code to get the length of the strings you add and dynamically size the combo. Let’s look at one such routine.

Private Sub AddComboItemWithWidth(ByVal comboToLoad As ComboBox, ByVal itemText As String, ByVal maxStringWidth As Integer)
    Dim MyGraphics As System.Drawing.Graphics = Me.CreateGraphics
    Dim StringSize As Integer = CInt(MyGraphics.MeasureString(itemText, comboToLoad.Font, maxStringWidth).Width) + 1
    If comboToLoad.DropDownWidth < StringSize Then
        comboToLoad.DropDownWidth = StringSize
    End If
    comboToLoad.Items.Add(itemText)
End Sub

In this routine we pass in the combobox that we want to load, the value of the string, and the maximum length we want the combobox dropdown to be in pixels (it might be a good idea to base this user screen size or other such factors). First, we create a graphics object using the current form’s method. If you wanted to have this routine outside of the confines of a single form you could pass this object in as a parameter. Next, we use the MeasureString method to get the number of pixels required by the string. If the size is greater than the maximum size the max is returned. I add one to the returned number for a little extra padding but you don’t need to do this since the method will do some padding for you. Next, the returned size is compared against the current size and, if it is greater, the combo’s DropDownWidth property is increased. Lastly, the item is added to the combo.

If you wanted to resize the combo after loading, you could do the following:

Private Sub SizeCombo(ByVal comboToSize As ComboBox, ByVal maxStringWidth As Integer)
    Dim MyGraphics As System.Drawing.Graphics = Me.CreateGraphics
    Dim StringSize As Integer
    For Each ItemValue As String In comboToSize.Items
        StringSize = CInt(MyGraphics.MeasureString(ItemValue, comboToSize.Font, maxStringWidth).Width) + 1
        If comboToSize.DropDownWidth < StringSize Then
            comboToSize.DropDownWidth = StringSize
        End If
    Next
End Sub

This routine works in the same way except that it sizes after the combo is loaded.

So, it seems that doing this is a lot easier and simpler in .NET than it was in VB6.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

1 comment June 19th, 2007

Select Text Upon Entry

Selling you on VB.NETOn my VB6 Notebook Archive Site I have several code examples that were part of my standard library of routines that I used in many places. I thought it would be an interesting exercise to revisit these functions to see how they would be implemented in VB.NET. The first one we’ll take a look at is the Select Text Upon Entry routine. In VB6 we used the SelStart and SelLength properties of the textbox in the GotFocus event to select all the text when the user enters it. How would you do this in VB.NET?

First of all, let’s take a look at how you would do this in VB6:

Private Sub Text1_GotFocus()
    Text1.SelStart = 0
    Text1.SelLength = Len(Text1.Text)
End Sub

Now, let’s see what our VB.NET/Framework 2.0 level options are.

SelStart and SelLength are still there although they are completely spelled out as SelectionStart and SelectionLength. Otherwise they function the same as their VB6 counterparts. However, there are two new .NET methods that select text, Select and SelectAll. Select allows you to select a range indicated by a start value and length while SelectAll selects all of the text in the textbox at once. So, it looks like our natural choice should be SelectAll.

The GotFocus event is still there too but there is also the new Enter event. Which should we use? Well according to MSDN the event sequence when the user, or code, enters a control is Enter then GotFocus. Microsoft recommends that programmers use the Enter/Leave events instead of the legacy GotFocus/LostFocus events. Therefore, to update the routine we should put our code in the Enter event.

So, our updated routine would look like this:

Private Sub TextBox1_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Enter
   TextBox1.SelectAll()
End Sub

Now we can test it and see what happens. We type in some text into TextBox1 and tab to another control and tab back to TextBox1 and our text is automatically selected. But, when we mouse into the textbox it isn’t selected. What gives?!? OK, let’s move the code back to our old reliable GotFocus event. The same thing happens!

As it turns out, this is caused by the way the event structure in .NET works. When the control is selected with the mouse, the TextBox’s Enter event is fired before the MouseDown event. The MouseDown event then sets the SelectionStart property to the location where the mouse was clicked. To fix this problem, you have to have code in both the Enter and MouseDown events.

Private Sub TextBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseDown
    TextBox1.SelectAll()
End Sub

This means we have to have the same code in two different events due to this new behavior in .NET. One option would be to subclass the textbox control and override the behavior in code there, thus moving the same code in two places from your main application to within the control code. This might be a good option to consider if this is a feature you need throughout your application.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

1 comment June 15th, 2007

Free VB.NET Resources From Microsoft

Free VB.NET Stuff From Microsoft!Microsoft often makes books, videos, code example and other media available online to developers in the MSDN library. Here are a few you should take advantage of if you’re making the move from VB6 to VB.NET or if you just want to improve your knowledge and productivity. It’s surprising that many developers simply don’t bother to check out all the freebies Microsoft makes available to us.

The first book is one written with the VB6 programmer in mind, Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET. It goes into ways to fix the inevitable upgrade issues, outlines the differences between VB6 and VB.NET, and provides some architectural and design advice. The appendix sections have a cross-reference of function and control differences that is quite valuable. Unfortunately, you’ll have to buy the book to get the code on the CD though.

Another book that’s available for download is Introducing Microsoft Visual Basic 2005 for Developers. The introductory chapters are quite helpful to a VB6′er who’s going straight to VB 2005. The rest of the book covers a lot of the changes in VB 2005, usually in a lot of helpful detail.

Next, let’s take a look at VB At The Movies. This is a set of instructional videos on a wide range of topics such as deployment, using XML, and many others. While the ‘movie posters’ promise an exciting ‘Hollywood’ video they’re actually pretty plain instructional videos. It’s good info though and it’s well worth your time to view them.

If you’re just getting started learning VB in general, check out the Beginner Developer Learning Center. You can use it to go at your own pace to learn VB on a well laid out learning path.

If you want to hop right into code, have a look at the Visual Basic Starter Kits & Power Packs. Here you’ll find template code for such useful things as Shareware licensing, PayPal integration, eBay auction management, web log analysis, time tracking, RSS management and many others. There’s even an example of how to program Lego Mindstorms robots. You will also want to download the Samples for Visual Studio 2005. These sets of examples cover the basics of coding for a wide range of topics. Most of the code in these samples can be easily moved to your applications so it’s not only of great instructional value but can be a timesaver as well.

If you have a LCD monitor, you might want to download the Consolas Font Pack. The letters of this monotype font are easier on the eyes than the usual Courier New but they only work well on a ClearType enabled LCD monitor.

The last freebie I’ll cover here is Refactor!. This is a handy tool to have since it assist you in reordering parameters, encapsulating property values, creating overloads and more. Since it operates modelessly it doesn’t get in your way as you code but it’s always there to help you if you want it to.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

Add comment June 12th, 2007

The Continue Statement

Continue keeps things runningBefore VS 2005 there was a missing piece of the puzzle in VB when it came to flow control in For…Next and Do loops, there was no good way to continue processing in a loop. This led to some programmers using crazy coding techniques, like huge If…ElseIf…Else blocks, or even the dreaded GoTo to implement it. Now, we have the Continue statement that allows us to skip to the next iteration of a loop with minimal code or heavy indenting. Here’s how we can put it to work.

If you’ve ever seen an bit of VB6 code like this:
.
.

Do While SomeFlag
    '20 lines of code here
    If Cond1 = TestFunction() Then
        '3 lines of code here
    Else
        '100 more lines of code here
    End If
Loop

Or, even worse, this:

Do While SomeFlag
    '20 lines of code here
    If Cond1 = TestFunction() Then
        '3 lines of code here
        GoTo EndOfLoop
    Else
        '100 more lines of code here
    End If
EndOfLoop:
Loop

The value of the Continue statement in terms of code readability and maintainability is probably already apparent to you.

Do While SomeFlag
    '20 more lines of code here
    If Cond1 = TestFunction() Then
        '3 lines of code here
        Continue Do
    End If
    '100 more lines of code here
Loop

By using it we get rid of a level of nested indenting, which can make reading the code more difficult, and we avoid the ambiguous and dangerous GoTo since Continue makes it clear where the next point of execution will be.

The Continue statement is modified by the type of looping structure it’s used in: For, Do, or While. This comes into play when you have nested looping code. For example:

Do While SomeFlag
    '20 more lines of code here
    If Cond1 = TestFunction() Then
        For Counter As Integer = 0 To 9
            '4 more lines of code here
            If TestValue < 6 Then
                '3 more lines of code here
                If Not SomeFlag Then
                    Continue Do
                Else
                    Continue For
                End If
            End If
            '25 more lines of code here
            If Not SomeFlag Then
                Continue Do
            End If
        Next
    End If
    '100 more lines of code here
Loop

In this example, we want to continue the inner For…Next loop in some conditions and continue the outer Do loop in others. Continue makes this easy to do. But, what if you have two nested loops of the same type?

Do While SomeFlag
    '20 more lines of code here
    If Cond1 = TestFunction() Then
        Do Until Counter = 10
            '4 more lines of code here
            If TestValue < 6 Then
                Continue Do
            End If
            '25 more lines of code here
        Loop
        '15 more lines of code here
        If Not SomeFlag Then
            Continue Do
        End If
    End If
    '100 more lines of code here
Loop

In this case, the inner loop takes precedence. In the example above the first Continue Do would move to the next iteration of the inner loop while the second one would work with the outer loop.

The thing to be careful about when using Continue is to be mindful of how you are structuring your code. Long routines may benefit from being broken up into 2 or more smaller chunks. Complex conditional situations may make Continue impractical. However, if used wisely, Continue can make your program’s flow more readable and maintainable.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

Add comment June 4th, 2007

Previous Posts


Visit Me At My New Site, Programming In C#

Most Popular Articles

Highest Rated Articles

Categories

Most Recent Articles

Feeds

 Subscribe in a reader

To subscribe by e-mail
Enter your address here

Delivered by FeedBurner

VB Opportunities

Archives