Get clipboard value for data validation - excel

I have an Excel file that are used by end users, where, in a specific range of cells, if a change is made, a change event macro is triggered.
What I do with this macro is to check if the last action is any type of pasting.
What I need is to, somehow, get in a variable the content the user has copied (clipboard?) and then execute a function or procedure which checks the validy of the data. If it's correct, it will paste the values mantaining the conditional format, and if wrong it will undo the operation and disable the events.
So far, I think I am close to have everything but I am missing the knowledge to get in VBA the clipboard content in a variable.
I would appreciate general feedback as well.
PD: I have stated range(B:B) to keep it simple, in reality I will have a function for each column because the validation changes based (but that's on me,I just need to have 1 correct in order to replicate the logic).
Private Sub Worksheet_Change(ByVal Target As Range)
lastAction = Application.CommandBars("Standard").FindControl(ID:=128).List(1)
If Left(lastAction, 5) = "Paste" Then
If Not Application.Intersect(Target, Range("B:B")) Is Nothing Then
validation (Application.Intersect(Target, Range("B:B")))
End If
Else
End If
End Sub
Function validation(cell) As Boolean
Dim check As Boolean
check = Application.WorksheetFunction.VLookup(cell, MDM.Range("AK2:AK86"), 1, False)
If check = True Then
ActiveSheets.PasteSpecial Paste:=xlPasteAllMergingConditionalFormats
Application.CutCopyMode = False
Else
With Application
.EnableEvents = False
.Undo
End With
Application.EnableEvents = True
End If
End Function
I need to do this validation because if the user paste the value from another excel, it will remove both the conditional formatting and the data validation for that column.

I've used the clsClipboard class described at the following link
http://www.la-solutions.co.uk/content/CONNECT/MVBA/MVBA-Clipboard.htm
Copy the VBA class module code to a file named clsClipboard.cls, then import new Class module into your project.
Usage:
Sub test()
Dim CB As New clsClipboard
Dim myVar As String
CB.SetText "this is a test"
myVar = CB.GetText()
Debug.Print myVar
Set CB = Nothing
End Sub

Related

Excel using auto-generated hyperlinks to hide rows in a table

I have a table where I want to be able to hide individual rows at a mouse click. The (seemingly) easiest solution I've found is to have a column filled with hyperlinks that call a macro to hide the row that they're in.
There are two ways of calling macros from hyperlinks: using Worksheet_FollowHyperlink with manual hyperlinks, and using =HYPERLINK.
The former works fine, except there's no way (that I've found) to have them generate automatically when new rows are added to the table. I would have to either manually copy them down every time, which is unviable, or add them with VBA, which adds a bunch of complexity to an otherwise simple task.
The latter generates fine, being a formula, but it doesn't actually work. It doesn't trigger Worksheet_FollowHyperlink, and when using =HYPERLINK("#MyFunction()") it just doesn't hide rows (or do much other than editing cells contents).
Function MyFunction()
Set MyFunction = Selection
Selection.EntireRow.Hidden = True
End Function
Is there a good solution to this?
Rather than a Hyperlink, you could handle a Double Click event on the table column
Something like
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim NameOfTableColumn As String
On Error GoTo EH:
NameOfTableColumn = "DblClickToHide" ' update to suit your table
If Not Application.Intersect(Target, Target.ListObject.ListColumns(NameOfTableColumn).DataBodyRange) Is Nothing Then
Target.EntireRow.Hidden = True
Cancel = True
End If
Exit Sub
EH:
End Sub
Please, copy the next code in the sheet code module where the table to be clicked exists. Clicking on each cell in its first column (dynamic to rows adding/insertions/deletions), the clicked row will be hidden:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim tbl As ListObject
If Target.cells.CountLarge > 1 Then Exit Sub
Set tbl = Me.ListObjects(1) 'you may use here the table name
If Not Intersect(Target, tbl.DataBodyRange.Columns(1)) Is Nothing Then
Application.EnableEvents = False
Target.EntireRow.Hidden = True
Application.EnableEvents = True
End If
End Sub
It would be good to think about a way to unhide the hidden row if/when necessary. If only a row should be hidden at a time, it is easy to unhide all the rest of column cells...

vba cope variable module

