Set range equal to another range dynamically using VBA - excel

I would like to set a range of cells with reference to a named range name1 equal to a similarly sized range of cells on another Excel sheet also referencing a named range name2. I would like the cells in Sheet1 to equal whatever the corresponding cell in Sheet2 currently equals, and therefore I cannot use the .value property.
Sheets("Sheet1").Range("name1").Offset(0, 1).Resize(15, 5) = Sheets("Sheet2").Range("name2").Offset(0, 1).Resize(15, 5).value
Except, that I do not want to use value. Is there a simple way like this to do what I require? I have searched some forums but cannot find a good way to do this. Do I need to use a For each and R1C1 naming? To reiterate - the cells on sheet1 should equal whatever the value is of the relative cell on sheet2 (ranges are the same size). So, eg. cell Sheet1!A1 has the formula =Sheet2!A1.

You could try something like the following:
Sheets("Sheet1").Range("name1").Offset(0, 1).Resize(15, 5).FormulaR1C1 = "=Sheet2!R[0]C[0]"
Update
If the ranges (name1 and name2) are at different positions, you will need to adjust the formula accordingly:
Dim nRowOffset As Long
Dim nColOffset As Long
Dim sFormula As String
nRowOffset = Sheets("Sheet2").Range("name2").Row - Sheets("Sheet1").Range("name1").Row
nColOffset = Sheets("Sheet2").Range("name2").Column - Sheets("Sheet1").Range("name1").Column
sFormula = "=Sheet2!R[" & nRowOffset & "]C[" & nColOffset & "]"
Sheets("Sheet1").Range("name1").Offset(0, 1).Resize(15, 5).FormulaR1C1 = sFormula

I've only tested this a little so it might not be all that robust.
Note This sub needs to be placed in a new (or existing) module, and not in any of the sheet or thisworkbook modules.
It's a macro, and so cannot be called from the worksheet as a UDF. Also, as it has arguments it cannot be directly called.
To use the code you need to create another sub to call this it for you, or call it directly from the immediate window.
Sub RunCode()
Main "Name1", "Name2" ' you could run this line in the immediate/debug window
End Sub
The sub RunCode should be available in the macros menu on in your workbook.
Sub Main(ByVal Name1 As String, ByVal Name2 As String)
Dim Cell As Long
Dim Range1 As Range: Set Range1 = ThisWorkbook.Names(Name1).RefersToRange
Dim Range2 As Range: Set Range2 = ThisWorkbook.Names(Name2).RefersToRange
' check to make sure Name1 and Name2 are the same size
If Range1.Cells.Count = Range2.Cells.Count Then
If Range1.Rows.Count = Range2.Rows.Count Then
If Range1.Columns.Count = Range2.Columns.Count Then
' populate the cells with the formula
For Cell = 1 To Range1.Cells.Count
Range2.Cells(Cell).Formula = "=" & Range1.Worksheet.Name & "!" & Range1.Cells(Cell).Address
Next Cell
End If
End If
End If
End Sub
If you wanted slightly more customizable interface to the function, then the following code should help. Running the RunCode2 macro will prompt you to enter the two names to pass to Main
Public Function nameExists(ByVal Name As String) As Boolean
Dim Result As Boolean: Result = fasle
Dim Item As Variant
For Each Item In ThisWorkbook.Names
If Item.Name = Name Then
Result = True
Exit For
End If
Next Item
nameExists = Result
End Function
Sub RunCode2()
Dim Response As Variant
Dim Name1, Name2 As String
Response = Application.InputBox(Prompt:="Name 1", Type:=2)
If VarType(Response) = vbBoolean Then
Debug.Print "RunCode2 - User Canceled Name 1 Selection"
Exit Sub
Else
If nameExists(Response) = False Then
MsgBox "Name [" & Response & "] Not Found", vbOKOnly
Exit Sub
Else
Name1 = Response
End If
End If
Response = Application.InputBox(Prompt:="Name 2", Type:=2)
If VarType(Response) = vbBoolean Then
Debug.Print "RunCode2 - User Canceled Name 2 Selection"
Exit Sub
Else
If nameExists(Response) = False Then
MsgBox "Name [" & Response & "] Not Found", vbOKOnly
Exit Sub
Else
Name2 = Response
End If
End If
Main Name1, Name2
End Sub

Related

How to find a Excel cell has hyperlink

