Make an Active X Control (Step by Step)
A step by step article on how to make an Active X control. In my eyes, I am not an expert but somebody asked me to write this (past comment on another submission) so I did. If you follow it, in the end you will have your own Custom PictureBox control that will have a property to assign a URL to an image to use for its picture along with an event to know when it completed its download. Hope you all enjoy. I did not realize it would take me so long to write.
Original Author: Clint LaFever
Code
How to build an Active X Control (Basic I cannot believe I am going to try to attempt to This tutorial is going to walk you through step Instructions assume you are using Visual Basic Open VB Choose to start a New Active X Control Project: Default Naming: Easy enough right. Ok, first things first, Now go up to your menu and Choose Project. Start of Coding: Ok, before we get started, Save your work Place a Picture Box on the UserControl (any where Double Click on an empty spot on the User Control. Private Sub This code make the picture box match the size of Note, instead of ME, you say UserControl when Now we need to code for when the user control Private Sub UserControl_Resize() Pretty much the same as before, but this just Private m_privateResize As Boolean Adding Events: Now lets just put in some basic events that our At the top of your code for the control in the Event Click() These are now events that you can raise later in Private Sub picBOX_Click() Pretty simple, really just raising our event when Now I want to add the code for when the control Go back to your UserControl_Resize code you typed Private Sub Properties: Now we need some basic properties, really the We are going to add the: Appearance, BackColor, BorderStyle, In your code type: Public Property Get Appearance() As Integer Ok, to explain all that, in general you just made Tweaking: Ok, with that added, we need to tweak a few Public Property Set Picture(ByVal New_Picture As Picture) There, that takes care of that. You may Custom Properties: Time to add our custom properties which makes our Const m_def_PictureURL = "" Now in code type: Public Property Get PictureURL() As String This is the statements to read and write to this However, now we need to go back to our Private Sub UserControl_ReadProperties(PropBag As PropertyBag) Note the last lines in each sub. We are Ok now, CLICK SAVE. You do not want to lose Finishing our Custom Properties: Ok, now we need to add the bit that gets an image Public Property Let PictureURL(ByVal New_PictureURL As String) This uses the AsyncRead method to get an image Ok almost done. The code above just starts Private Sub UserControl_AsyncReadComplete(AsyncProp As AsyncProperty) The user controls AsyncReadComplete event is Last Bit Of Code: Ok, we are pretty much done, but I think it would Event DownloadComplete() Then back in the "Private Sub Private Sub There, the code side of things is done. The reason for this is to make it that when you Finishing up: Now go back to your design view of your user Testing: Ok, time to test. Yeah. Don't close Now over in your project explorer tree right Rename the project to whatever you want. In the toolbox you should see either the image Presto, your control is on a form. Go ahead Yeah it does (at least for me). Over in the Then in the picture property go and browse for an Yeah, it worked and it resized right. Ok, now the real test. Remove the image Option Explicit Run your test project. Click on your control Did it download the image. Did you get all I did. yeah. Code Listing Reference: Ok, here at the end is a listing of all the code WebPictureBox (Code) Option Explicit Event Click() Event DblClick() Event MouseDown(Button As Integer, Shift As Integer, X As Event MouseMove(Button As Integer, Shift As Integer, X As Event MouseUp(Button As Integer, Shift As Integer, X As Single, Event Resize() Event DownloadComplete() Const m_def_PictureURL = "" Private m_PictureURL As String Private m_privateResize As Boolean Private Sub picBOX_Click() RaiseEvent Click End Sub Private Sub picBOX_DblClick() RaiseEvent DblClick End Sub Private Sub picBOX_MouseDown(Button As Integer, Shift As RaiseEvent MouseDown(Button, Shift, X, Y) End Sub Private Sub picBOX_MouseMove(Button As Integer, Shift As RaiseEvent MouseMove(Button, Shift, X, Y) End Sub Private Sub picBOX_MouseUp(Button As Integer, Shift As Integer, RaiseEvent MouseUp(Button, Shift, X, Y) End Sub Private Sub UserControl_Initialize() With UserControl .picBOX.Move 0, 0, .ScaleWidth, End With End Sub Private Sub UserControl_Resize() If m_privateResize = False Then With UserControl End With End If RaiseEvent Resize End Sub Public Property Get Appearance() As Integer Appearance = picBOX.Appearance End Property Public Property Let Appearance(ByVal New_Appearance As Integer) picBOX.Appearance() = New_Appearance PropertyChanged "Appearance" End Property Public Property Get BackColor() As OLE_COLOR BackColor = picBOX.BackColor End Property Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR) picBOX.BackColor() = New_BackColor PropertyChanged "BackColor" End Property Public Property Get BorderStyle() As Integer BorderStyle = picBOX.BorderStyle End Property Public Property Let BorderStyle(ByVal New_BorderStyle As picBOX.BorderStyle() = New_BorderStyle PropertyChanged "BorderStyle" End Property Public Property Get AutoRedraw() As Boolean AutoRedraw = picBOX.AutoRedraw End Property Public Property Let AutoRedraw(ByVal New_AutoRedraw As Boolean) picBOX.AutoRedraw() = New_AutoRedraw PropertyChanged "AutoRedraw" End Property Public Property Get AutoSize() As Boolean AutoSize = picBOX.AutoSize End Property Public Property Let AutoSize(ByVal New_AutoSize As Boolean) picBOX.AutoSize() = New_AutoSize PropertyChanged "AutoSize" End Property Public Property Get Picture() As Picture Set Picture = picBOX.Picture End Property Public Property Set Picture(ByVal New_Picture As Picture) Set picBOX.Picture = New_Picture If Me.AutoSize = True Then With UserControl End With End If PropertyChanged "Picture" End Property Private Sub UserControl_ReadProperties(PropBag As PropertyBag) picBOX.Appearance = PropBag.ReadProperty("Appearance", picBOX.BackColor = PropBag.ReadProperty("BackColor", picBOX.BorderStyle = PropBag.ReadProperty("BorderStyle", picBOX.AutoRedraw = PropBag.ReadProperty("AutoRedraw", picBOX.AutoSize = PropBag.ReadProperty("AutoSize", Set Picture = PropBag.ReadProperty("Picture", m_PictureURL = PropBag.ReadProperty("PictureURL", End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) Call PropBag.WriteProperty("Appearance", Call PropBag.WriteProperty("BackColor", Call PropBag.WriteProperty("BorderStyle", Call PropBag.WriteProperty("AutoRedraw", Call PropBag.WriteProperty("AutoSize", Call PropBag.WriteProperty("Picture", Call PropBag.WriteProperty("PictureURL", End Sub Public Property Get PictureURL() As String PictureURL = m_PictureURL End Property Public Property Let PictureURL(ByVal New_PictureURL As String) m_PictureURL = New_PictureURL If (New_PictureURL <> "") AsyncRead End If PropertyChanged "PictureURL" End Property Private Sub UserControl_InitProperties() m_PictureURL = m_def_PictureURL End Sub Private Sub UserControl_AsyncReadComplete(AsyncProp As On Error Resume Next Select Case AsyncProp.PropertyName Case "PictureURL" Case Else End Select RaiseEvent DownloadComplete End Sub I hope this all works out. I also hope you Feel free to email if you have any questions: -Clint LaFever
Tutorial)
teach since I never thought of myself as a teacher, but in one of my previous
postings, a comment asked if I could post a tutorial on how to make an Active X
Control because that person liked the way I explain things. So here I
go. I would like to request that if I mistake anything or call something
by it's wrong name that you do not flame me. Feel free to comment and let
others know of my mistake, but please, be nice :) Ok, here goes.
by step of how to create a new PictureBox control that will have a new property
to supply a URL to an image on the web to use as it's picture (without the use
of Winsock or Internet controls). I think everyone would find a use for
this type of control.
6.0
we need to name a few things and set some project properties. Click on the
Project Explorer Tree on the Project itself (PROJECT1) and then down in the
properties window, rename it to: WEBPIC. This name is going to become the
name of your OCX (duh). Then click on the user control branch and rename
it to: WebPictureBox. This name is the name it will be known as inside of
VB (the tool tip on the tool when you put it in your available components later
on for new projects that use it)
Then Choose WebPic Properties. In Project Description enter Web Picture
Box. This is the name it will be listed as when you pull up the list of
available controls to add to a project. You want to keep it english like
so you can tell what it is. I hate those who make controls bet never set
this and then it will default to the name of the OCX which most of the time is
some abbreviated name that does not make too much sense when you are just
skimming though. Anyhow, that is a different story. Go ahead and set
all the other properties you want about the project, Company, Copyright
etc. I personally like to set auto increment on the version tab.
Click Ok.
(however you like to save where ever you want)
you like, code with handle it's position later). Name it: picBOX.
This should take you to UserControl_Initialize(). In that Sub type:
UserControl_Initialize()
With UserControl
.picBOX.Move 0, 0, .ScaleWidth, .ScaleHeight
End With
End Sub
the control when it is first placed on a form later.
referring to your object. Me refers it to its exposed methods and
properties that we will put in soon.
gets resized. Go to the Resize Event for the UserControl and type:
If m_privateResize = False Then
With UserControl
.picBOX.Move 0, 0, .ScaleWidth, .ScaleHeight
End With
End If
End Sub
keeps the picture box the same size as the user control always. However, I
added a bit to check to see if code told it to resize or did the user do
it. Later down I have code resizing the control and I don't want this to
fire. Up in the General Declarations you need to defind m_privateResize as
Boolean, up top type:
control will have. You can add more later after you see how this is
done. I am going to add the Click, DblClick, MouseUp, MouseMove, MouseDown,
and Resize events to my control.
General Declarations section type:
Event DblClick()
Event MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Event MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Event MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Event Resize()
your code. Which we will program now. Pretty much, we want to say in
our control when somebody clicks on the picture box (or moves etc) we want to
raise those events out of our control. So in your code type:
RaiseEvent Click
End Sub
Private Sub picBOX_DblClick()
RaiseEvent DblClick
End Sub
Private Sub picBOX_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
RaiseEvent MouseDown(Button, Shift, X, Y)
End Sub
Private Sub picBOX_MouseMove(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
RaiseEvent MouseMove(Button, Shift, X, Y)
End Sub
Private Sub picBOX_MouseUp(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
RaiseEvent MouseUp(Button, Shift, X, Y)
End Sub
the corresponding events occur within our control.
itself gets resized.
earlier and add the RaiseEvent Resize line so it look like this:
UserControl_Resize()
If m_privateResize = False Then
With UserControl
.picBOX.Move 0, 0,
.ScaleWidth, .ScaleHeight
End With
End If
RaiseEvent Resize
End Sub
same ones as the picture box, I will skip some just to keep this quick.
AutoRedraw, AutoSize, and Picture property to our control.
Appearance = picBOX.Appearance
End Property
Public Property Let Appearance(ByVal New_Appearance As Integer)
picBOX.Appearance() = New_Appearance
PropertyChanged "Appearance"
End Property
Public Property Get BackColor() As OLE_COLOR
BackColor = picBOX.BackColor
End Property
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
picBOX.BackColor() = New_BackColor
PropertyChanged "BackColor"
End Property
Public Property Get BorderStyle() As Integer
BorderStyle = picBOX.BorderStyle
End Property
Public Property Let BorderStyle(ByVal New_BorderStyle As Integer)
picBOX.BorderStyle() = New_BorderStyle
PropertyChanged "BorderStyle"
End Property
Public Property Get AutoRedraw() As Boolean
AutoRedraw = picBOX.AutoRedraw
End Property
Public Property Let AutoRedraw(ByVal New_AutoRedraw As Boolean)
picBOX.AutoRedraw() = New_AutoRedraw
PropertyChanged "AutoRedraw"
End Property
Public Property Get AutoSize() As Boolean
AutoSize = picBOX.AutoSize
End Property
Public Property Let AutoSize(ByVal New_AutoSize As Boolean)
picBOX.AutoSize() = New_AutoSize
PropertyChanged "AutoSize"
End Property
Public Property Get Picture() As Picture
Set Picture = picBOX.Picture
End Property
Public Property Set Picture(ByVal New_Picture As Picture)
Set picBOX.Picture = New_Picture
PropertyChanged "Picture"
End Property
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
picBOX.Appearance = PropBag.ReadProperty("Appearance", 1)
picBOX.BackColor = PropBag.ReadProperty("BackColor", &H8000000F)
picBOX.BorderStyle = PropBag.ReadProperty("BorderStyle", 1)
picBOX.AutoRedraw = PropBag.ReadProperty("AutoRedraw", False)
picBOX.AutoSize = PropBag.ReadProperty("AutoSize", False)
Set Picture = PropBag.ReadProperty("Picture", Nothing)
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Call PropBag.WriteProperty("Appearance", picBOX.Appearance, 1)
Call PropBag.WriteProperty("BackColor", picBOX.BackColor, &H8000000F)
Call PropBag.WriteProperty("BorderStyle", picBOX.BorderStyle, 1)
Call PropBag.WriteProperty("AutoRedraw", picBOX.AutoRedraw, False)
Call PropBag.WriteProperty("AutoSize", picBOX.AutoSize, False)
Call PropBag.WriteProperty("Picture", Picture, Nothing)
End Sub
properties to your control that when they get set,
will then in turn set properties of the picture box inside of your
control. The ReadProperties and WriteProperties as subs that will save
these properties to the property bag of the control so it remembers what you set
even after you close. As you typed those lines (if you did not copy/paste)
then you would have noticed what each of those arguments
are, Name, Value, Default. Actually pretty
easy to understand I think.
things now. One thing that stands out is the AutoResize Event. Right
now our control is coded that if the developer resizes the user control, we
resize the picture box to fit. But what happens when AutoSize is set to
true and a new picture gets assigned. The picture box will change
size. Therefore, we need to code to make the usercontrol match back to the
size of the new picture loaded. So, in the "Public Property Set
Picture" sub, we need to add some code to make it look like this:
Set picBOX.Picture = New_Picture
If Me.AutoSize = True Then
With UserControl
m_privateResize = True
.Width = .picBOX.Width
.Height = .picBOX.Height
m_privateResize = False
End With
End If
PropertyChanged "Picture"
End Property
find other areas to tweak, but I am just building this the same time I am typing
so I have not thought of any yet.
new version of a picture box different from the default one. We need a new
property named PictureURL that will contain a string to a fully qualified URL to
an image on the web. Because this property does not correspond back to
some other property already of the picture box, we need a place to store it when
it gets set. So up in the General Declarations section type:
Private m_PictureURL As String
PictureURL = m_PictureURL
End Property
Public Property Let PictureURL(ByVal New_PictureURL As String)
m_PictureURL = New_PictureURL
PropertyChanged "PictureURL"
End Property
Private Sub UserControl_InitProperties()
m_PictureURL = m_def_PictureURL
End Sub
property. It will save and read from the m_PictureURL variable we defined
above and use the m_def_PictureURL constant as default the first time this
control is initialized.
ReadProperties and WriteProperties to make sure we tell the property bag to
remember what ever gets set here in design time.
picBOX.Appearance = PropBag.ReadProperty("Appearance", 1)
picBOX.BackColor = PropBag.ReadProperty("BackColor", &H8000000F)
picBOX.BorderStyle = PropBag.ReadProperty("BorderStyle", 1)
picBOX.AutoRedraw = PropBag.ReadProperty("AutoRedraw", False)
picBOX.AutoSize = PropBag.ReadProperty("AutoSize", False)
Set Picture = PropBag.ReadProperty("Picture", Nothing)
m_PictureURL = PropBag.ReadProperty("PictureURL", m_def_PictureURL)
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Call PropBag.WriteProperty("Appearance", picBOX.Appearance, 1)
Call PropBag.WriteProperty("BackColor", picBOX.BackColor, &H8000000F)
Call PropBag.WriteProperty("BorderStyle", picBOX.BorderStyle, 1)
Call PropBag.WriteProperty("AutoRedraw", picBOX.AutoRedraw, False)
Call PropBag.WriteProperty("AutoSize", picBOX.AutoSize, False)
Call PropBag.WriteProperty("Picture", Picture, Nothing)
Call PropBag.WriteProperty("PictureURL", m_PictureURL, m_def_PictureURL)
End Sub
saving and reading the private variable we defined and storing that. Once
again, the property bag is what remembers what you set in the property window
when you design a form and place controls on it. If you do not use the
property bag, no matter what you set on the property window later will never be
saved.
what you have done so far.
from the web. We need to go back to our "Public Property Let PictureURL"
sub and add some code. This code will get the image from the web for
us. Type to make our "Public Property Let PictureURL" sub look
like this:
m_PictureURL = New_PictureURL
If (New_PictureURL <> "") Then
AsyncRead m_PictureURL, vbAsyncTypePicture, "PictureURL", vbAsyncReadForceUpdate
End If
PropertyChanged "PictureURL"
End Property
from the web. Pretty simple huh :) The vbAsyncReadForceUpdate
argument tells the AsyncRead to always get the picture from the web and ignore
any cached copy. Maybe later you can change this and provide some new
property to have this as a setting. (nice upgrade to practice with)
the download. Now we need to get the picture when it is done. For
this we use the AsyncReadComplete event of our user control. Go ahead and
type:
On Error Resume Next
Select Case AsyncProp.PropertyName
Case "PictureURL"
Set Me.Picture = AsyncProp.Value
Case Else
End Select
End Sub
fired when the download is done. So we ready the AsyncProp object to
determine what was just downloaded. In the earlier code when we started
the download, we supplied a name "PictureURL" as the name of the
download (not the file name, but the name associated with the download.
Just like you name controls when you code). We check to see if this
downloaded file is the one we requested, if it is, assign it to the picture
property of the picture box. It is written this way to help you see that
you can add more capability to this if you wish and provide multiple downloads
and what not.
be nice to add an event to our control to tell the user/developer using it, that
the download is done. So back up in the General Declarations type:
UserControl_AsyncReadComplete" sub, add a line to make it look like this:
UserControl_AsyncReadComplete(AsyncProp As AsyncProperty)
On Error Resume Next
Select Case AsyncProp.PropertyName
Case "PictureURL"
Set Me.Picture = AsyncProp.Value
Case Else
End Select
RaiseEvent DownloadComplete
End Sub
Click Save. Ok, go for the first compile. Fix any typos and try to
compile again until you get a good compile. Now go back to your menu,
choose Project, Choose WebPic Properties. Click the component tab.
Turn on the option for Binary Compatibility (it should be defaulted pointing to
the ocx you just made)
make changes to your control it will make it so programs already compiled with
earlier versions of your control will still work. However, depending on
your changes, it may warn you that you are breaking compatibility. If you
break it, you should consider compiling under a new name if other programs exist
using your older version that are already compiled and released. If there
are no other programs released, you can break it and then try not to
again. Breakage occurs when you alter the declaration of an exposed method
or property that already existed in the older version. For example, if you
right now have the PictureURL property but later decided to call it just URL, it
will break compatibility. But if you add new properties or methods, or
just alter code inside existing functions or subs, it will not break.
control. I suggest sizing the user control down to a better size.
Remember, the size you set here will become the default size of the control when
it later gets placed on a form. Also, for the properties of the user
control, there is a property named: ToolBoxBitmap. Here is where you
assign an image to use as the image that will appear in the toolbox later
on. For best results make a BMP 16x15. Note it will attempt to read
(I believe the bottom left pixel, or top left I forget) to determine what color
it will use as its transparent color. I normally just keep a one pixel
border around the image I make and have the background color set to LIME green
or something to stay out of trouble. Feel free to make it what ever you
want or you can do it later.
this project but I do suggest closing all design and code windows of the control
(in your playing around later you will find out why), just go up to File, then
choose Add Project. Choose Standard EXE. Click Ok.
click on the project that just got added and choose Set As Start Up.
Like WebPicTest. Then go to the form and rename it to something like
frmTEST.
you made for your control, or the default generic image if you did not. If
you cannot tell, just mouse move over the controls listed as the bottom until
the tooltip of one reads the name of your control. Go ahead and click it
and add it to your form.
and resize it a bit to make sure our resize code works.
properties for it, check AutoResize to true.
image from your hard drive to test the Picture Property.
from the Picture Property. What we coded really is setup for us to use
either Picutre, or PictureURL but not really both, it won't crash, but just adds
a little confusion. Anyhow, delete the previous image from the picture
property then in the PictureURL property type: http://microsoft.com/library/homepage/images/init_windows.gif
and press enter. If you left the other picture there, all that would
happen is the new web downloaded image would replace it. Ok, time for
testing of the events and calling properties in code. First lets delete
what we typed in the PictureURL property. Then for the Picture Property go
ahead and browse and choose an image from your hard drive. The on the code
for this test form have it say:
Private Sub WebPictureBox1_Click()
Me.WebPictureBox1.PictureURL = _
"http://microsoft.com/library/homepage/images/init_windows.gif"
Debug.Print "Click"
End Sub
Private Sub WebPictureBox1_DblClick()
Debug.Print "DblClick"
End Sub
Private Sub WebPictureBox1_DownloadComplete()
Debug.Print "Download Complete"
End Sub
Private Sub WebPictureBox1_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Debug.Print "MouseDown"
End Sub
Private Sub WebPictureBox1_MouseMove(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Me.Caption = X & " : " & Y
End Sub
Private Sub WebPictureBox1_MouseUp(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Debug.Print "MouseUp"
End Sub
Private Sub WebPictureBox1_Resize()
Debug.Print "Resize"
End Sub
your debug.prints?
for the user control so you can just copy and paste from here if you had any
problems:
Single, Y As Single)
Single, Y As Single)
Y As Single)
Integer, X As Single, Y As Single)
Integer, X As Single, Y As Single)
X As Single, Y As Single)
.ScaleHeight
.picBOX.Move 0, 0, .ScaleWidth, .ScaleHeight
Integer)
m_privateResize = True
.Width = .picBOX.Width
.Height = .picBOX.Height
m_privateResize = False
1)
&H8000000F)
1)
False)
False)
Nothing)
m_def_PictureURL)
picBOX.Appearance, 1)
picBOX.BackColor, &H8000000F)
picBOX.BorderStyle, 1)
picBOX.AutoRedraw, False)
picBOX.AutoSize, False)
Picture, Nothing)
m_PictureURL, m_def_PictureURL)
Then
m_PictureURL, vbAsyncTypePicture, "PictureURL", vbAsyncReadForceUpdate
AsyncProperty)
Set Me.Picture = AsyncProp.Value
learned something. At least you got a new control. One final thing I
would do is back in the WebPic project. I would open the Object Browser,
then for each of the properties of our new control, define tips to display in
the property window (the area on the bottom of the property window that tells
you what a property does) and also pick what event I would want as my default
event. While this option is nice and make a control more professional, it
is just too much to explain here and requires another lesson.
http://lafever.iscool.net or http://vbaisc.iscool.net
Loading Comments ...
Comments
No comments have been added for this post.
You must be logged in to make a comment.