smithvoice.com
 Y'herd thisun? 

“cASe senSitiviTy is stupid... and it's also just plain dumb. Why do so-smart coders still stick to what was just a bug to begin with? And YOU CAN code by voice, but you have to accept that dogma is not law.”
-smith


from this page

Your own image control and App part 7

TaggedCoding, VB, Imaging

Originally published December 2002 on DoItIn.net using VB7.0/2002. Updated for VB7.1 February 2005

Links for compiled demo versions, all required resources and source code are included at the end of this article.

Plus, get the complete eBook in Adobe Acrobat 7 format ... all here.


 

7) Selecting specific fax pages

Head back over to the UC codeview, we're going to let the user pick from the pages.

In the "Declarations" > "Private Variables" sub-region, add "Private m_CurrentPageNumber As Integer = 0" and down in the "Public Properties" region add the following code:

 

Public Property CurrentPageNumber() As Integer
'no match the FrameDimension.Page value, the
'current page is held internally as zero-based
'we expose to users as the more logical one-based
  
Get
 If Not m_OriginalImage Is Nothing Then
 Return m_CurrentPageNumber + 1
  
 Else
 Return 0
  
 End If
  
End Get
  
Set(ByVal Value As Integer)
 If Not m_OriginalImage Is Nothing Then
 If Value <= TotalPageCount And Value > 0 Then
 Dim initialPage As Integer = m_CurrentPageNumber + 1
 m_CurrentPageNumber = Value - 1
  
 'Directly use the picturebox image instance
 'you could also create a new clone of the original image
 'and put that in the picturebox but this is an easier way
 'to allow iterative editing
 ' ... the thing to watch out for is degradation after repeated re-use
 pbDisplay.Image.SelectActiveFrame(FrameDimension.Page, m_CurrentPageNumber)
  
 'if you reclone the original image then the picturebox refreshes nicely
 'however I've found that working directly with the picturebox image
 'requires a forced refresh to clean up ghosts of the previously displayed page
 pbDisplay.Refresh()
  
 'tell the host
 RaiseEvent PageChanged(Me, _
 New svImagePageChangeEventArgs(TotalPageCount, initialPage, m_CurrentPageNumber + 1))
  
 Else
 Throw New ArgumentOutOfRangeException("Page " & Value.ToString & _
 " does not exist (file contains " & TotalPageCount.ToString & " pages)")
  
 End If
 End If
End Set
End Property

 

The comments tell most of the story and to answer the obvious question, a multipage image file doesn't do anything itself to help you show the internal images, it's just a container and you have to remember what page you're working with. To make a particular page active you specify the zero-based value in a call to [Image].SelectActiveFrame then, if showing the image as we are here, you reload it into the display widget.

The Set raises an ArgumentOutOfRangeException if the specified value makes no sense for the file (Remember, it's not advised to make a custom exception if a built-in exception easily covers the situation). If the Set works a new custom "PageChanged" event is raised to allow the dev-user to wire up their GUIs.

As with our first custom event, we'll add the signature to the svImageEdit Declarations>>Events region...

 

Public Event PageChanged(ByVal sender As Object, ByVal e As svImagePageChangeEventArgs)

... and we'll create the event's EventArgs class in the svEventArguments.vb file. Remember that this file is being used solely as a code container and all of the classes it contains are top-level, not internal to a wrapper class the way we did with svExceptions.vb. Open the svEventArguements file and below the previous "End Class", add the following code:

 

Public Class svImagePageChangeEventArgs
Inherits svImageLoadEventArgs
  
Private m_PreviousPage As Integer
Private m_NewPage As Integer
  
  
Public Sub New(ByVal totalPages As Integer, ByVal previousPage As Integer, ByVal newPage As Integer)
MyBase.New(totalPages)
m_PreviousPage = previousPage
m_NewPage = newPage
End Sub
  
Public ReadOnly Property PreviousPage() As Integer
Get
 Return m_PreviousPage
End Get
  
End Property
  
Public ReadOnly Property NewPage() As Integer
Get
 Return m_NewPage
End Get
  
End Property
End Class

 

Maybe providing the original page number won't ever be used in the real world, but darned if I didn't wish more components would provide that information on index switch events so I added it. :)

You'll notice that we inherited from our own svImageLoadEventArgs instead of from System.EventArgs. We didn't have to do this, svImageLoadEventArgs only has the single property TotalPageCount so the duplication of code we'd have had from deriving from EventArgs here wouldn't have been a big deal. However, it was duplication and it could be avoided so that's what we did.

Rebuild and Save the solution then call up the testHarness form's GUI designer.

Because we're going to be testing several new features you might have to make some space on your form. Add a label (Name="lblPageInfo" Text="Page: 0 of 0"), a combobox (Name="cboPages" DropdownStyle=DropdownList) and two buttons (Name="butSetPage" Text="set page" and Name="butSetInvalidPage" Text="set invalid page"). Remember to set the Anchor properties so they won't disappear when you resize the form.

