Adding a large number of cells to "Allow Edit Range" in VBA? - excel

So using VBA I'm trying to set the fillable fields in a form to an "allowed edit range"
Most fields on my form are merged so I can loop through get a selection and then select/deselect cells to the exact set I desire. So I get a result that looks like this
However the range definition looks like this $E$24:$G$24,$E$25:$G$25,$E$26:$G$26,$E$27:$G$27,$J$24:$L$24,$J$25:$L$25,$J$26:$L$26,$J$27:$L$27,$O$24:$Q$24,$O$25:$Q$25,$O$26:$Q$26,$O$27:$Q$27
As I have many more fields on this form this becomes an issue as the "allowed edit range" is only able to go up to 254 chars.
What I would like is a shorter range definition like this
E24:G27,J24:L27,O24:Q27
Then I can add that to an "allowed edit range"
Currently I'm iterating through each cell in the form and unioning any cell that is merged
Sub SelectMergedCells()
Dim scan_range As Range
Dim cell As Range
Dim merge_range As Range
Set scan_range = ActiveSheet.Range("B11:AM69")
For Each cell In scan_range
If cell.MergeCells Then
If merge_range Is Nothing Then
Set merge_range = cell
Else
Set merge_range = Union(merge_range, cell)
End If
End If
Next cell
merge_range.Select
End Sub
Is there a better way to select the cells? Is there a way to reduce my selection definition to something shorter? Or do I have to break up my definition into multiple 254 char chunks and add multiple ranges?
TIA

If I understand you correctly and if you don't mind using selection ... this is to answer this quote :
What I would like is a shorter range definition like this
so maybe you want to see the code below to help you get started....
The merge cells in a sheet is like this (please ignore the fill color as they are used to be easier to see the merge range).
Expected result :
F3:H3,H5:J6,F7:G7,A7:C8,I8:J11,E9:G11,A2:C4,F2:G2
So after I macro-recording my manual process then modified it a bit, the code is something like this :
Sub test()
Dim rg As Range: Dim c As Range: Dim rgU As Range
Application.ScreenUpdating = False
Set rg = Range("A1:J12"): rg(1, 1).Activate
'Set rg = Range("A1:N6"): rg(1, 1).Activate
With Application.FindFormat
.WrapText = False
.ShrinkToFit = False
.MergeCells = True
End With
Set c = rg.Find(What:="", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=True)
If Not c Is Nothing Then
fa = c.Address
Do
c.Select
If rgU Is Nothing Then _
Set rgU = Selection _
Else Set rgU = Union(rgU, Selection)
Set c = rg.Find(What:="", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=True)
Loop While c.Address <> fa
End If
Debug.Print Replace(rgU.Address, "$", "")
Application.FindFormat.Clear
End Sub
Debug print result is the same with the expected result:
F3:H3,H5:J6,F7:G7,A7:C8,I8:J11,E9:G11,A2:C4,F2:G2
Basically the sub is looping to a cell which is merged as c variable.
select the c, and create rgU variable as the union of rgU and the selection (not the c).
Test with a merge cell similar in your case :
the debug print result is like this :
A2:C5,F2:H5,K2:M5
As I have many more fields on this form this becomes an issue as the
"allowed edit range" is only able to go up to 254 chars.
just now I test the code which rg is A1:AQ75 (multiplying the existing merge cell). Although the rgU.select show a correct result, but I'm sorry, the debug.print rgU.address is only until cell R17.
So, even after this $E$24:$G$24,$E$25:$G$25,$E$26:$G$26,$E$27:$G$27 string is already shorten like this E24:G27, my code is still useless as I think it still won't be able to show the address until the last column and row of the merge cell if the range of the merge cells is too big (cell AQ75 in my case, cell AM69 in your case).
So it need a loop within rgU to get all the merge cell address :
for each cell in rgU.areas:debug.print cell.address:next
Then the last looped result is $AO$97:$AQ$75
btw, about allowed edit range, can't you just use your merge_range variable as reference without the need to have it's address?

Related

Find specific row based on two criteria and then copy paste range into row