when i change avalue in B2:B10 in need the message "update"
When running Insert_row it should not give that message.
The code works but when I place the procedure Insert_row in another module the public variable is not known ?
How can i solve this.
Public blockchange As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2:B10")) Is Nothing And blockchange = False Then
MsgBox "Update"
End If
End Sub
Sub Insert_rows()
blockchange = True
Dim LastRow As Integer
LastRow = 3 * ActiveSheet.UsedRange.Rows.Count
For i = 3 To LastRow Step 3
Cells(i, 1).EntireRow.Insert
Cells(i, 1).EntireRow.Insert
Next i
blockchange = False
End Sub
Some thoughts:
(1) As SJR wrote, put global variables into a regular module.
(2) If you insist, you can have a global variable also in the code of the sheet. In this case you access it by putting the codename of the sheet as prefix, eg Sheet1.blockchange. The codename of a sheet is the internal name of a sheet, you see it in the VBA editor in the project window or if you open the property window (F4), it is displayed in the first row as (name). This name doesn't change if a sheet is renamed, you can change it only in the VBA editor.
(3) For your specific case, it's maybe better not to use a variable but simply to prevent the events to fire while the Insert-code is running. You do this with the statement Application.EnableEvents = False. Just don't forget to put Application.EnableEvents = True at the end of the code, maybe with an error handler to prevent that the statement is not executed when an error occurs.

Multiple Non-contiguous rows in excel Based on cell value

My goal is to be able to have a drop down list that hides certain non-contiguous rows in excel based off the name of the individual in the list I create. I have this code which I found off Youtube and was wondering what was wrong with it as it was not working. I am relatively new to VBA
Private Sub Worksheet_Calculate()
Dim Andrew, Robert, Michael As Range
Set Andrew = Range("K30")
Set Robert = Range("K30")
Set Michael= Range ("K30")
Select Case Andrew
Case Is = "Andrew": Rows("8:10").EntireRow.Hidden = True
Rows("11:12").EntireRow.Hidden = False
Rows("13:13").EntireRow.Hidden = True
Rows("14:25").EntireRow.Hidden = False
End Select
Select Case Robert
Case Is = "Robert"
Rows("6:20").EntireRow.Hidden = True
Rows("21:25").EntireRow.Hidden = False
End Select
Select Case Michael
Case Is = "Michael"
Rows("1:5").EntireRow.Hidden = True
Rows("6:25").EntireRow.Hidden = False
End Select
End Sub
I created a dummy test Excel worksheet and inserted your VBA code into a new module. It worked fine for me, albeit is a bit clunky.
Some suggestions to help:
Always set Option Explicit at the top of your module, because this means any undeclared variables and other little things like that get picked up immediately. It's good practice to get into that habit early when starting out with VBA.
Always qualify your .Range statement with the prefix for the specific workbook/worksheet your code needs to work on. This may be why it isn't working for you, but ran fine for me. As it stands, your code will only run on whatever worksheet happens to be active at the time.
You have made this a Private Sub (private subroutine). If you have done the proverbial copy & paste then this subroutine will not show up in your list of macros, which could be another reason you cannot run it. I highly recommend you have a read of this ExcelOffTheGrid article, which breaks down the different types nicely. If you have inserted this into the Worksheet object of your VBA Project, then it may need to be moved into its own Module.
You have assigned three different names (Andrew, Robert, Michael) to the same .Range reference. This shouldn't really be allowed (although weirdly didn't flag an error when I copied your code) because what it is saying is that those text strings - and they could be anything, not just those names - all refer to the same specific cell on your worksheet. This hasn't affected your code, because you don't actually refer to them later on. In your Select Case logical tests you have used double quotes " " around each name, telling VBA it is a string of characters not a variable you have defined.
I would suggest something like this:
COPY & PASTE INTO A NEW MODULE
Option Explicit
'
'
Sub HideRows()
' This macro will hide specific non-contiguous rows based upon criteria in my drop down combo box.
'
Dim wkMyBook As Workbook
Dim wsMainSheet As Worksheet
Dim rName As Range
'
With Application
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
End With
Set wkMyBook = ActiveWorkbook
Set wsMainSheet = wkMyBook.Sheets("ENTER SHEET NAME HERE")
Set rName = wsMainSheet.Range("K30")
Select Case rName
Case Is = "Andrew"
wsMainSheet.Rows("8:10").EntireRow.Hidden = True
wsMainSheet.Rows("11:12").EntireRow.Hidden = False
wsMainSheet.Rows("13:13").EntireRow.Hidden = True
wsMainSheet.Rows("14:25").EntireRow.Hidden = False
Case Is = "Robert"
wsMainSheet.Rows("6:20").EntireRow.Hidden = True
wsMainSheet.Rows("21:25").EntireRow.Hidden = False
Case Is = "Michael"
wsMainSheet.Rows("1:5").EntireRow.Hidden = True
wsMainSheet.Rows("6:25").EntireRow.Hidden = False
End Select
With Application
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
End With
End Sub
COPY & PASTE INTO THE WORKBOOK OBJECT
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
' This code will run whenever a change is made to your worksheet
If Target.Address = "$K$30" Then
Select Case Target.Value
Case Is = "Andrew", "Robert", "Michael"
Call HideRows
End Select
End If
End Sub
This has bought your ticket, given you the lift, dropped you off right at the door. Now you gotta do that final step to put this together to make it work. Read the article I linked, learn about bit about how VBA is constructed and then next time you should be a bit further along the path before you need a pick up. Good luck!
Drop Down Worksheet Change
When you change a value in a cell via 'drop down', the Worksheet.Change event is triggered.
Copy the first code into the appropriate sheet module e.g. Sheet1.
Copy the second code into a standard module, e.g. Module1.
You do not run anything, it is automatically showing or hiding rows.
Sheet1
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const RangeAddress As String = "K30"
If Not Intersect(Target, Me.Range(RangeAddress)) Is Nothing Then
manipulateRows Me, Target.Value
End If
End Sub
Module1
Option Explicit
Sub manipulateRows(Sheet As Worksheet, checkString As String)
With Sheet
Select Case checkString
Case "Andrew"
.Rows("8:10").Hidden = True
.Rows(13).Hidden = True
.Rows("11:12").Hidden = False
.Rows("14:25").Hidden = False
Case "Michael"
.Rows("1:5").Hidden = True
.Rows("6:25").Hidden = False
Case "Robert"
.Rows("6:20").Hidden = True
.Rows("21:25").Hidden = False
Case Else ' When DEL is pressed (Empty Cell), shows all rows.
.Rows("1:25").Hidden = False
End Select
End With
End Sub