Double click on the form to call up the Form_Load event stub in codeview. This being a test project with the sole purpose of trying to hit bugs we don't want to add too much that might influence testing but there's a low-impact structure that can help our comprehension of raised events. In the Load stub, type "InitForm()" then create a new method by that name. Over time we'll likely have to perform a few initializers so we'll have this method stay clean by calling out to others as needed, starting with a call to a method named "InitPageCountDisplay" which will optionally accept a value for the count of pages in the current image.

 

Private Sub InitForm()
InitPageCountDisplay()
End Sub
  
Private Sub InitPageCountDisplay(Optional ByVal pageCount As Integer = 0)
  
'because it's best practice to not let a user
'play with something till it makes sense, we could
'disable the combo and button here, based on the page count using:
'
' cboPages.Enabled = CBool(pageCount > 1)
' butSetPage.Enabled = CBool(pageCount > 1)
'
'but our dev-user may not add that and this being a test harness for
'the dev-user we'll see if playing it loose causes problems
  
'init the combo
cboPages.Items.Clear()
  
'set the label
If pageCount = 0 Then
 lblPageInfo.Text = "Page: 0 of " & pageCount.ToString
 cboPages.Items.Add("0")
  
Else
 lblPageInfo.Text = "Page: " & _
 SvImageEditor1.CurrentPageNumber.ToString & _
 " of " & pageCount.ToString
  
 'fill the combobox
 With cboPages
 For i As Integer = 1 To pageCount
 .Items.Add(i)
 Next
  
 End With
  
End If
  
'force the displayarea to the first item
cboPages.SelectedIndex = 0
  
End Sub

Now go back to the SvImageEditor1_ImageLoaded event and replace the simple messagebox line with code to update the lblPageInfo text:

 

Private Sub SvImageEditor1_ImageLoaded(ByVal sender As Object, _
ByVal e As svImageLoadEventArgs) _
Handles SvImageEditor1.ImageLoaded
 
InitPageCountDisplay(e.TotalPages)
End Sub

Next use the IDE dropdowns to create the stub for the new PageChanged event and fill it in:

 

Private Sub SvImageEditor1_PageChanged(ByVal sender As Object, _
ByVal e As svImagePageChangeEventArgs) _
Handles SvImageEditor1.PageChanged
 
lblPageInfo.Text = "Page: " & e.NewPage & " of " & e.TotalPages
End Sub

All that's left is to add the button clicks. Use the IDE combox to create the stubs and type in the lines or just cut and paste the following to your codeview:

 

Private Sub butSetPage_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles butSetPage.Click
 
SvImageEditor1.CurrentPageNumber = CInt(cboPages.Text)
 
 End Sub
 
 Private Sub butSetInvalidPage_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles butSetInvalidPage.Click
 
Try
SvImageEditor1.CurrentPageNumber = 5000
 
Catch ex As Exception
MsgBox(ex.Message)
 
End Try
End Sub

Save the solution then Rebuild and F5 to bang on it. Test all of the image file types and alter the SvImageEditor1.CurrentPageNumber value in the butSetInvalidPage click event to make sure that your control is stable even when a dev-user lets silly data pass through.

We've wrapped up the first feature in the spec. The rest each have their own little oddities but for the most part getting them checked off will follow a more focused path now that we've set up our widget's infrastructure for exceptions and events.

Next: Rotating image displays

Robert Smith
Kirkland, WA

 

added to smithvoice march 2005


jump to:

  • 1) The spec
  • 2) Setting up the workspace
  • 3) Feature 1: Loading an image
  • 4) Custom Exceptions
  • 5) "Fax images" and Multipage TIFFs
  • 6) Custom events
  • 7) Selecting specific fax pages
  • 8) Feature 2: Rotating image displays
  • 9) The most useful tool in GDI+: DrawImage
  • 10) Feature 3: Zooming
  • 11) Handling the unhandleable exception
  • 12) Fixing the squish
  • 13) Zooming to fit the control
  • 14) You're already beating the Pros
  • 15) Feature 4: Cropping
  • 16) Bonus Feature: StickyMouse
  • 17a) Final Cleanup
  • 17b) Passing the current display image
  • 18) Making the application
  • 19) Source and result viewports
  • 20) A better toolbar
  • 21) Hooking the toolbar to the project
  • 22) Adding ImageEditors
  • 23) The toolbar ZoomCombo
  • 24) The final solution
  • 25) Saving to image files
  • 26) An integer-only textbox
  • 27) Passing save options between forms
  • 28) Dealing with that last exception
  • 29) Offer more options with menus
  • 30 The downloads and ebook


  • home     who is smith    contact smith     rss feed π
    Since 1997 a place for my stuff, and it if helps you too then all the better smithvoice.com