I have data in Column A in excel..I am iterating through column and i need to find if a cell value has hyperlink init.
LR=Activeworkbook.Worksheets("Emp").Range("A65000").End(xlup).Row
for j=1 to LR
if Thisworkbooks.Worksheets("Emp").cells(j,1)="" then 'Logic to find hyperlink
'Function
end if
next
Identify Cells Containing Hyperlinks
As Red Hare already mentioned in the comments, it is best tested with something like the following:
Dim cell As Range: Set cell = Sheet1.Range("A1")
If cell.Hyperlinks.Count > 0 Then ' has a hyperlink
Else ' has no hyperlink
End If
that is, using the Hyperlinks.Count property of the Hyperlinks object returned by the cell's Hyperlinks property which is a collection of hyperlinks in a range (in this case, a single cell). For a single cell, the Count property will return only 0 or 1 so you could actually use
If cell.Hyperlinks.Count = 1 Then ' has a hyperlink
instead.
Example Code
Option Explicit
Sub IdentifyCellsWithHyperlink()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' If it's not, modify accordingly.
Dim ws As Worksheet: Set ws = wb.Worksheets("Emp")
Dim rg As Range
Set rg = ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp))
Dim cell As Range
For Each cell In rg.Cells
If cell.Hyperlinks.Count > 0 Then ' has a hyperlink
Else ' has no hyperlink
End If
Next cell
End Sub
Here is something that can be used to run through each row to determine if it can be set as a hyperlink. Kinda hard to figure out what the range of possible solutions are that will work for you without fully understanding the context...
Private Sub cmdFollowLink_Click()
CreateHyperlink Me!cmdFollowLink, Me!txtSubAddress, _
Me!txtAddress
End Sub
Sub CreateHyperlink(ctlSelected As Control, _
strSubAddress As String, Optional strAddress As String)
Dim hlk As Hyperlink
Select Case ctlSelected.ControlType
Case acLabel, acImage, acCommandButton
Set hlk = ctlSelected.Hyperlink
With hlk
If Not IsMissing(strAddress) Then
.Address = strAddress
Else
.Address = ""
End If
.SubAddress = strSubAddress
.Follow
.Address = ""
.SubAddress = ""
End With
Case Else
MsgBox "The control '" & ctlSelected.Name _
& "' does not support hyperlinks."
End Select
End Sub

How to add custom text to a cell based on a specific entry in a different cell?

