http://www.developer.com/

Back to article

ActiveX Control Tutorial - Part 3


November 20, 2002

Guten Morgan and welcome to the third part of this surprisingly super ActiveX control tutorial.

If you've missed the previous two instalments, be sure to check out part one here and part two here.

As ever, I'm your host Karl Moore and this week we'll continue to chip away at the Super Cool Text Box project we started a couple of weeks ago.

We'll be:

  • Adding a chunk of code to make our text box work
  • Discussing how to add events to your code
  • Discussing how to add events to your diary
  • Getting a Wizard to type code for us

So mount the stallion they call VeeBee — it's time to ride off into the glorious, 24-bit pixelised Microsoft sunset...

So far, we've created the skeleton for our new and improved Text Box. We've added a couple of properties, defined enumerations and handled resizing.

Err, but it doesn't exactly work yet. In fact, I saw more work emitting from Queen Elizabeth's left eyebrow, the last time she ordered, "Take over the fort Charlie, I'm off for an all-expenses-paid weekend in Tunisia".

Hmm, think about it. Even if the AcceptType was set to 'Letters', the end user can still enter anything into the Text Box. But hey-ho, in this section, we're going to change all that.

  • Open last week's project
  • Enter the code window of our control
  • Select our Text Box from the Object drop-down list
  • Select the Keypress event from the Procedure drop-down list



Click here for larger image

The Keypress event fires whenever the user types something in the Text Box. An argument is passed with the event — KeyAscii.

Top Tips: KeyAscii is a number that represents a key on the keyboard. To convert KeyAscii to its keyboard letter, use Chr(KeyAscii). To convert a letter to its KeyAscii code, use Asc(Letter). Setting KeyAscii = 0 in the KeyPress event cancels the character.

  • Add the following code to the txtTextBox_KeyPress event:
Private Sub txtTextBox_KeyPress(KeyAscii As Integer)Select Case AcceptType

' Check out the AcceptType propertyCase Numbers' If only accepting numbers, check...If IsNumeric(Chr(KeyAscii)) = False Then' If the passed KeyAscii keyboard equivalent' is NOT numeric, then cancel the character!KeyAscii = 0BeepExit SubEnd If Case Letters' If only accepting letters, check...If Not (KeyAscii > 64 And KeyAscii < 91) And _Not (KeyAscii > 96 And KeyAscii < 123) Then' If the KeyAscii number isn't greater than' 64 and less than 91 (upper case characters)' and it isn't greater than 96 and less than' 123 (lower case characters), then cancel' the character!KeyAscii = 0BeepExit SubEnd IfEnd SelectSelect Case ConvertCase' Check out the ConvertCase propertyCase UpperCase' If text needs to be in upper case,' change the KeyAscii value to' the Ascii code of an upper case' version of the KeyAscii {phew!}' DON'T FORGET - Asc() changes a' letter to it's Ascii value, UCase()' changes a letter to upper case,' Chr() changes an Ascii character to' a normal letter. So here, we're converting' the Ascii to a normal letter, making it' upper case then changing it back again!KeyAscii = Asc(UCase(Chr(KeyAscii)))Case LowerCase' Errm... opposite of the above ;-)KeyAscii = Asc(LCase(Chr(KeyAscii)))End SelectEnd Sub

By observing the comments, you should be able to understand this code. Don't worry if it's seems slightly alien, after all, it was written by me, Karl Moore, distant son of Phwasack "Area 51" Carstolemew and third supreme commander of the planet Clot.

Just bare in mind that you don't need to know about 'KeyAscii codes' in order to create most ActiveX controls. Except this one.

In our control, it's the above chunk of code that does all the work - checks the properties, ensures characters are upper case or lower case, and beeps if a character is invalid.

Try testing our code. Add a new test 'Standard EXE' project and throw our control onto a Form.

Don't set the properties at design time — set them in code behind a Command Button. Then run your application, press the Command Button and attempt to type a few invalid characters.

Does the control work?

<Karl sniggers as he sinfully realises this is a one-way medium>

Note: If you set the control properties at design time, they will disappear when you run the application. That's because we haven't told Visual Basic to save that information. You do this via Property Bags. Remember that — it'll make sense later.

