The most common operation with interfaces that you will want to perform is implementation. You indicate that you want to implement an interface similar to the way you did so in VB6. The way to implement the members of an interface has changed. Let's take a look at implementing the IDrawable interface (see Listing 2.6). A synopsis follows the listing.
Listing 2.6 Implementing the IDrawable Interface
1: Imports System.Drawing 2: 3: Public Class Form1 4: Inherits System.Windows.Forms.Form 5: 6: [ Windows Form Designer generated code ] 7: 8: Private Sub Draw(ByVal DrawableObject As IDrawable) 9: DrawableObject.Draw(CreateGraphics) 10: End Sub 11: 12: Private Sub Button1_Click(ByVal sender As System.Object, _ 13: ByVal e As System.EventArgs) Handles Button1.Click 14: 15: Dim S As New GraphicString() 16: S.Text = "Visual Basic .NET Power Coding" 17: S.Location = New PointF(10, 150) 18: Draw(S) 19: 20: Dim B As New GraphicButton() 21: B.Location = New Point(10, 10) 22: B.Size = New Size(75, 50) 23: Draw(B) 24: 25: End Sub 26: End Class 27: 28: Public Interface IDrawable 29: Sub Draw(ByVal G As Graphics) 30: End Interface 31: 32: Public Class GraphicString 33: 34: Implements IDrawable 35: Private FText As String 36: Private FLocation As PointF 37: 38: Public Property Text() As String 39: Get 40: Return FText 41: End Get 42: Set(ByVal Value As String) 43: FText = Value 44: End Set 45: End Property 46: 47: Public Property Location() As PointF 48: Get 49: Return FLocation 50: End Get 51: Set(ByVal Value As PointF) 52: FLocation = Value 53: End Set 54: End Property 55: 56: Public Sub Draw(ByVal G As Graphics) _ 57: Implements IDrawable.Draw 58: 59: G.DrawString(FText, New Font("Courier", 20), _ 60: Brushes.Blue, FLocation) 61: 62: End Sub 63: 64: End Class 65: 66: Public Class GraphicButton 67: Implements IDrawable 68: 69: Private FLocation As Point 70: Private FSize As Size 71: 72: Public Property Location() As Point 73: Get 74: Return FLocation 75: End Get 76: Set(ByVal Value As Point) 77: FLocation = Value 78: End Set 79: End Property 80: 81: Public Property Size() As Size 82: Get 83: Return FSize 84: End Get 85: Set(ByVal Value As Size) 86: FSize = Value 87: End Set 88: End Property 89: 90: Public Sub Draw(ByVal G As Graphics) _ 91: Implements IDrawable.Draw 92: 93: ControlPaint.DrawButton(G, New Rectangle(FLocation, FSize), _ 94: ButtonState.Normal) 95: 96: End Sub 97: End Class
Lines 1 through 26 implement the Form1 class. When Button1 is clicked, one instance each of GraphicButton and GraphicString are created. Each of these objects implements IDrawable ; as a result, they can be passed to a method that accepts any object that implements IDrawable (see lines 8 through 10).
Lines 28 through 30 define the IDrawable interface. The contract stipulates that any class implementing this interface must define one method, Draw , which is defined to take a single Graphics object.
Lines 32 through 64 define the GraphicString class, and lines 66 through 97 define the GraphicButton class. Both classes implement IDrawable . GraphicString has String and Point properties, and GraphicButton has Size and Point properties. GraphicString implements IDrawable.Draw on lines 56 through 62, and GraphicButton implements IDrawable.Draw on lines 90 through 96.
The Implements statements at the beginning of the GraphicString and GraphicButton classes indicate that these two classes accept the IDrawable contract and will implement Draw . The Implements clauses in the Draw methods in the GraphicString and GraphicButton classes complete the requirements of the contract. (You indicate that a method is implementing an interface method by adding Implements interface.membername at the end of the method, where interface is the actual name of the interface and membername is the name of the member you are implementing. In our example, Implements IDrawable.Draw indicates that these methods are implementing the interface method.)
In Visual Basic .NET you can call the interface method directly or through a reference to the interface, as we did in line 9 of Listing 2.6.
Adding Properties to an Interface
You can add properties to interfaces by declaring the property header only. When you implement the interface, you define a property in the class implementing the interface and add an Implements clause to the end of the property header. If we want to extend IDrawable to include a Point property named Location , we can use the following code.
Public Interface IDrawable Sub Draw(ByVal G As Graphics) Property Location() As Point End Interface
After we have extended the contract this way, consumers of this version of IDrawable would have to implement the Location property too. A suitable implementation (by itself; just copy the following code to a class) might appear as demonstrated next .
Public Property Location() As Point Implements IDrawable2.Location Get Return FLocation End Get Set(ByVal Value As Point) FLocation = Value End Set End Property
In the revision the Implements clause is all we need to make this the implementation of the Location property, and FLocation is presumed to be a field in the containing class.
Adding Events to an Interface
Extending our theme, we could add an event to the IDrawable interface. Perhaps we might like to let a consumer know that the Draw method has been invoked. If we add an OnDraw PaintEventHandler event, consumers of our interface could raise this event when the Draw method is called. The code fragment that follows demonstrates the addition of the PaintEventHandler event.
Public Interface IDrawable Property Location() As Point Sub Draw(ByVal G As Graphics) Event OnDraw As PaintEventHandler End Interface
The preceding interface adds the event to the interface. The following code could be copied and pasted into a class that implements the IDrawable interface. (I have included the revised Draw method to demonstrate how the event might be raised.)
Public Sub Draw(ByVal G As Graphics) _ Implements IDrawable.Draw G.DrawEllipse(Pens.Red, GetRect()) DoDraw(G) End Sub Public Event OnDraw As PaintEventHandler _ Implements IDrawable.OnDraw Private Function GetRect() As Rectangle Return New Rectangle(FLocation.X, FLocation.Y, _ FSize.Width, FSize.Height) End Function Private Sub DoDraw(ByVal G As Graphics) RaiseEvent OnDraw(Me, New PaintEventArgs(G, GetRect())) End Sub
The Draw method literally performs the Draw operation. (In the example we are drawing an ellipse using GDI+.) Before the Draw operation exits we invoke a DoDraw method. Following convention, DoDraw raises the event for us. The implementation of the OnDraw event follows the Draw method, and GetRect and DoDraw are added for convenience.
The only code we had to add to support the IDrawable contract was code for Draw , Location , and OnDraw . The rest of the code was added for convenience. For example, GetRect was reused to ensure that the boundaries for drawing and drawing from DoDraw are identical.