My laboratory is capable of running 20+ different analyses, and we get contracts from about the same 15 companies to do a combination of these analyses. I created an Excel spread sheet to keep track of the work as it comes in, where columns are the 20 different analyses we can run, and rows are the companies. I type in either a checkmark or "NA", depending on whether that company requests that specific analysis. (Each company requests its own combination of analyses).
I need some help with the following:
If I enter "Company 1" in cell A100, I want cell B100 to display "NA". If I enter "Company 2" instead, I want cell D100 to display "NA". And if I enter "Company 3", do nothing, for example. I am OK with adding the check marks manually, as there are other variables that need not be mentioned.
Now, I have been able to develop some toy solution in VBA to some extent (please see code below). However, I have two issues:
In order to run the code, I have to switch to the VBA editor and press F5 after every entry. Instead, I would like it to work like when using formulas for the cells. In other words, if I type in "Company 1" in any cell of column A and hit "Enter", I would like the "NA" to display automatically in the appropriate cells on the row. I guess I could record a macro for this, but the file is shared with many people and I would prefer to avoid that.
In the future I will need to add more companies and analyses, so I need a code I can quickly go in and update. Or maybe have a list of companies that I add to and link it somehow to my code.
Sub writeNA()
For i = 1 To 20 Step 1
x = Cells(i, 1).Value
If x = "Company 1" Then
Cells(i, 2).Value = "NA"
End If
If x = "Company 2" Then
Cells(i, 3).Value = "NA"
End If
If x = "Company 3" Then
Cells(i, 4).Value = "NA"
End If
Next
End Sub
Thank you!
You could add a Worksheet Change event handler, so that whenever the worksheet is changed, the function runs and adds "NA" where needed.
Here is the function that I used for proof of concept. It also adds "NA" when "Company 3" is entered (not sure if that is desired or not).
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case Target.Text
Case "Company 1", "Company 2", "Company 3"
Target.Offset(0, 1).Cells.Value2 = "NA"
End Select
End Sub
Update Cells When Entering Values (Worksheet Change Event)
Usually the code has to be copied to different modules (if you want to use it in multiple worksheets). Optionally you can copy both codes into the sheet module.
Adjust the values in the constants section.
No need to run anything, it runs automatically.
If you already have values in the Criteria Column then do a copy/paste and the data will get updated.
Sheet module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
updateCompany Me, Target
End Sub
Standard Module e.g. Module1
Option Explicit
Sub updateCompany( _
ws As Worksheet, _
Target As Range)
Const ProcName As String = ""
On Error GoTo clearError
Const CompanyList As String = "Company 1,Company 2,Company 3"
Const ColsList As String = "B,D,"
Const CriteriaList As String = "NA,NA,"
Const FirstRow As Long = 2
Const CritCol As String = "A"
Dim cel As Range
Dim rng As Range
' Define Processing Range (First Cell to Bottom-Most Cell (1048576)).
Set rng = ws.Columns(CritCol) _
.Resize(ws.Rows.Count - FirstRow + 1) _
.Offset(FirstRow - 1)
' Define Last Non-Empty Cell.
Set cel = rng.Find( _
What:="*", _
LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
' Validate Last Non-Empty Cell
' i.e. check if Processing Range contains a value.
If cel Is Nothing Then
GoTo ProcExit
End If
' Define Source Range (First Cell to Last Non-Empty Cell).
Set rng = rng.Resize(cel.Row - rng.Row + 1)
' Define Target Range.
Set rng = Intersect(Target, rng)
' Validate Target Range i.e. check if the change happened in Source Range.
If rng Is Nothing Then
GoTo ProcExit ' Change didn't happen in Source Range.
End If
' Write values from Company List to Company Array.
Dim Company() As String: Company = Split(CompanyList, ",")
' Write values from Columns List to Columns Array.
Dim Cols() As String: Cols = Split(ColsList, ",")
' Write values from Criteria List to Criteria Array.
Dim Criteria() As String: Criteria = Split(CriteriaList, ",")
Application.EnableEvents = False
' Write values to cells in rows of changed cells.
Dim CurrentMatch As Variant
' Loop through cells of Target Range (can be non-contiguous).
For Each cel In rng.Cells
' Check if current cell is not blank (Empty or "").
If Len(cel.Value) > 0 Then
' Try to find the value in current cell (Company) in Company Array.
CurrentMatch = Application.Match(cel.Value, Company, 0)
' If found...
If IsNumeric(CurrentMatch) Then
' Define the current index of the found value.
CurrentMatch = CurrentMatch - 1 ' -1 because 0-based.
' Check if the value in Columns Array is different than "".
If Cols(CurrentMatch) <> "" Then
' Write value from Criteria Array to cell in current row
' of the column found in Columns Array.
Cells(cel.Row, Cols(CurrentMatch)) = Criteria(CurrentMatch)
Else
' The value in Columns Array is "".
End If
Else
' Couldn't find Company name in Company Array.
End If
Else
' Cell is blank or empty.
End If
Next cel
SafeExit:
Application.EnableEvents = True
ProcExit:
Exit Sub
clearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume SafeExit
End Sub

How to validate and insert text into Excel sheet defined position based on input text value format?

I would like to parse inserted cell text and use it to properly fill cell data in the corresponding sheet. For instance, based on the value in cell B2 on sheet 1.1 I would like to insert text, into specified location on the sheet 1.2. The cell text specifies the destination location of data in the following format:
<destination_sheet_name>:<destination_panel_id>:<destination_module_id>
In that case the corresponding data on sheet 1.2 should look like below and contain location of data from the source sheet:
<source_sheet_name>:<source_panel_id>:<source_module_id>
Do you think is it possible without writing a VBA script? Can you propose an approach on how to achieve this for a non-pro Excel user ;-)?
Alt + F11
Double click on the Workbook module in the left pane (usually called "ThisWorkbook").
Paste this code. I tried to comment essential parts.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim s() As String
Dim shTarg As Worksheet
Dim lngTargPanelRow As Long
Dim lngTargModuleCol As Long
Dim sSourcePanel As String
Dim sSourceModule As String
Dim c As Range
On Error GoTo EndSub
If Target.Count = 1 Then
' Checking if the new value in the cell (ambiguously called Target) has 2 colons
If Len(Target) - Len(Replace(Target, ":", "")) = 2 Then
' Making an array with elements which are separated by colons in the cell
s() = Split(Target, ":")
' Associating the VBA object with the particular sheet by its name
Set shTarg = Me.Sheets(s(0))
' Finding the target row
Set c = shTarg.Range("$A:$A").Find(s(1))
If c Is Nothing Then GoTo EndSub Else lngTargPanelRow = c.Row
' Finding the target column
Set c = shTarg.Range("1:1").Find(Replace(s(2), "M", "Module "))
If c Is Nothing Then GoTo EndSub Else lngTargModuleCol = c.Column
' Finding the source panel
sSourcePanel = Sh.Cells(Target.Row, 1).Value
' Finding the source module
sSourceModule = Sh.Cells(1, Target.Column).Value
' Shortening the source module name
sSourceModule = Replace(sSourceModule, "Module ", "M")
' Putting the value into the target cell
Application.EnableEvents = False
shTarg.Cells(lngTargPanelRow, lngTargModuleCol) = Sh.Name & ":" & sSourcePanel & ":" & sSourceModule
Else
EndSub:
MsgBox "No changes were made in any other cell"
End If
If Err <> 0 Then MsgBox "Error " & Err & " - " & Err.Description
Application.EnableEvents = True
End If
End Sub