How to call VBA function from excel

I have en excel file where i have to put validation rule. I have one cell let says "customer Time" where user can enter time but it is customize time. User can enter time like that
23:45
98:20
100:30
User cannot enter string and no special character except colon. I have made one macro and it works perfectly accoriding to customer demand. Here is macro
Public Function isValidTime(myText) As String
Dim regEx
Set regEx = New RegExp 'Regular expression object
regEx.Pattern = "^[0-9]+([:]+[0-9]+)*$" ' Set pattern.
If regEx.test(myText) Then
isValidTime = myText
Else
isValidTime = "Null"
End If
End Function
Note: To test this macro you have to go to VBA ide in Tool then reference and then select microsoft visual basic vbascript 5.5
Now i want to call this at excel. I can call like =IsValidTime("23:43") and getting result but customer is not interested to call this. Customer need a excel where he/she enter the value and value will validate according to above criteria and get the exact value or Null.
I dont know how to fix this task. I have Validated date and time as well from Data and then data validation and set the rule and it works perfect, i need the same way for my this rule as well. Any help will be highly appreciated...
Thanks
Kazmi
You can use the Worksheet_Change event inside the sheet. Inside the VBE, select the sheet and choose Workhseet from the left drop-down and Change from the right.
Enter the following code:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then 'assumes user input cell is A1
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
On Error GoTo ErrTrap
Target.Value = isValidTime(Target.Value)
End If
KeepMoving:
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
Exit Sub
ErrTrap:
MsgBox Err.Number & Err.Description
Resume KeepMoving
End Sub
Public Function isValidTime(myText) As String
Dim regEx
Set regEx = New RegExp 'Regular expression object
regEx.Pattern = "^[0-9]+([:]+[0-9]+)*$" ' Set pattern.
If regEx.test(myText) Then
isValidTime = myText
Else
isValidTime = "Null"
End If
End Function

How can I automatically convert a field in lowercase to uppercase in Excel?

I have produced some VBA code to resolve this problem :
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Value <> Empty Then
Target.Value = UCase(Target.Value)
End If
End Sub
But when I try to input some data in a field, Excel stops working without a single error message.
Does anyone know where this problem can come from ?
You probably have set Application.EnableEvents = False. Open the Immediate window in the VBA editor and type in application.EnableEvents = True then ENTER to turn them back on.
Also, you need to disable events if you don't want to cause a cycle of changing the sheet and re-triggering the event. The ISEMPTY function is slightly different in VBA and your code could be updated to the following which will also handle changing more than just 1 cell
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim cell As Variant
Application.EnableEvents = False
For Each cell In Target
If Not IsEmpty(cell.Value) Then
cell.Value = UCase(cell.Value)
End If
Next cell
Application.EnableEvents = True
End Sub
or if you want to restrict this running to 1 cell change only, replace the for each loop with If Target.rows.count = 1 AND Target.columns.count = 1....
You may not have the callback function in the right spot:
From Events And Event Procedures In VBA
For sheet (both worksheet and chart sheet) level events, the event procedure code must be placed in the Sheet module associated with that sheet. Workbook level events must be placed in the ThisWorkbook code module. If an event procedure is not in the proper module, VBA will not be able to find it and the event code will not be executed.

Resources