Re: Write, Move, Delete text on PictureBox




"Rick Rothstein (MVP - VB)" <rickNOSPAMnews@xxxxxxxxxxxxxxxxx> wrote in
message news:PYydnbq57anbiHnZnZ2dnUVZ_sKdnZ2d@xxxxxxxxxxxxxx
Am I to assume that if I wish to be able to write several messages on the
same picturebox (container) that I would need to create new labels on the
fly?

Maybe... it depends on the construction of your project. In your case, if
I understand the functionality you want to implement correctly, I'd say
the answer is yes. This is easy to do though. Simply place a Label control
on your form and set its Index property to 0 (this creates a control array
of Labels). Usually, I only use this control as a template for the control
and never show it to my users (hence, set its Visible property to 0; but
set all of the properties you want the other Labels to inherit from it
also, for example, the FontName, FontSize, AutoSize, BackStyle, BackColor,
etc., etc.). Now, whenever you want create a Label, you would just use
this code

Dim ControlIndex As Long
ControlIndex = Label1.Count
Load Label1(ControlIndex)
Label1(ControlIndex).Visible = True

and then finish off by setting any other properties you want to (for
example the Left and Top, which I assume you will take from the TextBox's
Left and Top properties).

Okay, while you were busy studying my code, I changed it... to make it
easier to use. Let's show you how (and I'll incorporate the new Label
creation idea for you). Start a new project and add a Module to it (click
Project/AddModule on VB's menubar). Now copy/paste the following code into
the Module (my write-up follows the module code)...

***********************Start of Module Code***********************
Option Explicit

Private Declare Function SendMessageLong Lib _
"User32" Alias "SendMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

Private Const EM_FMTLINES = &HC8
Private Const EM_LINEFROMCHAR = &HC9

Private MinWidth As Long
Private MaxWidth As Long
Private HeightOfLine As Long
Private FontContainer As Object

Public Sub InitializeTextBox(TBox As TextBox, _
Optional FormOrPictureBox As Variant)
If TypeOf FormOrPictureBox Is Form Or _
TypeOf FormOrPictureBox Is PictureBox Then
If IsMissing(FormOrPictureBox) Then
Set FontContainer = TBox.Parent
Else
Set FontContainer = FormOrPictureBox
End If
With TBox
Set FontContainer.Font = .Font
MaxWidth = .Container.ScaleWidth - .Left
MinWidth = FontContainer.TextWidth("XX")
HeightOfLine = FontContainer.TextHeight("X")
.Height = HeightOfLine
.Width = MinWidth
End With
Else
Err.Clear
Err.Raise 10001, "InitializeTextBox Subroutine", _
"You did not pass either a Form or " & _
"PictureBox into the FormOrPictureBox " & _
"argument for the InitializeTextBox " & _
"subroutine."
End If
End Sub

Public Sub ProcessTextBoxChangeEventCode()
Dim LinesHigh As Long
Const BORDERPIXELS = 6
Const WHITESPACEPIXELS = 2
With Screen.ActiveForm.ActiveControl
.Width = FontContainer.TextWidth(.Text) + (WHITESPACEPIXELS + _
BORDERPIXELS * .BorderStyle) * Screen.TwipsPerPixelY
If .Width < MinWidth Then
.Width = MinWidth
ElseIf .Width > MaxWidth Then
.Width = MaxWidth
End If
SendMessageLong .hwnd, EM_FMTLINES, 1#, 0#
LinesHigh = 1 + UBound(Split(Replace(.Text, _
vbCr & vbCrLf, vbNewLine), vbNewLine))
SendMessageLong .hwnd, EM_FMTLINES, 0#, 0#
.Height = LinesHigh * HeightOfLine + _
BORDERPIXELS * .BorderStyle * Screen.TwipsPerPixelY
.Refresh
End With
End Sub

Public Sub TransferTextFromTextBoxToLabel(TB As TextBox, LBL As Label)
SendMessageLong TB.hwnd, EM_FMTLINES, 1#, 0#
LBL.Caption = Replace(TB.Text, vbCr & vbCrLf, vbCrLf)
SendMessageLong TB.hwnd, EM_FMTLINES, 0#, 0#
End Sub

***********************End of Module Code***********************

Give the Module a descriptive name (say, DynamicTextBox). When you save
the project, give the file a descriptive name and place it in a location
you can reference easily down the line; then, in the future when you need
this functionality, all you will have to do is add this module to the
project you are creating (use the Existing tab on the AddModule dialog
box).

Okay, now add a Label control to the form and set its Index property to 0,
Visible property to False, Left property to 0 (this has to do with how
I'll show you the new Label controls as I create them), Width property to
60 (also has to do with the Label creation process), BackStyle to
1-Opaque, AutoSize to True and BackColor to &H00C0FFFF& (to make it more
easily seen).

Next, add a TextBox to the form, set its MultiLine property to True and
set up the Font properties for it anyway you want (but use a True Type
font); and place it below the Label control somewhere (so it is out of the
way of the Labels I will eventually display). Also add a CommandButton to
the form (this will be used to create a new Label and move the contents of
the TextBox into it). Oh, and move the Form to the left of your screen and
make it as wide as you can (the new Label controls I will add will be
placed next to each other horizontally).

Okay, we are at the last stage. Add the following code to the form's code
window...

***********************Start of Form Code***********************
Option Explicit

Private Sub Form_Load()
InitializeTextBox Text1
End Sub

Private Sub Text1_Change()
ProcessTextBoxChangeEventCode
End Sub

Private Sub Command1_Click()
Dim ControlIndex As Long
ControlIndex = Label1.Count
Load Label1(ControlIndex)
Label1(ControlIndex).Visible = True
Label1(ControlIndex).Move Label1(ControlIndex - 1).Left + _
1.1 * Label1(ControlIndex - 1).Width
TransferTextFromTextBoxToLabel Text1, Label1(ControlIndex)
End Sub
***********************End of Form Code***********************

Are you surprised at how little code there is? That is because all of the
work is being done in the module (which can be reused in new projects by
doing nothing more than adding it to the project). A little explanation
about the code we just added.

Before using the dynamic TextBox, you must initialize the module
variables. To do this, you simply call the InitializeTextBox subroutine
(which is Public on the module, so any form you add to your project can
use it) and pass it the TextBox you want to make dynamic (note, we are
passing the control as an Object, not as a String, so no quote marks
around its name). In case it hasn't dawned on you yet, the fact that you
can pass a TextBox control into the subroutine means you can have multiple
dynamic TextBox controls within your project... just call the
InitializeTextBox before you attempt to use any NEW TextBox for the first
time... if you only have one TextBox in your project, you only have to
initialize it once (because there will never be another TextBox to come
along and change the values sent into the module for it). Oh, and there is
an optional 2nd argument available for the InitializeTextBox subroutine.
By default, the form's font properties will be changed to match those of
the TextBox; but if you are printing directly to that form, this could be
a problem. So, the optional 2nd argument allows you to use a different
form (if you have one available) or a PictureBox (which you can add you
the form and set its Visible property to False). An example of calling
this subroutine where you specify a PictureBox named Picture1 as the "font
processor", is as follows...

InitializeTextBox Text1, Picture1

(again note, we are passing the Object, so no quote marks around it name).

Now, let's look at the Change event for the TextBox which is being used as
a dynamic TextBox... only one subroutine call is necessary... the
ProcessTextBoxChangeEventCode subroutine (which takes no arguments). Since
its event is firing, it is the ActiveControl on the ActiveForm and the
subroutine picks this up automatically. In addition to that, the
subroutine picks up some values that were set when you initialized the
TextBox (the setting of these values is why a TextBox must be initialized
before its first use as a dynamic TextBox). If you have more than one
TextBox that you are going to make dynamic, place the
ProcessTextBoxChangeEventCode call in each of their Change event
procedures.

Lastly, is the code in the Click event... it is used to create new Label
controls and then the text from the dynamic TextBox is transferred to it.
The first five statements are used to create the new Label, make it
Visible and move it into it proper location. Here, I am simply moving it
next to the previously created Label, but you will more than likely be
using the dynamic TextBox control's Left and Top properties to position
the Label at the same location. The last statement is the one that is
doing the actual movement of the text between controls and it is done by
calling the TransferTextFromTextBoxToLabel subroutine from the module.
This subroutine takes two arguments (both of which are required)... the
first argument is the TextBox (passed as an Object) being used as a
dynamic TextBox (note setup is what allows you to have multiple dynamic
TextBox controls within your project) and the second is the Label (again,
being passed as an Object) that you want to receive the text.

That is it (I think I covered everything). Run the program, type some text
in the TextBox and then click the CommandButton, type or edit the text in
the TextBox to change it and then click the CommandButton again, and then
keep repeating the process until you run out of room on your form. The
module concept, and the simple 3 subroutines it exposes makes
incorporating dynamic TextBoxes into a program extremely simple. Hopefully
you will find it useful.

Rick


Rick,

I'm a tad puzzled by the InitializeTextBox routine.

If the text from the textbox is ultimately going to be placed into a label
caption, why would we set the container for the textbox (a picturebox in my
project) to have the same font as the textbox?

Now in my project, there are prices that appear along the left edge of the
picturebox. Various tools I draw on my chart also have text associated with
them. These font sizes are adjustable in my Settings.

I've have a setting for the TextTool, so the user can make the text as small
or big as desired. So I suspect in my case that the container font and the
textbox font must be different. Should I modify the InitializeTextBox
routine to not set the container font equal to textbox font, or am I missing
something?

Thanks.
Rick


.