And Another Note: Can you spot the bug? What happens when you choose the 'Letters' option and tap the spacebar? What about the backspace? Can you fix this?

So you've created your own control with two easy-to-use properties. You've even put those properties to use via a chunk of code in the KeyPress event of the text box.

Now it's time for a nerdy developer to test your control.

You stick him at your desk and 2.4 seconds later he feebly complains, "But how can I read or write stuff inside the text box?"

"Damn", comes your reply. "You noticed"

If using a normal text box, you could retrieve the value by running the code...

MsgBox Text1.Text

... but unfortunately your control doesn't have a text property. However our actual Text Box (txtTextBox) does.

'Cause you're a super-intelligent ActiveX boffin now, you could easily implement such functionality by adding code similar to the following...

Public Property Get Text() As StringText = txtTextBox.TextEnd PropertyPublic Property Let Text(ByVal NewText As String)    txtTextBox.Text = NewTextEnd Property

Do you understand what is happening here? You're just passing the Text property of the Text Box around through Property procedures.

But what if users requested all the other usual properties of a Text Box control? Alignment, Appearance, BackColor, BorderStyle, Font and ForeColor - to name just a few.

That's a lot of monotonous coding. And what if they also wanted to respond to certain events, such as the KeyPress and Click events of your Text Box?

Well, we've not really mentioned events yet so let's discuss them now.

Most controls have at least a few events related with them. The Text Box control has a KeyPress event, as we saw earlier today. The Command Button has a Click event. The Scroll Bar has a Change event. And so on.

When the Text Box or Command Button decides it should fire the event well, it does. And that means any code you have written behind the event runs. After your code has finished, control is then returned to the control. Confused? You should be, but please stick with it.

Now, you can create your own custom control events by adding a simple statement to the General Declarations section of your code, like this:

Public Event Click()

or...

Public Event DocumentProcessed(DocumentID as Integer)

Simple enough. And then you could raise the event to the developer using your control by adding a line like this to your code:

RaiseEvent Click

or...

RaiseEvent DocumentProcessed(5623)

Note: Notice how that last event passes an argument, the DocumentID.

Here's a quick diagram showing the course of events (pun most certainly intended):


Blow Up!

Not too difficult, eh?

So let's recall our original problem what if our end user wanted to respond to events of the Text Box? Say, the Click event?

Well, we could create our own custom events identical to those of the Text Box. Then, when those Text Box events fire, we could raise our own events to the end user.

Let's see an example of how we'd do this with the Click event:

Public Event Click()Private Sub txtTextBox_Click()    RaiseEvent ClickEnd Sub

All we're doing here is every time the Click event of our Text Box fires, we're just passing the message to our end user via our own control's Click event.

But writing all of that code for every single Text Box event would become just as boring and error-prone as it would be to write similar code for every single Text Box property - so boring in fact, that only Train Spotters International ever tried it. Unfortunately they all kicked the bucket before the project was completed. Old age, you know.

So is there an alternative to rid of writing all these monotonous properties, events or even methods? You bet your bottom dollar there is and it's all hidden behind an innocent-looking Microsoft wizard...

Thankfully whilst beta-testing Visual Basic 5/6, Billionaire Bill and his gang of merry programmers decided that making developers type line after line of such repetitive code was rather cruel. And also dead boring.

So one of the head nerds played around to produce a magical Microsoft wizard that can automatically produce run-of-the-mill code and rid of all your worries. Well, maybe not all your worries.

Anyway, interested? Let's load up the Wizard...

  • Click Add-Ins, Add-In Manager
  • Double-click on the 'VB 5/6 ActiveX Ctrl Interface Wizard' until it's Load Behaviour changes to 'Loaded'
  • Click Add-Ins, ActiveX Control Interface Wizard
  • If you're presented with a welcome screen, click Next



Click here for larger image

The current screen should ask you to select the 'interfaces' you want Visual Basic to automatically 'code' for you.

Interfaces? Yeah, that's just a posh word to describe what stuff your control shows (or 'exposes') to the developer. And 'stuff' is a technical term defined as properties, methods and events.

So - the list box to the left of your screen displays this list of suggested interface members, such as the Font property and the Click event.

