I have a VBA UserForm in Excel, with very simple code. It displays a collection (a dictionary, actually) of objects, one at a time, with buttons for "first, previous, next, and last". Everything works great, but if I were to continually click the next button to go through the items, I have to click it slowly (roughly once a second). If I click any faster, the click is ignored. For example, if I click four times over two seconds, it will only 'register' the first and third click and advance twice, instead of four times.
Below is example code for the 'next' button (and the other applicable pieces of code in the userform module):
Private dQIDs As Dictionary
Public Sub TransferQIDs(ByVal dIncomingQIDs As Dictionary)
Set dQIDs = dIncomingQIDs
End Sub
Private Sub bNext_Click()
Call LoadQID(CLng(lIndex.Caption) + 1)
End Sub
Private Sub LoadQID(lQID As Long)
Dim QID As cQID
Set QID = dQIDs(lQID)
lIndex.Caption = lQID
lItems.Caption = "Viewing new QID " & lQID & " of " & dQIDs.Count
Me.tQID = QID.lQID
Me.tTitle = QID.sTitle
Me.tVID = QID.sVendorID
Me.bOS = QID.bOSPatch
Me.bApp = Not QID.bOSPatch
Me.bPrev.Enabled = Not (lQID = 1)
Me.bFirst.Enabled = Not (lQID = 1)
Me.bNext.Enabled = Not (lQID = dQIDs.Count)
Me.bLast.Enabled = Not (lQID = dQIDs.Count)
End Sub
Any ideas?
Personally I would just disable to button while content is loaded.
Private Sub bNext_Click()
Dim b1 As Button
Set b1 = ActiveSheet.Buttons("LoadQID")
REM or Me.LoadQID
b1.Font.ColorIndex = 15
b1.Enabled = False
Application.Cursor = xlWait
Call LoadQID(CLng(lIndex.Caption) + 1)
b1.Enabled = True
b1.Font.ColorIndex = 1
Application.Cursor = xlDefault
End Sub
Reason why this happens is that accessing a single object takes quite a bit of time in Excel. This way if you can click it will be registered.
Alternatively you can toggle UI update with:
Application.ScreenUpdating = False
Application.ScreenUpdating = True
windows is checking for a doubleclick. so if you click fast it registers a doubleclick.
2 options. increase you doubleclick speed in the windows mouse settings
or make a doubleclick event
Related
I've created a program that is fully functional and I have sent it to some clients. Some of them have really old computers with really low resolution and they can't access it easily since the form and the controls are oversized for them. Is there an easy way for me to make it to automatically resize both form and controls according to the resolution?
As I've said in the title, this is for Visual Basic 6.0. Thanks to all of you in advance.
You can store size and location of each control on the form, and move or resize controls according to your needs.
In the code below, I use "TabIndex" property as unique id for each control (I can't remember in my old VB6 memory if that's the right thing to do...).
I store the size of the form, and the size and location of each control in the Form_Load event.
Private lWidth As Long
Private lHeight As Long
Private Enum ePROPERTY
ep_Top = 0
ep_Left = 1
ep_Width = 2
ep_Height = 3
End Enum
Private aControlSize() As Long
Private Sub Form_Load()
Dim ctlTmp As Control
lWidth = Me.Width
lHeight = Me.Height
ReDim aControlSize(3, Form1.Controls.Count)
For Each ctlTmp In Form1.Controls
aControlSize(ctlTmp.TabIndex, ep_Top) = ctlTmp.Top
aControlSize(ctlTmp.TabIndex, ep_Left) = ctlTmp.Left
aControlSize(ctlTmp.TabIndex, ep_Width) = ctlTmp.Width
aControlSize(ctlTmp.TabIndex, ep_Height) = ctlTmp.Height
Next
End Sub
Then each time the form is resized (Form_resize event), you'll have to move or resize each control.
Some of them need to be anchored to the right or to the bottom (or both). Some need to be resized and moved. Others don't need nothing.
Private Sub Form_Resize()
Dim ctlTmp As Control
For Each ctlTmp In Form1.Controls
Select Case LCase$(ctlTmp.Name)
Case "text1"
' Text1 is anchored to the left and right borders of the form :
ctlTmp.Width = Me.Width - (lWidth - aControlSize(ctlTmp.TabIndex, ep_Width))
Case "command1"
' Command1 is anchored to the right border of the form :
ctlTmp.Left = aControlSize(ctlTmp.TabIndex, ep_Left) - (lWidth - Me.Width)
Case "check1"
' check1 is anchored to the bottom border of the form :
ctlTmp.Top = aControlSize(ctlTmp.TabIndex, ep_Top) - (lHeight - Me.Height)
End Select
Next
End Sub
Form loaded :
Form Resized :
Please be advised that my code is largely perfectible...
There's probably a more elegant solution that goes through overload each Control and to add properties/methods like the existing ones in dotnet.
I am making a risk-type game for school that dynamically creates a 4x4 grid of buttons inside a table layout panel in visual basic. I have successfully created the panel and buttons with names that correspond to the row and column of the button. There are also two parallel arrays - one for button owner and the other for button number - that correspond to the owner of the button and the number of "armies" in the button. My issue is that when the user clicks a certain button, I need to reference the button name/value to know how many "armies" the button has to control the "attack" portion of the player's turn.
The following code creates the table layout panel and the buttons with names.
'Create table Dynamically
Dim ColCount As Integer = 4
Dim RowCount As Integer = 4
Dim f As New System.Drawing.Font("Arial", 15)
riskTable.AutoScroll = True
riskTable.Dock = DockStyle.Fill
riskTable.ColumnCount = ColCount
riskTable.RowCount = RowCount
For rowNo As Integer = 0 To riskTable.RowCount - 1
For columnNo As Integer = 0 To riskTable.ColumnCount - 1
Dim buttonname As String
buttonname = "B" & rowNo & columnNo
Dim button As Control = New Button
button.Size = New Size(179, 100)
button.Name = buttonname
button.Text = "1"
button.ForeColor = Color.White
button.Font = f
AddHandler button.Click, AddressOf buttonname_Click
riskTable.Controls.Add(button, columnNo, rowNo)
Next
Next
Me.Controls.Add(riskTable)
This is the dynamic event handler that I created. I tried using 'Me.Click' to get the name of the button, but it only returns the name of the form. I need to have code in here that references the name of the currently clicked button to then in turn reference the box owner and box number arrays.
Private Sub buttonname_Click(sender As Object, e As EventArgs) Handles Me.Click
MessageBox.Show(Me.Name)
End Sub
Any help would be greatly appreciated! I think that once I get this working, the rest of the game will be pretty simple to figure out.
Thanks!
Put the name in 'button.Tag' instead/also:
button.Tag = buttonname
Then it is easy to get the name with:
Private Sub buttonname_Click(sender As Object, e As EventArgs) Handles Me.Click
Dim result As String = CType(CType(sender, System.Windows.Forms.Button).Tag, String)
End Sub
(Check the System.Windows.Forms.Button though, might need some tweak to match your buttons inside the table. riskTable.Controls.button ?)
When I try to google it for 3 days, I found that there is only 1 picture/image available in 1 picbox. My goal is to display multiple images, and they cannot overlap. If they overlap, there a red colour should be shown.
I'm using VB6. I'm using 1 combobox1, for select image n 1 commandbutoon. but when I select 2nd image in click button, the image on picbox will auto overwrite it. Is it caused by .cls ??
Private Sub Combo1_Click()
Dim pin As String
Dim intx As Integer
If UCase$(Combo1.List(intx)) = UCase$(pin) Then
Combo1.ListIndex = intx
End If
End Sub
Private Sub Command1_Click()
If Combo1.ListIndex = 0 Then
Set mPic = pin8.Image
ElseIf Combo1.ListIndex = 1 Then
Set mPic = pin12.Image
Else
Set mPic = pin16.Image
End If
mPicWidth = Me.ScaleX(mPic.Width, vbHimetric, Picture1.ScaleMode)
mPicHeight = Me.ScaleY(mPic.Height, vbHimetric, Picture1.ScaleMode)
ShowPictureAtPosition mLeft, mTop
End Sub
Thank you.
Best regard,
chan
Look, The only way to do that is to add 2 images as resources in the project then make one is the default picture or leave it blank as you wish.
The process now is when you click on the command button you switch between the two pictures.
Button Code:
if
PictureBox1.Image = My.Resources.<Name_of_res_file>.<Name_of_image1111_resource>
Then
PictureBox1.Image = My.Resources.<Name_of_res_file>.<Name_of_image2222_resource>
Else
PictureBox1.Image = My.Resources.<Name_of_res_file>.<Name_of_image1111_resource>
End If
This will switch between the 2 pictures. Hope this helps you.
validation on textbox in vb 6.0
i tried
Private Sub Text1_KeyPress(KeyAscii As Integer)
If Not (KeyAscii = vbKeyBack Or KeyAscii = vbKeyDelete Or KeyAscii = vbKeySpace Or (KeyAscii >= Asc("1") And KeyAscii <= Asc("9"))) Then
KeyAscii = 0
End If
but now i want that paste option should disable when i right click on textbox
If you want to prevent right-click menu items from being used on the textbox you can create your own context menu.
Then, using API calls you need to unhook the default menu from the textbox and hook up the custom menu.
(I'm not aware of other APIs which only let you disable/hide items in the existing context menu)
The downside off course is that any menu item you want to keep such as copy or delete you need to write the code for yourself.
You can find a very good explenation on how to do this here Disable right click menu inside a textbox and here Weird reaction on a popup menu
What next,.. what is if the user uses CTRL+V to paste? Or what if the user has paste mapped to different key combinations, other than CTRL+V?
Validate the data instead?
You can end up writing a lot of code trying to prevent data entry. Why not save that work and instead let the user enter what they like and use the validate event to validate the data?
I wrote a sample on another site on using the validate event of a textbox here: Validate Value Is Numeric. That link also has a vb6 demo project I put together attached in the post.
The type of validation is irrelevant I suppose, it simply demonstrates using the validate event allows you to focus on validating the data, rather than trying to code every single possible way you can think of trying to prevent data to be entered in the first place.
The Validate event is triggered before lostfocus and before the next control's getfocus.
Only if the validate event is not told to cancel the action, will the lostfocus event and any subsequent event be executed.
The Validate event is intended to be used to ensure the control's value is validated before executing any other event.
Private Sub Text1_Change()
If Text1.Tag <> Text1.Text Then
Text1.Text = Text1.Tag
Text1.SelStart = Len(Text1.Text)
End If
End Sub
Private Sub Text1_KeyPress(KeyAscii As Integer)
Text1.Tag = Text1.Text & Chr(KeyAscii)
End Sub
Old problem but still relevant with legacy projects.
The solution proposed by Sam Sylvester is interesting but doesn't allow you to delete characters (use backspace) and appends the 22 ASCII character (Ctrl + V) in the textbox (which is bad).
This solution I found worked for me (prevents paste using Ctrl + V and also using the context menu):
Dim preventPasteFlag As Boolean
Private Sub Text1_Change()
If preventPasteFlag Then
Text1.Text = Text1.Tag
preventPasteFlag = False
Else
Text1.Tag = Text1.Text
End If
End Sub
Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii = 22 Then
preventPasteFlag = True
End If
End Sub
Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = 2 Then
preventPasteFlag = True
End If
End Sub
this is quite similar:
Private Sub Text1_Change()
Dim i As Long, sTemp As String
For i = 1 To Len(Text1)
If InStr(1, "0123456789", Mid$(Text1, i, 1)) > 0 Then sTemp = sTemp & Mid$(Text1, i, 1)
Next
Text1 = sTemp
End Sub
i have written some Macros for Visio. Now I copied these to a Stencil called Macros.vss
How can I call my Macros now?
It all depends on what the macros do and how you'd like to call them. I'm going to assume they're simply macros that will execute something within the active Visio page.
By default in Visio VBA, any public subs with no arguments get added to the Visio Tools->Macros menu, in a folder named by the document holding the macros (in this case Macros) and then separated into folders by module name. If you're the only person using the macros then you probably don't need to do anything else.
However, since you put them in a vss file I'll assume you'd like to distribute them to other people.
There's something funny (and by funny I mean irritating) about Visio and how toolbars and buttons work, when added programmatically. Unfortunately, when you create a toolbar using the UIObject and Toolbar and ToolbarItem classes, Visio is going to assume the code you're calling resides in the active drawing, and cannot be in a stencil. So I can give you a little guidance on using those classes, but basically it consists of distributing a .vst template along with your .vss files, with just a single required sub in the .vst file.
So, instead of using a custom toolbar, you can attach code to shape masters in your .vss file that execute the code when they get dropped on a drawing document (using CALLTHIS and the EventDrop event in the shapesheet). With this method I just have a sub that gets called using callthis that takes a shape object as an argument, executes some code, then deletes the shape (if I don't want it around anymore).
And lastly, you can manipulate the Visio UI programmatically to add a toolbar and buttons for your macros. Below is some sample code, basically the way I do it with a solution I developed. As I mentioned above, the most important part of using this method is to have a document template (.vst) that holds a sub (with the below code it must be named RunStencilMacro) that takes a string as an argument. This string should be the "DocumentName.ModuleName.SubName". This sub must take the DocumentName out of the string, and get a Document object handle to that document. Then it must do ExecuteLine on that document with the ModuleName.SubName portion. You'll have to step through the code and figure some things out, but once you get the hang of what's going on it should make sense.
I'm not sure of any other ways to execute the macros interactively with VBA. I think exe and COM addons may not have this issue with toolbars...
Private Sub ExampleUI()
Dim UI As Visio.UIObject
Dim ToolbarSet As Visio.ToolbarSet
Dim Toolbars As Visio.Toolbars
Dim Toolbar As Visio.Toolbar
Dim ToolbarItems As Visio.ToolbarItems
Dim ToolbarItem As Visio.ToolbarItem
Dim TotalToolBars As Integer
Dim Toolbarpos As Integer
Const ToolbarName = "My Toolbar"
' Get the UIObject object for the toolbars.
If Visio.Application.CustomToolbars Is Nothing Then
If Visio.ActiveDocument.CustomToolbars Is Nothing Then
Set UI = Visio.Application.BuiltInToolbars(0)
Else
Set UI = Visio.ActiveDocument.CustomToolbars
End If
Else
Set UI = Visio.Application.CustomToolbars
End If
Set ToolbarSet = UI.ToolbarSets.ItemAtID(visUIObjSetDrawing)
' Delete toolbar if it exists already
TotalToolBars = ToolbarSet.Toolbars.Count
For i = 1 To TotalToolBars
Set Toolbar = ToolbarSet.Toolbars.Item(i - 1)
If Toolbar.Caption = ToolbarName Then
Toolbar.Visible = False
Toolbar.Delete
Exit For
End If
Next
' create toolbar
Set Toolbar = ToolbarSet.Toolbars.Add
Toolbar.Caption = ToolbarName
Dim IconPos As Long ' counter to determine where to put a button in the toolbar
IconPos = IconPos + 1
Dim IconFunction As String
IconFunction = """Macros.Module1.SubName"""
Set ToolbarItem = Toolbar.ToolbarItems.AddAt(IconPos)
With ToolbarItem
.AddOnName = "RunStencilMacro """ & IconFunction & """"
.Caption = "Button 1"
.CntrlType = Visio.visCtrlTypeBUTTON
.Enabled = True
.state = Visio.visButtonUp
.Style = Visio.visButtonIcon
.Visible = True
.IconFileName ("16x16IconFullFilePath.ico")
End With
' Now establish the position of this toolbar
With Toolbar
.Position = visBarTop 'Top overall docking area
.Left = 0 'Puts it x pixels from the left
.RowIndex = 13
.Protection = visBarNoCustomize
Toolbar.Enabled = True
.Visible = True
End With
Visio.Application.SetCustomToolbars UI
Visio.ActiveDocument.SetCustomToolbars UI
End Sub