How to use .Net visual controls in PowerBuilder Classic
Using non-visual .Net classes in PowerBuilder Classic is somewhat straightforward. Provided the assembly (and the classes and methods within it) have been marked as COM Visible, you can run REGASM on them to create OLE registry entries and then use the class through OLE Automation. I have a video on the SAP D&T Academy that demonstrates the process.
Using Net visual controls takes a bit more work. There is an add-in for Visual Studio Professional called the Microsoft InteropForms Toolkit that essentially puts an ActiveX wrapper around a .Net visual user object that can contain one or more visual controls. Some observations:
The toolkit is an add-in for Visual Studio. As a result, it will only work in the Professional or higher versions of Visual Studio. The Express versions of VB6 Visual Studio don't support add ins. Apparently some people have found a way around the limitation, but doing so is beyond the scope of this article.
The toolkit only supports the VB.net language. If you are more comfortable with C#, there are third party utilities that extend the toolkit so that you can use C# instead. That will not be covered in this article, but you can seeInterop Forms Toolkit for C&#35; - Home for one example.
The toolkit exposes Windows Forms controls. However, you can add an ElementHost control to the toolkit control and then use that to host a WPF control. Once again, that is beyond the scope of this article.
I have a video on the SAP D&T Academy as well that covers this technique. This blog post will look at a different sample implementation than the one used in that video and will include the code used.
So, the first thing we're going to do is fire up Visual Studio.Net and create a new VB6 Interop UserControl.
Drag a PictureBox control over from the Toolbox onto the user object in the designer and then set the Dock property of the control to Fill. Since we've only got one control in this user object, this will ensure that the PictureBox will always fill the entire contents of the user control when it's resized.
In the code editor, we're now going to declare some instance variables in the user object that we will use to track a few properties we're interested in the
"VB6 Interop Code" Region, just after the "#If COM_INTEROP_ENABLED Then" declaration.
Private _CurrPage As Integer = 0 'defining the current page (its some sort of a counter)
Private _Opened As Boolean = False 'if an image was opened
Private _NumPages As Integer 'the number of pages in the tiff file
Private _FileName As String 'the name of the file that was opened
We want a couple of those to be read only properties as well for the control, so we add those into the "VB6 Properties" Region:
Public ReadOnly Property NumberOfPages() As Integer
Return _NumPages + 1
Public ReadOnly Property CurrentPageNumber() As Integer
Return _CurrPage + 1
The reason that we're adding 1 to each of these values before returning it is because the mechanism that tracks the page numbers internal to the control is zero index based (the first page is page 0). We need to adjust that before displaying it to the user who understands page numbers as one index based.
Finally, we're going to add some methods into the "VB6 Methods" Region, one of which is internal and the others which we'll be using from PowerBuilder to interact with the control.
The first method is the internal method that is used to refresh the image in the control when a TIF is first loaded or the user navigates to a different page:
Private Sub RefreshImage()
Dim myBmp As Image 'a new occurrence of Image for viewing
Dim myImg As Image 'setting the selected tiff
myImg = System.Drawing.Image.FromFile(_FileName) 'setting the image from a file
_NumPages = myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page) - 1 'the first page is0 so we must correct the number of pages to -1
myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, _CurrPage) 'going to the selected page
myBmp = New Bitmap(myImg, PictureBox1.Width, PictureBox1.Height) 'setting the new page as an image
'Description on Bitmap(SOURCE, X,Y)
PictureBox1.Image = myBmp 'showing the page in the pictureBox1
Recreating myImg an _NumPages each time the function is called is perhaps a bit of overkill, but I wasn't interesting in trying to refactor the code, just convert it for this sample.
The following function is used to pass a filename from PowerBuilder into the control so that the control will read it and display the first page:
Public Sub OpenFile(ByVal FileName As String)
_FileName = FileName
_CurrPage = 0 'reseting the counter
RefreshImage() 'refreshing and showing the new file
_Opened = True 'a file was opened.
This function is used to navigate forward one page in the TIF file:
Public Sub NextPage()
If (_Opened) Then 'the button works if the file is opened. you could go with button.enabled
If (_CurrPage = _NumPages) Then 'if you have reached the last page it ends here
'the "-1" should be there for normalizing the number of pages
_CurrPage = _NumPages
_CurrPage = _CurrPage + 1
And finally, this function is used to navigate back one page in the TIF file:
Public Sub PriorPage()
If (_opened) Then 'the button works if the file is opened. you could go with button.enabled
If (_CurrPage = 0) Then 'it stops here if you reached the bottom, the first page of the tiff
_CurrPage = 0
_CurrPage = _CurrPage - 1 'if its not the first page, then go to the previous page
RefreshImage() 'refresh the image on the selected page
That's a good start. The user object could obviously be embellished. For example, offering methods to navigate to a certain page directly, zoom in and out on the displayed image, print one or more pages of the image, etc. could all be added later.
Once you compile the project, Visual Studio.Net will create the registry entries that make the user object available as an ActiveX control. Open up PowerBuilder Classic, open a window control and start to insert an OLE Control onto the window. In the dialog that appears, select the third tab (Insert Control), and then scroll to the name of the control you created in Visual Studio.net. In my sample, I give it the rather unimaginative name of "TiffViewerControl.TiffViewerControl"
In the PowerBuilder window, I added buttons to open a TIF file and display it in the control, to move forward one page and to move backwards one page. I also have a static text field that display the current page number and number of pages in the document.
Note that the functions and properties of the user object do not display in the PowerBuilder IDE. You end up calling them as methods and properties of the ole control object attribute. PowerBuilder compiles that without question and only attempts to validate that the references are valid when the code is run.
The script in the button that opens a TIF file for display is as follows: