cut, copy, and paste

S

Steve

I'm trying to code cut, copy, and paste in vb 2005 so that when the user
clicks on a toolbar button, the cut/copy/paste will work with whatever
textbox the cursor is current located in (I have about 20 textboxes on the
form). Also to ensure that the button can't get used if the cursor isn't in
a textbox field. And to ensure the contents of the clipboard are "text"
contents that have been cut/copied from one of the textboxes on the form.

Thanks

Steve
 
T

Tim Patrick

The TextBox control includes Cut, Copy, and Paste members, so you can use
them directly. In VB2005, you can also use the features in My.Computer.Clipboard.
If you want to ensure that you are only pasting content that came from your
program, you might want to add a custom content type to the clipboard along
with (or instead of) the standard text. Look at My.Computer.Clipboard.GetData()
and .SetData() in the online help for more information.

To determine the current control, use Me.ActiveControl. You can determine
if it is a TextBox object using this code.

If (TypeOf Me.ActiveControl Is TextBox) Then
' ----- Add special code here.
End If
 
S

Steve

Tim...

Thanks - To go one step farther, how would I verify that the contents of the
clipboard was in the same structure as the target textbox. For instance,
I've cut a Currency value out of a textbox that is formatted as
FormatCurrency. I want to only allow the paste to occur if the target
format is the same. Same for FormatPercent.

Steve
 
T

Tim Patrick

One solution would be to take advantage of the available TextBox.Tag property.
In each text box's Tag property, store a short name that indicates the allowed
data. For instance, use "Currency" or "Percent" or "PetNames" or whatever.
Then when you copy the content to the clipboard using My.Computer.Clipboard.SetData,
use that tag name as the format name. Then when someone tries to paste it,
you can compare the format name to what was in the Tag property of the target
text box. You could also store a more complex data structure on the cliipboard
that included the format type, and then just use a single common format name
used by SetData. Either way would work.
 
S

Steve

Okay...I set the Tag Property = Currency and then in my cut/copy events, I
have the line...

Clipboard.SetData(Me.ActiveControl.Tag, Me.ActiveControl.Text)

When I'm pasting, how do I check the format of data or text I'm returning?
I tried,

Dim sFormat As String = Clipboard.GetData()

But I get an error telling me that I must supply the format as a string.

What I thought I could do was return the format of the value in the
clipboard. Check it against the tag property for the textbox. And if not
equal then raise a messagebox and cancel the paste.

Steve
 
T

Tim Patrick

Try:

If (My.Computer.Clipboard.ContainsData(CStr(Me.ActiveControl.Tag)) = True)
And
(TypeOf Me.ActiveControl Is TextBox) Then
CType(Me.ActiveControl, TextBox).Text = _
CStr(My.Computer.Clipboard.GetData(CStr(Me.ActiveControl.Tag)))
Else
' Error processing here
End If
 
S

Steve

That worked great! One more question. When the user has selected some text
in the textbox to cut or copy, what event would I checked to determine this.
And I assume if the length of the selected text was greater than zero than I
would turn on the cut, copy, and delete options.

Thanks for your assistance on this by the way.

Steve
 
S

Stephany Young

That would be the Opening event of your ContextMenuStrip control.

In there, test for the value of the SelectionLength property of the relevant
TextBox control.
 
S

Steve

I added code to opening. But it appears that opening gets called whenever
one of the menu items is clicked and completed.

In other words,

Opening gets called when you first right click. Then if you click on one of
the menu items, the code in the menu item is run, and then the opening gets
called again causing the the code in opening to be run. And if you are
trying to change which items are enabled during the item click, and which
items are enabled during the opening click, then the items get overwritten.
Hopefully I'm making sense.

Anyway, has either of you ever written you're own click events to handle
cut, copy, paste, undo, redo, delete where you only want text to pasted and
deleted based on format types (using the tag approach that Tim suggested).
I'm trying to do this for about 15 different textbox fields that consist of
percent types, number types, phone number types, alpha types, and
alphatnumeric types, and I don't want to allow the user to paste text into
textboxes of the wrong format.

Steve
 
S

Steve

Stephany/Patrick...

Here's the code that I have for cut, copy, paste. delete, selectall, undo
and the strip. As mentioned in my prior post, I'm trying to control how a
user can paste text back into a textbox by setting my textbox tags
(suggested by Patrick) to be phonenumber, zipcode, alpha, alphanumeric, or
numeric and then use the tag to set the format when cutting or pasting back
to the original textbox or a different textbox. I think I fixed the problem
with how the items in the popup menu are enabled/disabled by moving all code
into the opening event instead of in each of the click events.

Not sure if I'm doing this right or if the code can be tighter. Having some
problems with Delete/Undo. tb.CanUndo doesn't seem to work when working
with the Delete event.

Anyway, if you have any time to review/test the code and validate/improve
it, I would appreciate a lot. Its the last thing I need to do before
completing the app.

Thanks

Steve

Private Sub tsmiCut_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiCut.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
'Is any text selected to cut?
If tb.SelectionLength > 0 Then
' Cut the selected text to the clipboard
Clipboard.SetData(tb.Tag, tb.Text)
' Initialize the textbox
tb.Text = ""
' Set the action to 'Cut' so that the items are properly
enabled/disabled
' during the next time the Context Menu is displayed.
CMS_Action = "Cut"
End If
End If

End Sub


Private Sub tsmiCopy_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiCopy.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
'Is any text selected to copy?
If tb.SelectionLength > 0 Then
'Copy the selected text to the clipboard
Clipboard.SetData(tb.Tag, tb.Text)
' Set the action to 'Cut' so that the items are properly
enabled/disabled
' during the next time the Context Menu is displayed.
CMS_Action = "Copy"
End If
End If
End Sub


Private Sub tsmiPaste_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiPaste.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
If (My.Computer.Clipboard.ContainsData(CStr(tb.Tag)) = True)
Then
CType(tb, TextBox).Text =
CStr(My.Computer.Clipboard.GetData(CStr(tb.Tag)))
CMS_Action = "Paste"
Else
MsgBox("Invalid Format", MsgBoxStyle.Information, "Paste")
End If
End If

End Sub

Private Sub tsmiUndo_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiUndo.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
If (My.Computer.Clipboard.ContainsData(CStr(tb.Tag)) = True)
Then
' Can we Undo the last action?
If tb.CanUndo = True Then
' Undo the last thing the user did.
tb.Undo()
' Clear the undo buffer to make sure that clicking Undo
again doesn't redo the last thing undone.
tb.ClearUndo()
End If
CMS_Action = "Undo"
End If
End If

End Sub

Private Sub tsmiDelete_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiDelete.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
'Is any text selected to delete?
If tb.SelectionLength > 0 Then
' Delete the selected text to the clipboard
Clipboard.SetData(tb.Tag, tb.Text)
' Initialize the textbox
tb.Text = ""
' Set the action to 'Delete' so that the items are properly
enabled/disabled
' during the next time the Context Menu is displayed.
CMS_Action = "Delete"
End If
End If

End Sub
Private Sub tsmiSelectAll_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles tsmiSelectAll.Click

If (TypeOf Me.ActiveControl Is TextBox) Then
Dim tb As TextBox = Me.ActiveControl
With tb
.SelectionStart = 0
.SelectionLength = tb.Text.Length
End With
CMS_Action = "SelectAll"
End If

End Sub

Private Sub ContextMenuStrip1_Opening(ByVal sender As Object, ByVal e As
System.ComponentModel.CancelEventArgs) Handles ContextMenuStrip1.Opening

If Me.ActiveControl Is Nothing Then
Exit Sub
End If

Select Case CMS_Action
Case "Undo"
tsmiUndo.Enabled = False
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
tsmiPaste.Enabled = False
tsmiDelete.Enabled = True
tsmiSelectAll.Enabled = True
Exit Sub
Case "Cut"
tsmiUndo.Enabled = True
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
tsmiPaste.Enabled = True
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = False
Exit Sub
Case "Copy"
tsmiUndo.Enabled = False
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
tsmiPaste.Enabled = True
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = False
Exit Sub
Case "Paste"
tsmiUndo.Enabled = True
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
tsmiPaste.Enabled = True
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = True
Exit Sub
Case "Delete"
tsmiUndo.Enabled = True
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
tsmiPaste.Enabled = True
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = False
Exit Sub
Case "SelectAll"
tsmiUndo.Enabled = False
tsmiCut.Enabled = True
tsmiCopy.Enabled = True
If
(My.Computer.Clipboard.ContainsData(CStr(Me.ActiveControl.Tag)) = True) And
(TypeOf Me.ActiveControl Is TextBox) Then
tsmiPaste.Enabled = True
Else
tsmiPaste.Enabled = False
End If
tsmiDelete.Enabled = True
tsmiSelectAll.Enabled = False
Exit Sub
End Select