Looping through ListBox to enter values into sheet array

I would like to find the cells (or Rows) in Column B, Sheet1, who have matching values placed into ListBox2. Then, I'd like to change the value of a cell 4 columns over (using an Offset command).
I believe using a For loop is the most efficient way of going thru the values placed into ListBox2. I tried using a Forloop to go thru all values placed into ListBox2.List. Upon calling a value, the code would look for this value in Column B. Once found, it would "remember" the Row in which this value was found. Then, the code would use a Range/Offset command to change the value of a cell 4 columns over in that Row.
Private Sub ButtonOK_Click()
Dim wb As Workbook
Dim ws As Worksheet
Dim SerialList As Range
Dim SerialRow As Long
Dim i As Long
Set wb = ActiveWorkbook
Set ws = ActiveWorkbook.Worksheets("Sheet1")
Dim strFind As Variant
With ws
For i = 0 To Me.ListBox2.ListCount - 1
Set SerialList = ws.Range("B:B").Find(What:=Me.ListBox2.List(i))
SerialRow = SerialList.Row
If Not SerialList Is Nothing Then
ws.Range("B", SerialRow).Offset(0, 4).Value = Me.ListBox2.List(i) 'error occurs here!
MsgBox (ListBox2.List(i) & " found in row: " & SerialList.Row)
Else
MsgBox (ListBox2.List(i) & " not found")
End If
Next i
End With
End Sub
The MsgBoxes do say the correct ListBox2.List(i) value and the correct SerialList.Row, meaning that the program is correctly finding the row in which the list box value is located. However, I get an error saying that my range is not correctly defined at line "ws.Range("B", SerialRow)....."
How do I select the cell I'm searching for to correctly set it to =Me.ListBox2.List(i)?
Couple of fixes:
Dim lv
'....
For i = 0 To Me.ListBox2.ListCount - 1
lv = Me.ListBox2.List(i)
Set SerialList = ws.Range("B:B").Find(What:=lv, LookAt:=xlWhole) '<< be more explicit
'don't try to access SerialList.Row before checking you found a match...
If Not SerialList Is Nothing Then
ws.Cells(SerialList.Row, "F").Value = lv '<< Cells in place of Range
MsgBox (lv & " found in row: " & SerialList.Row)
Else
MsgBox (lv & " not found")
End If
Next i

Data validation macro

I'm creating an Excel sheet which different people are going to add to, so am wondering if there's any simple way to check for the row where user starts writing being filled?
For example, if user starts typing in cell A1, macro checks if the cells are filled on the same row.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim rsave As Range
Dim cell As Range
Set rsave = Sheet1.Range("a1:i1")
For Each cell In rsave
If cell = "" Then
Dim missdata
missdata = MsgBox("missing data", vbOKOnly, "Missing Data")
Cancel = True
cell.Select
Exit For
End If
Next cell
End Sub
to expand on the suggested solution, you can do the following. Instead of looping through each cell, your problem can be solved efficiently with two lines of code:
'get the used range
Set rsave = Sheet1.Range("a1:i1")
'Select all blank(not yet filled) cells
rsave.SpecialCells(xlCellTypeBlanks).Select
This will select all cells which've not been filled in the range a1:i1 of the sheet. Alternatively, you can use some colour to make it more explicit. If it works, don't forget to accept the answer.
If by saying "data validation", you mean check for blanks, you can use this:
Sub Test()
Dim wrng As Range
Set wrng = ActiveSheet.UsedRange
MsgBox "The data in a range: '" & wrng.Address & "' are" & IIf(IsValidData(wrng), "", "n't") & " valid"
Set wrng = Nothing
End Sub
Function IsValidData(rng As Range) As Boolean
IsValidData = rng.SpecialCells(xlCellTypeBlanks).Count = 0
End Function
Note, that the UsedRange method returns a range starting from A1 cell. So, you need to add extra code to select a range occupied by the data (skip blanks rows and columns).
Sub Test()
Dim wrng As Range
Set wrng = GetDataRange()
MsgBox "The data in a range: '" & wrng.Address & "' are" & IIf(IsValidData(wrng), "", "n't") & " valid"
End Sub
Function GetDataRange() As Range
Dim wrng As Range, c As Range, saddr As String
Dim pos As Integer
'get used range
Set wrng = ActiveSheet.UsedRange
'find first non-empty cell in a used range
saddr = ActiveSheet.Range(wrng.End(xlToLeft).Address, wrng.End(xlUp).Address).Address
pos = InStr(1, saddr, ":")
'skip blanks rows and set new range
Set GetDataRange = ActiveSheet.Range(Mid(saddr, pos + 1, Len(saddr) - pos) & ":" & wrng.SpecialCells(xlCellTypeLastCell).Address)
Set wrng = Nothing
End Function
Good luck!

Resources