Creating the Components
The first thing to do is add the data components to form. Add an SqlConnection, an SqlDataAdapter, and a DataSet. Cancel out of the wizard for the SqlDataAdapter. When the DataSet configuration wizard appears, select untyped DataSet and click OK. Name the SqlConnection mConnection, the SqlDataAdapter mAdapter, and the DataSet mDataset. Close the Designer window and open the Code window.
Now it's time to start writing code. The first bit of functionality to add is the ability to connect to a database. We will use our Connection object for this. First create a property to expose the Connection object to the world. This will have an interesting effect we will look at later on:
Public Property Connection() As SqlClient.SqlConnection Get Connection = mConnection End Get Set(ByVal Value As SqlClient.SqlConnection) mConnection = Value End Set End Property
Next create a separate property to get and set a ConnectionString. This is really a convenience feature. You'll see why in a minute. Add the following property:
Public Property ConnectionString() As String Get If Not mConnection Is Nothing Then ConnectionString = mConnection.ConnectionString End If End Get Set(ByVal Value As String) mstrConn = Value If mstrConn = mConnection.ConnectionString Then Exit Property If mConnection.State = ConnectionState.Open Then _ mConnection.Close() mConnection.ConnectionString = mstrConn End Set End Property
The way this works is simple. If you change the ConnectionString, the control closes any open connection and assigns the value to the ConnectionString property of the Connection object. Now compile the project. Open frmCtlTest. Select the data navigator control. Look at the Properties window. Scroll down until you see the Connection property. Click the plus sign. Look at what it did! It created a Connection property that expands to reveal all of the properties of the Connection object. Try doing that with your VB 6. With this functionality the users of your control can assign their own Connection object to the control, or just use the built-in one. See Figure 11.3.
Figure 11.3. The Connection property.
Now let's assign a ConnectionString to the control. But, who can remember all the arcane syntax of a ConnectionString? I have been programming in VB since VB 1 and I still have to look some of them up. Not to worry: You can have the Connection object generate the string for you. First create a user connection. Hover the mouse pointer over the Server Explorer. It should roll out. If you have a slower machine it might take a few seconds if this is the first time you've accessed it, because it has to refresh all of its connections. If you don't have it rolled up, then open it from the toolbar. Next click the Connect to Database button. The familiar ADO Database Connection dialog appears. Connect to the Northwind database. Figure 11.4 shows how mine looked .
Figure 11.4. Selecting the Data Connections.
Now go to the Properties page and click on the ConnectionString property of the Connection object. Do not click on the one we added. In the drop-down the list, you will see the connections we added. Select the Northwind one. Both ConnectionString properties are now populated with correctly formed ConnectionStrings. Note that we did all of this just by exposing the Connection object through a simple property. Now add properties for the DataAdapter and the DataSet. Your code should look like this:
Public Property DataAdapter() As SqlClient.SqlDataAdapter Get DataAdapter = mAdapter End Get Set(ByVal Value As SqlClient.SqlDataAdapter) mAdapter = Value End Set End Property Public Property DataSet() As DataSet Get DataSet = mDataSet End Get Set(ByVal Value As DataSet) mDataSet = Value End Set End Property
Pretty straightforward, eh? Now we can see all of the properties of the adapter and the DataSet in the Properties windowonce we've compiled the control. This is the easy part. The more difficult tasks will follow.
Let's take a break from the database programming to take care of one small detail. We want to add a Text property to our control so we can update the caption between the navigation buttons . You'll notice that the UserControl has a Text property that we can override. Click Overrides in the component list at the top of the Code window, then drop down the procedures list to see it. Select the Text property. The Code window creates the procedure stub for you. Make the procedure look like this:
Public Overrides Property Text() As String Get Text = lblText.Text End Get Set(ByVal Value As String) lblText.Text = Value End Set End Property
This will assign the string to the Text property of the label control. Now compile the project. Open the form designer for frmCtlHost again. Select the data navigator control. Look at the Properties window. There is no Text property! What gives?
The Text property of the UserControl base class has an attribute that prevents the property from displaying in the Properties window. Since we overrode this property, our class inherited the attribute. What is an attribute? An attribute is a class object that you can attach to a class, procedure, property, or member that affects how that object behaves. You can, obviously, do many things with attributes. In this case the attribute controls the design-time behavior of the property. If you open the object browser and select the Text property of the UserControl, you will see its attributes listed. One of them is System.ComponentModel.BrowsableAttribute. It is set to False. We have to override this behavior, so add the following line (in bold) just before the property procedure:
<System.ComponentModel.BrowsableAttribute(True)> _ Public Overrides Property Text() As String Get Text = lblText.Text End Get Set(ByVal Value As String) lblText.Text = Value End Set End Property
Now compile the project. Switch back to frmCtlTest and select the navigator. You will now see the Text property in the Properties window.
How did I know this? By digging until I found it. I can't find a list of predefined attributes listed anywhere in the VB or .NET documentation. They are all documented, but not very usefully. For example, a list of attributes that can be applied to properties would be nice.
VB 6 VB 6 had a menu item called Procedure Attributes in the Tools menu. This is where you found the capability to control things like appearing in the Properties window and whether or not a property was bindable. Interestingly, VB 6 also supported attributes but in a different way. Since VB 6 Forms package objects were private and pre-declared, the attributes were expressed differently. If you ever opened a VB 6 form or UserControl using Notepad (or another text editor), you could see the attributes before each procedure if any were used. They had an arcane syntax and were not documented. Since these could all be set using the Procedure Attributes menu item, there was never any need to manually edit them. It would have been nice if Microsoft included such a feature in the .NET IDE, but it didn't. Maybe in the first service pack.