I'm trying to copy data from a column in a sheet called "KPI", in cells H6:H100, to a specific row in a sheet named "table". The row depends on two variables in the KPI sheet which user selects from drop downs in C2:D2.
I have managed to get the code to find the right row each time by searching columns A then B in the "data" sheet.But when it comes to the copy paste/transpose column H from "KPI" sheet into the right row on the "table" sheet it throws up a 424 error.
I might be missing something really obvious so any help is appreciated.
Sub copy_transpose()
Dim rng_source As Range
Dim Found As Range, Firstfound As String
Dim rngSearch As Range
Dim Criteria As Variant
Set rng_source = ThisWorkbook.Sheets("KPI").Range("H6:H100")
Set rngSearch = Sheets("Table").Range("A:A")
Criteria = Sheets("KPI").Range("C2:D2").Value
Set Found = rngSearch.Find(What:=Criteria(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Found Is Nothing Then
Firstfound = Found.Address
Do
If Found.EntireRow.Range("B2").Value = Criteria(1, 2) Then Exit Do 'Match found
Set Found = rngSearch.FindNext(After:=Found)
If Found.Address = Firstfound Then Set Found = Nothing
Loop Until Found Is Nothing
End If
If Not Found Is Nothing Then
Application.Goto Found
rng_source.Copy
Sheets("Table").Range(cell.Offset(0, 1), cell.Offset(0, 7)).PasteSpecial Transpose:=True
Else
MsgBox ("Error")
End If
End Sub
I needed more coffee. I hadn't spotted that is was referencing "cell" instead of "found".
Today I learned that "cell" is not a vba function, and was actually something I had dimensioned in my older code, and was the equivalent of "found".

How to check whether a row contains certain text

I am trying to check whether Row 1 of my active sheet named "Exceptions" contains the text "Control  Date" (two spaces) or "Control Date".
My code finds the condition false.
Dim a As Range
Dim exceptions As Worksheet
Set exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each c In Exceptions.Range("A1:Z1")
If c = "Control Date" Then
Cells.Find(What:="control date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Else
Cells.Find(What:="Control Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
End If
Next c
Example of a worksheet with two spaces in "Control  Date"
How to write the condition
As far as checking if the cell value is "Control Date" with a single space or one with two spaces, there are two ways of going about it:
Use the like operator
The like operator makes it easy to compare a string to a basic pattern. In this example, using the wildcard character * (Zero or more characters) will return true regardless of how many spaces are between Control and Date.
If cell.Value2 Like "Control*Date" Then
' Do something with that cell
End If
Use the or operator
Using the or operator ok to use as well, although not a flexible and perhaps a bit more difficult to see what's going on for your specific example.
If cell.Value2 = "Control Date" Or cell.Value2 = "Control Date" Then
' Do something with that cell
End If
Worksheet Codename
Each worksheet has whats called a codename. This is a reference that can be called directly in the code to that specific worksheet by it's name.
To set this name, in the properties window update the name property
So instead of
Dim Exceptions As Worksheet
Set Exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each cell In Exceptions.Range("A1:Z1")
' Do something...
Next cell
you can call the worksheet reference directly
For Each cell In Exceptions.Range("A1:Z1")
' Do something...
Next cell
Putting it together
Instead of using c for your variable, I like to make my variables easier to read and follow so I used cell.
Also, instead of hard coding your header columns in range, you could loop the cells of the entire first row. This is option suggestion though.
Lastly, be more explicit in what property you are looking for in your Cell. In my example I use .value2 to show I am looking for the value of that cell.
Public Sub Demo()
Dim cell As Range
For Each cell In Exceptions.Rows(1).Cells
If cell.Value2 Like "Control*Date" Then
' Do something with that cell
End If
Next cell
End Sub
Why duplicate the data into a third column? Whenever you need the "combined" date, just go get it, but do not store it twice.
Option Explicit '<<-- always have this
Sub doFindControl()
Dim a As Range
Dim c As Range '<<-- add this
Dim colDate As Long, colNumber As Long, colBlank As Long '<<--add this
Dim exceptions As Worksheet
Set exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each c In exceptions.Range("A1:Z1")
' first find the 2 key columns
If InStr(c, "Control") > 0 Then
If InStr(c, "Date") > 0 Then
colDate = c.Column
ElseIf InStr(c, "Number") > 0 Then
colNumber = c.Column
End If
' second look for the first blank column for you to put results in
ElseIf c.Text = "" Then
colBlank = c.Column
Exit For ' stop looking after its found
End If
Next c
' now you have the 2 FROM columns, and the TO column
MsgBox (colDate & " " & colNumber & " " & colBlank)
' and you can loop thru all the rest of the rows doing combine
End Sub
Thank you for all the answers! Robert Todar's answer led me to my lightbulb moment, and I can't believe at how simple the answer was. All I had to do was change this code:
Cells.Find(What:="Control Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
to:
Cells.Find(What:="Control*Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate

Find number in column, insert row below column, populate with data, and then repeat until it has found all of the pre-defined numbers

I need to find the value "5005" (only this value) in column J:J, insert a new row below it, and then fill the row with values in columns A-U.
I am new to VBA and I am unable to do this without making a mess of code.
The draft would look something like this
Find all cells with value 5005 in column J:J,
Insert Row below,
Put value1 in A,
Put Value2 in B,
etc.... until column U,
Repeat on the next cell that has "5005" in it until there are no more
I am unsure what code would work best at this point and I think seeing this written out by a pro would help significantly.
In the messy code I've provided below I was able to search for the value "5005" and insert a line below it, but whatever cell I have selected in excel will be filled with the value "TRUE" and the code is quite messy. Not sure If I was going the right direction with it.
Sub AAAAAAAtest()
Dim find5005 As Range
'Have excel search 1 column instead of all cell
Set find5005 = Cells.Find(What:="5005", LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, MatchCase:=True, SearchFormat:=False)
If find5005 Then ActiveCell.Value = find5005.Offset(1).EntireRow.Insert
End Sub
Narrative is in the code comments
Option Explicit
Sub insert5005()
Dim rng As Range, urng As Range, faddr As String
Dim vals As Variant
'get some dummy values quickly
vals = buildAU()
With Worksheets("sheet5")
'find first 5005
Set rng = .Range("J:J").Find(What:="5005", after:=.Cells(.Rows.Count, "J"), _
LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext)
'continue if found
If Not rng Is Nothing Then
'record first found cell
faddr = rng.Address
'start loop for insert, populate and additional cells
Do
'insert new row
rng.Offset(1, 0).EntireRow.Insert
'populate row
.Cells(rng.Offset(1, 0).Row, "A").Resize(1, UBound(vals) + 1) = vals
'look for another
Set rng = .Range("J:J").FindNext(after:=rng)
'keep going until first address is reached a second time
Loop Until rng.Address = faddr
End If
End With
End Sub
Function buildAU()
'construct some dummy values
Dim i As Long, tmp As String
For i = 65 To 85
tmp = tmp & Format(i, "|v\alu\e00")
Next i
buildAU = Split(Mid(tmp, 2), Chr(124))
End Function

Find Code from Another Sheet and Return Info From Same Row

I have searched to find the answers to get to where I am but am now stuck! I am a relative beginner with VBA.
I have a Workbook that lists a few hundred orders that we are producing for our customer.
The order details are on the first sheet called "In Progress" and on the 3rd sheet called "StyleData" are more details about each product such as its composition, design reference, SKU etc...
At present my code searches column A on the Data sheet based on the 6 digit style code in the active cell on the In Progress Sheet, then goes to that cell. I have put a MsgBox in purely to put a pause in the code so I know where it has got to.
What I want it to do after finding the style code on the data sheet is return a value on the same row from column H, preferable in a format that the use can select and copy, then it will return to the original cell at the start of the macro.
Code as follows:
Sub get_composition()
Dim item_no As String
Dim data_sheet As Worksheet
Dim found_item As Range
Set Rng = ActiveCell
item_no = ActiveCell.Value
Set data_sheet = Sheets("StyleData")
If Trim(item_no) <> "" Then
With Sheets("StyleData").Range("A:A")
Set found_item = .Find(What:=item_no, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not found_item Is Nothing Then
Application.Goto found_item, True
Else
MsgBox "Nothing found"
End If
End With
End If
MsgBox "Return to Original Cell"
Application.Goto Rng
End Sub
if I understand what you want :
you arrive at cell "found_item" and want to return a value from the same row.
If it's so, you can use method Offset on "found_item"
found_item.Offset() allow you to navigate from the current range
https://msdn.microsoft.com/en-us/library/office/ff840060.aspx
If you are on column A, found_item.Offset(, 1) will return the range on the same line but column B

Excel Dynamic Links

I know this is a simple fix, but all my searching has not uncovered the answer. I need to create a dynamic hyperlink.
Sheet 1
Column A1 = 3/0/1
Column A2 = 3/0/2
Sheet 2
Column C3 = 3/0/1
Column D3 = 3/0/2
Now, creating a link on Sheet 2 to the appropriate cell in Sheet 1 is easy. Think is, Sheet 1 has the potential to be sorted in a variety of ways. So I need the link dynamic. I need it to find "3/0/1" whether it's in column A1 or A77, or whatever.
I've seen examples using the ADDRESS function within the HYPERLINK function, but can't get it to work. Any ideas?
This might actually be one of those time when you want to use .Selection to govern which cells the macro is enacted upon.
Sub hlink_Sel()
Dim fnd As Range, sel As Range
On Error Resume Next
With Sheets("Sheet1")
For Each sel In Selection
Set fnd = .Columns("A:A").Find(What:=sel.Text, After:=.Cells(1, 1), LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchDirection:=xlNext, MatchCase:=False)
If Not fnd Is Nothing Then
Debug.Print fnd.Address(0, 0, xlA1, external:=True)
.Hyperlinks.Add Anchor:=sel, Address:="", _
SubAddress:=fnd.Address(0, 0, xlA1, external:=True), TextToDisplay:=sel.Text
End If
Next sel
End With
Set fnd = Nothing
End Sub
Just select some cells that are supposed to have their values duplicated in Sheet1's column A and run the macro. Given the odd nature of your sample data, I've used .Text as the search term in the find operation. Note that the .Address function has the external parameter set to true in order that the worksheet name is included.

Resources