When you click a control on a form, the window displays the control’s properties andlets you edit them in ways that are appropriate for the different property data types.. It shows how t
Trang 1Property Support
Visual Studio developers take the Properties window for granted, but it’s actually a pretty able tool When you click a control on a form, the window displays the control’s properties andlets you edit them in ways that are appropriate for the different property data types It lets youenter text for properties that are strings or simple values such as numbers, it lets you select filesand colors from dialogs, and it lets you pick choices from a drop-down list for enumerated values
remark-If a property is a collection of objects, you can click an ellipsis to the right of the property to open acollection editor where you can add and remove objects, and edit their properties
When you build a custom control, you get all of this functionality for free In many cases, thisdefault behavior is more than enough to let developers use your controls
In fact, if you are developing controls for in-house use, I would recommend that you try to livewith the capabilities provided by the Properties window for you Additional features (such as cus-tom property editors) make developing with controls easier, but they are fairly tricky to imple-
ment Unless you will be spending a lot of time working with the properties, it may not be worth
the additional effort of writing customized tools
In contrast, if you are building controls that will be used by developers outside of your project, itmay be worthwhile providing some of these extra capabilities You may also need to build some
of these tools if your controls’ properties will be directly accessible to end users For example, thePropertyGridcontrol lets users view and modify object properties at run-time If you use thatcontrol, you may need to streamline your properties so that they are easier for the users to under-stand and manage
Unfortunately, controls (and code, in general) often live far longer than was originally intended The control that you plan to use only once may be adopted by other projects, and you may end up supporting it practically forever To avoid unwelcome work, you should make your controls (and all of your code) as bulletproof as possible, but you can still avoid adding gratuitous extras Make the control’s property procedures validate their data so developers cannot push bad data into the control You don’t necessarily need to give every property fancy property editors until you’re cer- tain there’s a need for them.
Trang 2This chapter describes some of the ways you can add design-time support for custom controls and theirproperties It shows how to add drop-down and dialog editors to the Properties window, how to addsmart tags to a control, and how to build property sheets.
Some of the attributes, interfaces, and classes used by the examples in this chapter require that you add a reference to System.Designand System.Windows.Forms.Designnamespaces Double-click My Project, select the References tab, and check the namespaces in the list If one of the namespaces (probably
System.Design) is missing from the list, click the Add button and select the namespace there.
Customizations Over view
This chapter describes several ways that you can enhance Visual Basic’s design time for custom controls Todemonstrate most of these enhancements, this chapter uses an example control named ScribbleControl.This control displays a hand-drawn image At design time, you can both see and edit the image At run-time, the user can view the image If the control’s Enabledproperty is True, the user can also edit theimage at run-time
Figure 11-1 shows the control at design time displaying an image
Figure 11-1: The ScribbleControldisplays a hand-drawn image at design time and run-time
This control represents its drawing as a series of polylines, where a polyline is a series of connected
points The control’s PolyPolylineproperty contains a reference to a PolyPolylineobject That objectcontains an array of Polylineobjects, each of which contains an array of Points Each of these classeshas support methods that perform such tasks as drawing a Polylineor PolyPolyline
Trang 3Figure 11-1 shows several enhancements to Visual Basic’s design-time support for the ScribbleControlclass Because the PolyPolylineproperty is an array of objects, the Properties window would normallydisplay a collection editor to allow the developer to add, remove, and modify Polylineobjects That’snot a very useful way to specify a drawing, however, so the control overrides this behavior.
If you click the ellipsis shown on the right of the PolyPolylineproperty in Figure 11-1, Visual Basicdisplays the editor dialog shown in Figure 11-2 On this editor, you can click the left mouse button anddrag to draw new polylines If you click the right mouse button, the editor clears the picture When youare finished, you can click OK to save the new PolyPolylineproperty, or Cancel to leave the control’sPolyPolylineproperty unchanged
Figure 11-2: You can edit a ScribbleControl’s picture at design time
Notice also that the Properties window displays a tiny image of the drawing in the PolyPolylineproperty’s entry
The ScribbleControlprovides several other custom properties The LineColorproperty determinesthe color that the control uses to draw its polylines In this control, however, LineColorisn’t just anyold color Instead, it must be one of red, orange, yellow, green, blue, indigo, violet, black, or white.You can see in Figure 11-1 that the LineColorentry in the Properties window displays the name of thecurrently selected color If you select that property, the window displays a drop-down arrow on the right
as it normally would for a color property If you click this arrow, however, you don’t see a normal colorselection dialog Instead, you see the color selection drop-down shown in Figure 11-3
The LineWidthproperty determines the thickness of the lines that the control draws The Propertieswindow displays LineWidthby showing a sample of the line and the line’s width in pixels Like theLineColorproperty, LineWidthdisplays a custom drop-down when you click its drop-down arrow.Figure 11-4 shows the LineWidthdrop-down
Trang 4Figure 11-3: The LineColorproperty displays
a custom selection drop-down
Figure 11-4: The LineWidthproperty displays
a custom selection drop-down
Below the properties in the Properties window in Figure 11-1, you can see a link labeled “Draw Spiral.”This is a command verb supported by the ScribbleControlclass If you click this link, the control
clears its picture and draws a spiral More generally, a command verb can take any action that is
appropri-ate to configure or modify the control While the entries in the Properties window affect only a singleproperty, these verbs can perform any action on the control
Trang 5The ScribbleControlin the form designer is selected in Figure 11-1 If you look closely near thecontrol’s upper-right corner, you’ll see a small box holding a little rightward-pointing triangle This is
called a smart tag If you click a smart tag, a panel of actions pops up that lets you configure the
associ-ated control
If you click the smart tag for the ScribbleControl, you’ll see the smart tag panel shown in Figure 11-5.Controls on this panel let you set the ScribbleControl’s BackColor, LineColor, and LineWidthproperties
Figure 11-5: The ScribbleControl’s smart tag lets you configure the control
The smart tag’s Draw Star command clears the control’s picture and draws a star Figure 11-6 shows thecontrol after clicking this command
Figure 11-6: The Draw Star command draws a star
The Draw Spiral command clears the control’s picture and draws the spiral shown in Figure 11-7 TheDraw Spiral command is the same command displayed as a link below the properties in the Propertieswindow
The bottom of the smart tag’s panel contains an Information area that displays the number ofPolylinesin the current drawing In Figure 11-5, this area shows that the shark drawing contains
14Polylines
Trang 6Figure 11-7: The Draw Spiral command draws a spiral.
Finally, the ScribbleControlalso provides property pages These have gone somewhat out of fashionsince Visual Basic NET was released, but they are still useful for manipulating a control in very complexways Like the smart tag panel, property sheets let you change all of the control’s properties in one place.They can also provide more complex commands such as the Draw Star and Draw Spiral tools
While the smart tag pop-up must display all of its tools at once, property sheets can include many pages
In some very complex controls (such as graphing and charting controls), property sheets may containpages to set basic properties (colors, line types, point symbols), chart style (line graph, bar graph, piechart), data, axis labels, and so forth
If you right-click the ScribbleControland select Properties, Visual Basic displays the property sheetsshown in Figure 11-8 (Normally, right-clicking and selecting Properties displays the Properties window.But if the control has property pages, Visual Basic displays those instead.) You can also display the prop-erty sheets by clicking the property pages button at the top of the Properties window You can see thisbutton in Figure 11-4 to the right of the lightning bolt button that displays the control’s Events
Figure 11-8: Property sheets let you manipulate a control in complex ways
The column on the left shows icons and names for the pages contained by the property sheet The rest
of the dialog contains controls that manipulate the control you right-clicked in the form designer.Figure 11-8 shows the first property page, which lets you set the control’s basic properties
Trang 7If you click the Drawing item in the left-hand column, you’ll see the second property page shown inFigure 11-9 This page lets you use the mouse to draw a new picture.
Figure 11-9: This property sheet lets you draw pictures with the mouse
When you make a change to any of the controls on the property pages, the Apply button is enabled Ifyou click Apply, the property sheet applies all of the changes to the ScribbleControlthat you origi-nally right-clicked in the form designer If you click OK, the dialog applies the changes and closes If youclick Cancel, the dialog closes and leaves the ScribbleControlunchanged
The following sections explain in detail how to implement each of these design-time features Thiscontrol contains a lot of code for performing such chores as adding Polylineobjects to the control’sPolyPolylineproperty, adding points to a Polyline, and drawing the control’s image This code isn’tcentral to creating the design-time customizations, so most of this code has been omitted to save space.Download the example project from www.vb-helper.com/one_on_one.htmto see the details
Important Note: When you build the editors and other classes that provide the design-time support, some of them seem to get cached or linked tightly to Visual Studio If you make a change to the editor class and recompile, Visual Studio may continue using the old version.
To make Visual Studio relinquish its hold on the older editor, save your changes, close Visual Studio, delete the project’s binand objdirectories, restart Visual Basic, and rebuild the application.
Note also that Visual Studio sometimes gets confused if you load a form containing a control that you have modified For example, suppose you save a form containing a ScribbleControl, close the form, and then remove the control’s LineWidthproperty When you reopen the form, the form designer tries
to reload the control’s properties from the form’s resources Unfortunately, those resources include a
LineWidthproperty, so the form designer will probably become confused You may need to manually edit the resource file (which is just XML text) and remove the offending property.
One way to avoid this problem is to not save forms containing the controls you are currently building Open the application and then open a test form Place a control on it and test the design-time features you are working on Then close the form without saving changes Modify the control as needed, and then, when you reopen the form, you can add a new control.
The chapter finishes by discussing another common scenario: providing design-time support for ties that are references to objects It shows how you can allow developers to view and modify the object’ssub-properties
Trang 8proper-Displaying and Editing LineWidth
To display a property in a special format and to allow custom editing in the Properties window, you
need to associate the property with a property editor class For example, the following code shows how
the ScribbleControldeclares its LineWidthproperty:
‘ The thicknesses of the lines
Private m_LineWidth As Integer = 1
<EditorAttribute(GetType(LineWidthEditor), GetType(UITypeEditor))> _
Public Property LineWidth() As Integer
GetReturn m_LineWidthEnd Get
Set(ByVal value As Integer)
If value < 1 Then value = 1
If value > 10 Then value = 10m_LineWidth = value
Me.Invalidate()End Set
End Property
The EditorAttributeattribute indicates that the PolyPolylineEditorclass provides supportfor editing the PolyPolylineproperty in the Properties window It provides that support with theLineWidthEditorclass
The LineWidthEditorclass uses a LineWidthListBoxobject The LineWidthEditorand LineWidthListBoxclasses are described in the following sections (You can download these examples atwww.vb-helper.com/one_on_one.htm.)
LineWidthEditor
The LineWidthEditorclass performs two tasks First, it lets the user edit a LineWidthproperty byselecting an item from a drop-down list Second, it displays a sample line with the right thickness inthe Properties window Figure 11-4 shows both the editing drop-down and the sample line drawn in theProperties window
The following code shows the LineWidthEditorclass perform these tasks:
Imports System.Drawing.Design
Public Class LineWidthEditor
Inherits System.Drawing.Design.UITypeEditor
‘ Indicates that this editor displays a dropdown
Public Overloads Overrides Function GetEditStyle( _ByVal context As System.ComponentModel.ITypeDescriptorContext) _
As System.Drawing.Design.UITypeEditorEditStyleReturn UITypeEditorEditStyle.DropDownEnd Function
‘ Edit a line width
Public Overloads Overrides Function EditValue(_
Trang 9ByVal context As System.ComponentModel.ITypeDescriptorContext, _ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
‘ Get an IWindowsFormsEditorService
Dim editor_service As IWindowsFormsEditorService = _CType(provider.GetService(GetType(IWindowsFormsEditorService)), _IWindowsFormsEditorService)
‘ If we failed to get the editor service, return the value
If editor_service Is Nothing Then Return value
‘ Convert the generic value into a line width value
Dim line_width As Integer = DirectCast(value, Integer)
‘ Make the editing control
Dim editor_control As New LineWidthListBox(line_width, editor_service)
‘ Display the editing control
editor_service.DropDownControl(editor_control)
‘ Save the new results
Return editor_control.SelectedIndex + 1End Function
‘ Indicate that we draw a representation for the Properties window’s value.Public Overrides Function GetPaintValueSupported(_
ByVal context As System.ComponentModel.ITypeDescriptorContext) As BooleanReturn True
End Function
‘ Draw a representation for the Properties window’s value
Public Overrides Sub PaintValue(_
ByVal e As System.Drawing.Design.PaintValueEventArgs)e.Graphics.FillRectangle(Brushes.White, e.Bounds)Dim line_width As Integer = DirectCast(e.Value, Integer)Dim y As Integer = e.Bounds.Y + e.Bounds.Height \ 2Using line_pen As New Pen(Color.Black, line_width)e.Graphics.DrawLine(line_pen, _
e.Bounds.Left + 1, y, _e.Bounds.Right - 1, y)End Using
End SubEnd Class
LineWidthEditorinherits from the UITypeEditorclass That class provides basic support for theProperties window
The editor overrides its inherited GetEditStylefunction to return UITypeEditorEditStyle.DropDown,indicating that the editor provides a drop-down property editor
The EditValuefunction actually does the editing It casts the value it should edit into an IntegerLineWidthvalue It then makes a LineWidthListBoxcontrol (described in the following section) anduses an IWindowsFormsEditorServiceobject to display the control The function finishes by returning
Trang 10the line width selected by the control The Properties window sets the LineWidthproperty of theScribbleControlit is editing to this value.
The LineWidthEditor’s GetPaintValueSupportedfunction returns Trueto indicate that the class candraw a special representation of the LineWidthproperty Subroutine PaintValuedoes the drawing Itclears a rectangle inside the bounds where it should draw, and then draws a line with the appropriatethickness
See Figure 11-4 to see the result Notice that the Properties window displays a textual representation ofthe LineWidthin addition to the sample line
‘ The editor service displaying us
Private m_EditorService As IWindowsFormsEditorService
‘ A margin around the image in the list box
Private Const ITEM_MARGIN As Integer = 2Public Sub New(ByVal line_width As Integer, _ByVal editor_service As IWindowsFormsEditorService)MyBase.New()
m_EditorService = editor_service
‘ Give the list some items
For i As Integer = 0 To 9Me.Items.Add(i)Next i
Me.SelectedIndex = line_width - 1Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixedMe.ItemHeight = 16 + 2 * ITEM_MARGIN
End Sub
‘ Close the dropdown
Private Sub LineWidthEditorDropdown_Click(ByVal sender As Object, _ByVal e As System.EventArgs) Handles Me.Click
If m_EditorService IsNot Nothing Then m_EditorService.CloseDropDown()End Sub
‘ Draw a menu item
Private Sub LineWidthEditorDropdown_DrawItem(ByVal sender As Object, _ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem
Trang 11‘ Clear the background background.
e.DrawBackground()
‘ Decide where to draw
Dim x1 As Integer = e.Bounds.X + ITEM_MARGINDim x2 As Integer = e.Bounds.Right - ITEM_MARGINDim y As Integer = e.Bounds.Y + e.Bounds.Height \ 2
‘ Draw the appropriate line
Using line_pen As New Pen(Color.Black, e.Index + 1)e.Graphics.DrawLine(line_pen, x1, y, x2, y)End Using
‘ Draw the focus rectangle if appropriate
If e.State = DrawItemState.Selected Then e.DrawFocusRectangle()End Sub
End ClassThe control begins with a ToolboxItemattribute with the value False That prevents the control fromappearing in the form designer’s toolbox This control is used only by the ScribbleControl’s design-time support classes, so application developers don’t need to use it
The control inherits from the ListBox, so it’s basically a list control that draws its items
The control’s constructor adds items to represent line widths from 1 to 10 to its Itemscollection andselects the appropriate item It sets its DrawModeproperty to indicate that the control’s code will draw theitems and that they will all have the same size It sets ItemHeightto 16 plus a margin to indicate thatthe items will have this height
When the user selects an item from the control’s list, the Clickevent handler calls the editor service’sCloseDropDownmethod This tells the service that the user has finished using the drop-down At thatpoint the LineWidthEditorobject’s call to the service’s DropDownControlmethod returns, so theLineWidthEditorcan save the new LineWidthvalue
The LineWidthListBoxcontrol’s DrawItemevent handler is invoked when the drop-down must draw
an item The control simply draws a sample line within the bounds on the Properties window Then, ifthe item it is drawing is currently selected in the list, the control draws a focus rectangle around it
Displaying and Editing LineColor
The ScribbleControlhandles its LineColorproperty much as it does its LineWidthproperty Thefollowing code shows how the ScribbleControldeclares its LineColorproperty:
‘ The color we use for the lines
Public Enum LineColorsRed
OrangeYellowGreenBlue
Trang 12IngidoVioletBlackWhiteEnd Enum
Public Const NumLineColors As Integer = 9
Private m_LineColor As LineColors = LineColors.Black
<EditorAttribute(GetType(ColorIndexEditor), GetType(UITypeEditor))> _
Public Property LineColor() As LineColors
GetReturn m_LineColorEnd Get
Set(ByVal value As LineColors)m_LineColor = valueMe.Invalidate()End Set
Like LineWidthListBox, the ColorIndexListBoxcontrol inherits from the ListBoxcontrol and drawsits own items The only important difference is that ColorIndexListBoxdraws samples of colors ratherthan sample lines
Unlike the LineWidthEditor, the ColorIndexEditorcontrol does not provide an implementation ofits inherited GetPaintValueSupportedand PaintValueroutines That means the class does not dis-play a sample of the color in the Properties window It displays a drop-down, just as the control does forediting LineWidth, but it doesn’t show a sample of the color in the Properties window
Instead the Properties window automatically generates a textual representation from the enumeratedtype Figure 11-3 shows the color drop-down that lets you pick a color value The Properties windowshows the textual equivalent Yellow above it
Displaying and Editing PolyPolyline
The ScribbleControl’s PolyPolylineproperty also provides support for special display and editing.This property is different from LineWidthand LineColorin a couple of respects First, it wouldn’tmake much sense to display a textual representation of the property in the Properties window (whatstring would represent the fish drawing?) To prevent confusion, the code overrides this behavior anddisplays a blank string in the Properties window
Second, this property provides the modal dialog editor shown in Figure 11-2 instead of a drop-downeditor
Trang 13The following code shows how the ScribbleControldefines its PolyPolylineproperty:
‘ The points we draw
Private m_PolyPolyline As PolyPolyline = New PolyPolyline
<TypeConverter(GetType(PolyPolylineConverter))> _
<EditorAttribute(GetType(PolyPolylineEditor), GetType(UITypeEditor))> _Public Property PolyPolyline() As PolyPolyline
GetReturn m_PolyPolylineEnd Get
Set(ByVal value As PolyPolyline)m_PolyPolyline = valueMe.Invalidate()End Set
End PropertyThe TypeConverterattribute tells Visual Studio that the PolyPolylineConverterclass can convert
aPolyPolylineobject into other data types The Properties window uses this converter to translate thePolyPolylineproperty to and from strings
The EditorAttributeattribute indicates that the PolyPolylineEditorclass provides support forediting the PolyPolylineproperty in the Properties window
of a collection of Polylines or a sequence of coordinates
The following code shows how the PolyPolylineConverterclass works:
‘ Displays a blank string for a PolyPolyline property
‘ Cannot convert from anything
Imports System.ComponentModelPublic Class PolyPolylineConverterInherits TypeConverter
‘ We can convert into String
Public Overrides Function CanConvertTo( _ByVal context As System.ComponentModel.ITypeDescriptorContext, _ByVal destinationType As System.Type) As Boolean
Trang 14If destinationType Is GetType(String) Then Return TrueReturn MyBase.CanConvertTo(context, destinationType)End Function
‘ Convert into a blank String
Public Overrides Function ConvertTo(_
ByVal context As System.ComponentModel.ITypeDescriptorContext, _ByVal culture As System.Globalization.CultureInfo, _
ByVal value As Object, ByVal destinationType As System.Type) As Object
If destinationType Is GetType(String) ThenReturn “”
ElseReturn MyBase.ConvertTo(context, culture, value, destinationType)End If
End FunctionEnd Class
The class inherits from TypeConverter It overrides the inherited CanConvertTofunction to return True
if the destination data type is String This tells Visual Studio that the class can convert to a String.The ConvertTofunction returns a blank string if the destination type is String This is what theProperties window displays
The class does not override its inherited CanConvertFromand ConvertFrommethods, so the controlcannot convert other types such as Stringinto a PolyPolyline If you wanted to allow the user toenter new textual values for the property in the Properties window, you would override these routines
to translate Strings into PolyPolylines
PolyPolylineEditor
The PolyPolylineEditorclass provides editing and display capabilities for the PolyPolylineerty in the Properties window When you click the ellipsis to the right of the property in the Propertieswindow, Visual Studio displays the dialog shown in Figure 11-2 It also draws the tiny image of thedrawing shown in Figure 11-1
prop-The following code shows the first half of the PolyPolylineEditorclass This is the code that providesthe editing dialog
‘ This is an editor for a PolyPolyline property
‘ It displays a ScribbleControl in a modal dialog
Imports System.Drawing.Design
Public Class PolyPolylineEditor
Inherits System.Drawing.Design.UITypeEditor
#Region “Value Editing Code”
‘ Indicates that this editor displays a modal dialog
Public Overloads Overrides Function GetEditStyle( _ByVal context As System.ComponentModel.ITypeDescriptorContext) _
As System.Drawing.Design.UITypeEditorEditStyleReturn UITypeEditorEditStyle.Modal
Trang 15End Function
‘ Edit a PolyPolyline
Public Overloads Overrides Function EditValue(_
ByVal context As System.ComponentModel.ITypeDescriptorContext, _ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
‘ Get an IWindowsFormsEditorService
Dim editor_service As IWindowsFormsEditorService = _CType(provider.GetService(GetType(IWindowsFormsEditorService)), _IWindowsFormsEditorService)
‘ If we failed to get the editor service, return the value unchanged
If editor_service Is Nothing Then Return value
‘ Convert the generic value into a PolyPolyline
Dim ppline As PolyPolyline = DirectCast(value, PolyPolyline)
‘ Make the editing dialog
Dim editor_dialog As New PolyPolylineEditorDialog()editor_dialog.PolyPolyline = ppline
editor_dialog.EditorService = editor_service
‘ Display the editing dialog
editor_service.ShowDialog(editor_dialog)
‘ Save the new results
If editor_dialog.DialogResult = DialogResult.OK Then
‘ Return the new PolyPolyline
Return editor_dialog.PolyPolylineElse
‘ Return the old PolyPolyline
Return pplineEnd If
End Function
#End Region ‘ Value Editing CodeLike the LineWidthEditorand LineColorEditorclasses, this class inherits from UITypeEditor.Whereas the other classes override their GetEditStylefunctions to return UITypeEditorEditStyle.DropDown, this class returns UITypeEditorEditStyle.Modalto indicate that it displays a modaleditor dialog
In the previous classes, the EditValuefunction displays a drop-down In this class, EditValuedisplaysthe modal editor First, it casts the value that it is editing into a PolyPolylineobject It then makes anew PolyPolylineEditorDialog This is a form defined in the ScribbleControlproject, and is theform shown in Figure 11-2 You can download the example program to see all of the dialog’s code.The PolyPolylineEditorDialogcontains a ScribbleControlwith the Enabledproperty set to True
so you can click the control’s surface to draw a new PolyPolyline.The editor form has a public PolyPolylineproperty When the PolyPolylineEditorobject sets theform’s PolyPolylineproperty, the form copies the PolyPolylineinto its ScribbleControl
Trang 16After setting the dialog’s PolyPolylineproperty, the PolyPolylineEditordisplays the dialogmodally If the user clicks the OK button, the EditValuemethod returns the dialog’s new PolyPolylineproperty If the user clicks Cancel, the function returns the original PolyPolylinevalue so the control’sproperty is unchanged.
The following code shows the second half of the PolyPolylineEditorclass This is the code thatlets the Properties window display the miniature image of the ScribbleControl’s drawing shown inFigure 11-1
#Region “Property Display Code”
‘ Indicate that we draw a representation for the Properties window’s value.Public Overrides Function GetPaintValueSupported(_
ByVal context As System.ComponentModel.ITypeDescriptorContext) As BooleanReturn True
End Function
‘ Draw a representation for the Properties window’s value
‘ by converting the scribble into a tiny image
Public Overrides Sub PaintValue(_
ByVal e As System.Drawing.Design.PaintValueEventArgs)
‘ Get the points
Dim ppline As PolyPolyline = DirectCast(e.Value, PolyPolyline)
If (ppline IsNot Nothing) Then
‘ Find the points’ bounds
Dim ppline_rect As Rectangle = ppline.Bounds()ppline_rect.Inflate(CInt(ppline_rect.Width * 0.1), _CInt(ppline_rect.Height * 0.1))
‘ Make a bitmap to hold a small image of the scribble
Dim pts_wid As Integer = ppline_rect.WidthDim pts_hgt As Integer = ppline_rect.HeightDim bm_wid As Integer = e.Bounds.Width - 2Dim bm_hgt As Integer = e.Bounds.Height - 2Dim x_scale As Double = bm_wid / pts_widDim y_scale As Double = bm_hgt / pts_hgtDim scale As Single = CSng(Math.Min(x_scale, y_scale))Dim bm As New Bitmap(bm_wid, bm_hgt)
Using gr As Graphics = Graphics.FromImage(bm)Dim x0 As Integer = CInt(ppline_rect.X + _(ppline_rect.Width - bm_wid / scale) / 2)Dim y0 As Integer = CInt(ppline_rect.Y + _(ppline_rect.Height - bm_hgt / scale) / 2)gr.TranslateTransform(-x0, -y0)
gr.ScaleTransform(scale, scale, Drawing2D.MatrixOrder.Append)gr.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
ppline.Draw(gr, Pens.Black)End Using
‘ Copy the little bitmap onto the drawing surface
e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.Highe.Graphics.DrawImage(bm, e.Bounds.X + 1, e.Bounds.Y + 1)End If