Note: These suggested interfaces have been determined by the existing properties, methods and events of controls you currently have in your workspace. At the moment, we just have a Text Box so it's suggesting it should code all the 'stuff' related to a Text Box, such as the Font property.

  • Move a few of the interface members across to the second box:
  • -- BackColor, BorderStyle, Click, DblClick, Font, FontBold, FontItalic, FontUnderline, PasswordChar, Refresh, Text
  • Click Next

If you wanted to add any extra properties, methods or events the current screen would be the place to do it. But we don't, so...

  • Click Next

Now we're going to 'map' all the properties, methods and events we selected direct to our Text Box. This lets the wizard know that our custom Click event, for example, should be fired when the Click event of the Text Box is activated. And the custom BackColor property should pass its value direct to the BackColor property of the Text Box. And so on and so forth.

This is called 'mapping'.

  • Select all the interface members in the list box (hold your left mouse button down and drag along the list)
  • From the 'Maps to Control' list, select your Text Box
  • Click Next

If you added any properties, methods or events that don't directly 'map' to anything - this is where you give the wizard a little more information, allowing it to create a basic skeletal outline procedure for you. But we haven't, so we won't.

  • Click Next
  • Click Finish

Hold on, Max... err, what's happened to all our beautiful code?

So the wizard has finished whirring and you're sitting there, staring in astonishment/disbelief/annoyance.

Let's take a look at an example of the wizard's work:

'WARNING! DO NOT REMOVE OR MODIFY'THE FOLLOWING COMMENTED LINES!'MappingInfo=txtTextBox,txtTextBox,-1,TextPublic Property Get Text() As String    Text = txtTextBox.TextEnd PropertyPublic Property Let Text(ByVal New_Text As String)    txtTextBox.Text() = New_Text    PropertyChanged "Text"End Property

Hmm, nothing too complex there. In fact, it's almost exactly as we anticipated, except for the 'PropertyChanged' line, but we'll explain that shortly.

Hold on... have you noticed yet? The wizard has commented out the AcceptType and ConvertCase properties you worked so hard to create!

Oh, wait a minute - yep, it's OK - the Wizard has replaced them with almost identical procedures - except the Let procedures now have a 'PropertyChanged' line in them.

Top Tip: As the wizard has now commented out your two procedures, you no longer need the m_CharType and m_CaseType variables you created. The wizard has created it's own variables to hold the values of the AcceptType and ConvertCase properties the more sensibly christened m_AcceptType and m_ConvertCase

What else has that darn wizard done? Oh, it seems there's now code in the WriteProperties and ReadProperties events of the UserControl object. And that code babbles on about 'ReadProperty' and 'WriteProperty'.

Thankfully the code still works, no problems. But tell me Joe, darn it, what's the story with all these here Property thangs?

Well, remember what I mentioned earlier? If you change the properties of your control at design time, then run your project those changes disappear faster than Hitler's wig on a windy day.

That's 'cause we've never told Visual Basic to save them. However the Wizard has now added all this Read/Write Property code that does just that saves or 'persists' our properties.

But that's enough on that topic for now. Go have a good ol' rest and join us next week for the full, feature-length nail-biting explanation!

<Ed: What a cliff hanger!>

This week we added code to make our Super Cool Text Box control work. We then discussed events and how to raise them within your control.

We finished off by checking out the neat ActiveX control wizard and how it can make mundane, error-prone coding a task of the past.

Perhaps we haven't had as much 'hands-on' today as previous weeks but don't worry, you've just about completed your control and learned a lot of important lessons along the way.

And that means you're getting closer to becoming an ActiveX Wizard! And I'm not talking about the Microsoft type.

Hey - Well Done!

Now for a little homework. Bear in mind that you read a property via the Get statement and write to it via the Let statement. What could you do to make a property read-only or write-only? And why would you want to do that?

Incidentally, "Because you suggested it" is not a valid answer.

First chap to post the correct answer on the bulletin board wins a three-year old Mars bar.

Before the next tutorial, it'd also be good practice to play around with events. Try raising your own and then responding to them in code. You can even add extra events to our Super Cool Text Box control, if you wish.

Next week, we'll unravel the mystery of the wizard's weird property code and take a geek peek at Property Bags, which incidentally are nothing to do with shopping bags, leather bags or indeed, baggy bags.

But until then, this is your host Karl Moore signing off for tonight. Goodnight!

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date