Dim tb As TextBox = Me.ActiveControl
If tb.Text = "" Then
tsmiUndo.Enabled = False
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
If
(My.Computer.Clipboard.ContainsData(CStr(Me.ActiveControl.Tag)) = True) And
(TypeOf Me.ActiveControl Is TextBox) Then
tsmiPaste.Enabled = True
Else
tsmiPaste.Enabled = False
End If
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = False
End If
If tb.SelectionLength > 0 Then
tsmiCut.Enabled = True
tsmiCopy.Enabled = True
If
(My.Computer.Clipboard.ContainsData(CStr(Me.ActiveControl.Tag)) = True) And
(TypeOf Me.ActiveControl Is TextBox) Then
tsmiPaste.Enabled = True
Else
tsmiPaste.Enabled = False
End If
tsmiDelete.Enabled = True
If tb.SelectionLength = Len(tb.Text) Then
tsmiSelectAll.Enabled = False
Else
tsmiSelectAll.Enabled = True
End If
Else
tsmiCut.Enabled = False
tsmiCopy.Enabled = False
If
(My.Computer.Clipboard.ContainsData(CStr(Me.ActiveControl.Tag)) = True) And
(TypeOf Me.ActiveControl Is TextBox) Then
tsmiPaste.Enabled = True
Else
tsmiPaste.Enabled = False
End If
tsmiDelete.Enabled = False
tsmiSelectAll.Enabled = True
End If

CMS_Action = ""

End Sub
 
S

Steve

Another option - Is it possible to add additional items to the default
context menu? If not, is it possible to create a new context menu that
references the items on the default so that I don't have to replicate the
code?

Steve
 
T

Tim Patrick

I know that you can add items to the system menu for a form (that's the menu
in the upper-left corner that contains Maximize, Minimize, Restore, etc.)
because I've done it before in VB6. It's probably possible to add items to
a control's context menu, but it would probably require using making calls
to unmanaged API calls. I would create a new context menu from scratch and
carry out the appropriate action. For text boxes, you would only have to
implement things like Cut, Copy, Paste, and Select All, so it wouldn't be
too much of a burden.
 
T

Tim Patrick

You should remove all of the code related to CMS_Action. It assumes that
the user will not switch over to another program between a copy and paste
action in your program. Also, it has some logic flow problems that cause
it to work improperly. The code you have in the second half of the Opening
event handler is, with a small bit of modification, sufficient, IMO. When
I removed the CMS_Action code, the Paste menu item was enabled only when
it needed to be. That's great.

Your Delete event handler code is modifying the clipboard. It should not.

Personally, I would add some more comments, especially a comment that lets
the reader know you have used the Tag properties to differentiate the various
TextBox controls.

I can tell by your code that you do not have Option Strict enabled (and possibly
Option Explicit). Please enable Option Strict in your application through
the project properties so that you can reduce any possible bugs from data
type conversions.

If you want to advance the program even more, you can try using the Clipboard.GetDataObject
and .SetDataObject methods instead of just plain .GetData and .SetData. By
using .SetDataObject, you can add both plain text and your custom currency/percent/whatever
content to the clipboard at the same time. This way, other programs can have
access to the clipboard content as plain text.
 
S

Steve

Tim...

Thanks for taking the time to help me on this. I made all the changes you
suggested except the GetDataObject and SetDataObject. A couple of
questions:

1. If you use the default context menu, you have the option of undo-ing the
delete. Whereas in your suggestion, delete doesn't allow the undo. Why
would you not allow the undo, and if so, how would you implement it?

2. GetData and SetData allow you to handle the data by format. Whereas
GetDataObject and SetDataObject only appear to allow you to handle the
data - not the format. If I use GetDataObject and SetDataObject, where am I
handling setting of the format to the tag property.

Thanks

Steve
 
T

Tim Patrick

Are you sure a delete doesn't trigger the text box control's undo setting?
If so, you can monitor the CanUndo property. I'm too tired tonight to try
it out tonight, and Christmas is coming. If you want to keep the CMS_Action
code, it's fine, but be sure to test it thoroughly as it kept doing unpredictable
things when I was using it.

For the SetDataObject() method, here is some sample code from the book _Visual
Basic 2005 in a Nutshell_, page 618 (on sale now! operators standing by!).

Dim pasteData As New System.Windows.Forms.DataObject
pasteData.SetText("This is a pen.")
pasteData.SetData("Japanese-Romaji", "Kore wa pen desu.")
My.Computer.Clipboard.SetDataObject(pasteData)

In that example, "Japanese-Romaji" is the format name.
 
S

Steve

Well I modified the code to use setdataobject and getdataobject. Still
can't get delete and undo to work. Modified the opening to check whenever
tb.text = "" and else, to determine if .canundo is true - to enable
cmsiUndo, but doesn't work. Wondering if I'm missing something, or if
delete isn't suppose to work with undo (even though the vb supplied default
does it).

Let me know if you discover anything. And have a great holiday!

Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top