Borders Object in Excel 2010, pass by reference? - excel

I've been working on a formats encoder that grabs the format of a given range, and then provides the ability to paste that format on another given range (basically emulating "Paste Formats", but storable). I'm trying to set the Borders object of a range (in the set routine), but it seems that a Borders Object is passed by value, and not reference?
I can get the current values of the Borders object just fine, but if I try to set any value to it, it's straight up ignored (without even an "Read only" error). Follows is a snippet of my code:
Sub SetBorders(sInput As String, ByRef Target As Borders)
Dim resultPart() As String
'Border indexes go from 5 to 12
For i = 5 To 12
'Set resultPart
resultPart = Split(Split(sInput, CharEOList)(i - 5), CharEORecord)
If Len(resultPart(0)) > 0 Then
Target(i).ColorIndex = CLng(resultPart(0))
...
What am I doing wrong? Should I be using a higher Range object and drilling down to the Borders object inside it?

Apparently, access order is very important to the Borders object. I had been setting .LineStyle at the top, when it should have been more at the bottom. Setting LineStyle first (ie, to None), and then setting a Color, reverts the LineStyle to the one in the Borders Object.
Finished portion of that code:
Sub SetBorders(sInput As String, ByRef Target As Borders)
Dim resultPart() As String
'Border indexes go from 5 to 12
For i = 5 To 12
'Set resultPart
resultPart = Split(Split(sInput, CharEOList)(i - 5), CharEORecord)
'If index is empty, set that property to Variant/Null
If Len(resultPart(0)) > 0 Then Target(i).ColorIndex = CLng(resultPart(0)) Else Target(i).ColorIndex = Null
If Len(resultPart(1)) > 0 Then Target(i).color = CDbl(resultPart(1)) Else Target(i).color = Null
If Len(resultPart(2)) > 0 Then Target(i).ThemeColor = CDbl(resultPart(2)) Else Target(i).ThemeColor = Null
If Len(resultPart(3)) > 0 Then Target(i).TintAndShade = CDbl(resultPart(3)) Else Target(i).TintAndShade = Null
'Weight and LineStyle seem to always be set
Target(i).Weight = CLng(resultPart(4))
Target(i).LineStyle = CLng(resultPart(5))
On Error GoTo 0
Next i
End Sub
Function GetBorders(b As Borders)
Dim Result As String
Result = ""
Dim resultPart() As String
'Border indexes go from 5 to 12
For i = 5 To 12
'Reset resultPart
resultPart = Split(",,,,,", ",")
'Skip errors. This will leave that index blank, which equates to a Null when using Borders
On Error Resume Next
resultPart(0) = b(i).ColorIndex
resultPart(1) = b(i).color
resultPart(2) = b(i).ThemeColor
resultPart(3) = b(i).TintAndShade
resultPart(4) = b(i).Weight
resultPart(5) = b(i).LineStyle
On Error GoTo 0
Result = Result + Join(resultPart, CharEORecord) + CharEOList
Next i
GetBorders = Result
End Function

This worked for me (XL2010), so there must be some other issue going on with your code.
EDIT - I see you figured it out.
Sub tester()
SetBorders Selection.Borders
End Sub
Sub SetBorders(obj As Borders)
Dim arr, x
arr = Array(xlEdgeBottom, xlEdgeLeft, xlEdgeRight, xlEdgeTop)
For x = LBound(arr) To UBound(arr)
With obj(arr(x))
.LineStyle = xlContinuous
End With
Next x
End Sub

Related

VBA - Excel Listbox - Look for Duplicates when Adding Items to Second Listbox

So I am new to the forms side of the VBA coding and I seem to be struggling a bit with this one.
What I have did was follow this tutorial:
https://www.excel-easy.com/vba/examples/multiple-list-box-selections.html
And I have adapted it in a way that suits my needs, but now I am having an issue or two that I do not understand how I can resolve.
The code in the tutorial adds two list boxes to a form and then the add button copy items from the first listbox to the second and the remove button removes items from the second listbox.
The problem is that you can add a specific item more than once, and considering I would like to use the values in the second listbox, this is a problem as I need only unique values.
The code below is what I have come up with so far, but I am getting an error:
Private Sub btn_Add_Filter_Click()
For i = 0 To lbx_Filters_List.ListCount - 1
If lbx_Filters_List.Selected(i) = True Then
For X = 0 To lbx_Filters.ListCount
If Not IsError(lbx_Filters.List(X)) Then
mVal = 0
If lbx_Filters.List(X) <> "" And lbx_Filters.List(X) = lbx_Filters_List.List(i) Then
myVal = 1
End If
End If
If myVal = 0 Then
lbx_Filters.AddItem _
lbx_Filters_List.List(i)
End If
Next X
End If
Next i
End Sub
The error occurs the second time I try and add the same item from the first listbox and what happens is that the second for loop will loop once and on the second loop it throws an error on this line:
If Not IsError(lbx_Filters.List(X)) Then
Error being:
Could not get the list property. Invalid property array index
I eventually (with the help of the comments above) solved the issue. Thank you to all who assisted.
Private Sub btn_Add_Filter_Click()
Dim Size As Integer
Size = lbx_Filters.ListCount
Dim ListBoxContents() As String
Dim ListBoxC() As Variant
Dim i As Integer, y As Integer, X As Integer, myVal As Integer, lItem As Integer
myVal = 0
For i = 0 To lbx_Filters_List.ListCount - 1
If lbx_Filters_List.Selected(i) = True Then
If Size > 0 Then
For lItem = 0 To lbx_Filters.ListCount - 1
For X = 0 To lbx_Filters_List.ListCount - 1
If Not IsError(lbx_Filters_List.List(X)) And lbx_Filters.List(lItem) = lbx_Filters_List.List(i) Then
myVal = 1
End If
Next X
Next lItem
End If
If myVal = 0 Then
lbx_Filters.AddItem _
lbx_Filters_List.List(i)
End If
End If
Next i
End Sub

VBA Making chart from array - problem with missing values

I'm making a line chart with markers. The data is being created in code into array and this array put into chart.
Problem is that there's missing values represented in array as EMPTY.
When plotting the two point that do exist are being connected with line.
The option is selected to plot gaps if cells is empty.
Serie formula shows EMPTY as #N/A.
XValues=={"11/28/2016","12/5/2016","12/12/2016","12/19/2016","12/26/2016","1/2/2017","1/9/2017","1/16/2017","1/23/2017","1/30/2017","2/6/2017","2/13/2017","2/20/2017","2/27/2017"}
Values ={125.15,93.875,#N/A,#N/A,#N/A,#N/A,42,125,48.5714285714285,137,127.285714285714,81.6428571428571,89.9375,69.5,65.6428571428571,75.5,47.1666666666666}
Tried replacing with 0, "", NaN, nothing works. I want to have a break in plotting line.
I have existing value then serie of missing values and then some value.
I noticed that if serie starts with missing values it's plotting fine with gap.
Otherwise not working.
For i = LBound(p_data, 1) + 1 To UBound(p_data, 1)
sSerieName = p_data(i, 0)
If sSerieName <> "" Then
Dim serie() As Variant
Dim w As Long
w = 0
For j = LBound(labels) To UBound(labels)
ReDim Preserve serie(j): serie(j) = p_data(i, j + 1)
Next j
If Not Len(Join(serie, "")) = 0 Then
On Error Resume Next
With p_chart.Chart.SeriesCollection.NewSeries
.XValues = labels
.Values = serie
.Name = sSerieName
End With
On Error GoTo 0
End If
End If
Next i
I don't think this is possible when setting the values etc by arrays. It seems to resolve Empty as N/A. I have tried the following solution, which would involve you keeping a tally of where these empty's happen. So for example, in this case, I have substituted the previous value for my empty, at point 3, and used the following
Values wanted are 10,20,EMPTY,40
With s.NewSeries
.XValues = Array("a", "b", "c", "d")
.Values = Array(10, 20, 20, 40)
.Points(3).Format.Line.Visible = msoFalse
End With
The full solution I tried is as follows
Sub x()
Dim s As SeriesCollection
Dim lc As Long
Dim aTest() As Variant
Dim serie As String
aTest = Array(15, 20, Empty, 40)
Set s = ActiveChart.SeriesCollection
For lc = 0 To UBound(aTest)
If aTest(lc) = "" Then ' <--- record these lc values to hide points
If lc = 0 Then
aTest(lc) = 0
Else
aTest(lc) = aTest(lc - 1)
End If
Else
End If
Next lc
With s.NewSeries
.XValues = Array("a", "b", "c", "d")
.Values = aTest
.Points(3).Format.Line.Visible = msoFalse
End With
End Sub

Blank cell contains a value when code is executed

I’m using a userform with 12 listboxes (numbered 2-13). Each list box could contain 0-8 items assigned by user from main listbox1. I run the following code to output the content of each list box (12 boxes) to sheet “Tray” when a button is pressed.
Each listbox is then output into corresponding columns of each tray from columns B-M. Listbox2 fills column 1 of each tray and so on. A maximum of 4 trays can be filled. The code checks the 1st well of each tray and if it contains a value it assumes the tray is full & begins filling the next tray.
Problem: If the first tray contains a blank column(listbox) and the second tray contains values in the same listbox, the code will fill blank column of the frist tray with values that should be in the second tray. Please see pictures below and updated code below:
Listboxes 2,3 and 4 for Tray 1 (note listbox3 is empty)
Listboxes 2,3 and 4 for tray 2 (note listbox3 has data)
Code ran two times: Listbox3 from tray2 appears in tray1 (erroneously!!!)
Expected output:
Sub Worklist()
'
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Dim item As ListBox
Const cstrNames As String = "Listbox2,Listbox3,Listbox4,Listbox5,Listbox6,Listbox7,Listbox8,Listbox9,Listbox10,Listbox11,Listbox12,Listbox13"
Application.ScreenUpdating = False
lngColNum = 2
For Each VarName In Split(cstrNames, ",")
If UserForm2.Controls(VarName).ListIndex <> -1 Then 'if listbox is not blank
If Sheets("Tray").Cells(4, lngColNum).Value = 0 Then
'checks if value in row 3 column "lngColNum" is empty
lngRowNum = 4
ThisWorkbook.Sheets("Tray").Range("C2").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(15, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 15
ThisWorkbook.Sheets("Tray").Range("C13").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(26, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 26
ThisWorkbook.Sheets("Tray").Range("C24").Value = UserForm2.TextBox1.Value
Else 'otherwise assumes tray starts in row 5, column "lngColNum"
lngRowNum = 37
ThisWorkbook.Sheets("Tray").Range("C35").Value = UserForm2.TextBox1.Value
End If
For i = 0 To UserForm2.Controls(VarName).ListCount - 1
Var = UserForm2.Controls(VarName).List(i)
DblDashPos = InStr(1, Var, "--")
FirstPeriodPos = InStr(1, Var, ".")
Sheets("Tray").Select
ActiveSheet.Cells(lngRowNum, lngColNum) = Left(Var, DblDashPos - 1) & Right(Var, Len(Var) - FirstPeriodPos + 1)
lngRowNum = lngRowNum + 1
Next i
End If
lngColNum = lngColNum + 1
Next
Application.ScreenUpdating = True
End Sub
Thank you very much!
The problem is that you're only testing the column that corresponds to the ListBox to see if the cell is empty. If you want to test that all of the columns in a "tray" are empty, you need to test once for the entire sheet. Something like this (untested because I'm too lazy to rebuild your form):
Private Function FindFirstUnusedRow(sheet As Worksheet) As Long
Dim testColumn As Long, testRow As Long
Dim used As Boolean
For testRow = 4 To 37 Step 11
used = False
For testColumn = 2 To 13
If IsEmpty(sheet.Cells(testRow, testColumn)) = False Then
used = True
Exit For
End If
Next testColumn
If used = False Then
FindFirstUnusedRow = testRow
Exit For
End If
Next testRow
End Function
Then in your code, call it before your loop:
Sub Worklist()
Dim var As Variant
Dim i As Long, dashPos As Long, periodPos As Long, colNum As Long
Dim rowNum As Long, Dim sheet As Worksheet
Application.ScreenUpdating = False
Set sheet = ThisWorkbook.Sheets("Tray")
rowNum = FindFirstUnusedRow(sheet)
If rowNum = 0 Then
Debug.Print "All trays full."
Exit Sub
End If
Dim current As ListBox
For colNum = 2 To 13
Set current = UserForm2.Controls("Listbox" & colNum)
If current.ListIndex <> -1 Then 'if listbox is not blank
sheet.Cells(rowNum - 2, colNum).Value = UserForm2.TextBox1.Value
For i = 0 To current.ListCount - 1
var = current.List(i)
dashPos = InStr(1, var, "--")
periodPos = InStr(1, var, ".")
sheet.Cells(rowNum + i, colNum) = Left$(var, dashPos - 1) & _
Right$(var, Len(var) - periodPos + 1)
Next i
End If
Next colNum
Application.ScreenUpdating = True
End Sub
A couple other notes: You can ditch the Sheets("Tray").Select line entirely - you never use the selection object. Same thing with the mixed references to ActiveSheet and ThisWorkbook.Sheets("Tray"). Grab a reference and use it.
Also, these lines don't do what you think they do:
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Of all the variables you declare, everything is a Variant except lngRowNum. If you want to combine declarations on one line like that, you still need to specify a type for each variable, or they'll default to Variant. See the example code above.

Vba - Rows Property

I am a novice of Vba.
I have been litteraly fighting all day with this bit of code:
Sub ComandsCompactVisualization()
Dim x, i As Integer
Dim CellToAnalyse As Range
x = 2
i = 0
For i = 0 To 5 Step 1
Set CellToAnalyse = Worksheets("Comandi").Cells(x + i, 2)
If Not CellToAnalyse.Font.ColorIndex = 2 Then
Worksheets("Comandi").Rows("x+i:2").Hidden = True
End If
Next i
End Sub
I am trying to hide all the rows that in cell (x+i,2) have not got red text.
I am almost there but... Rows does not seem to accept as content Rows("x+i:2").
I obtain Runtime error 13 "Type mismatch".
If I substitute its content with Rows("2:2") row 2 is deleted but I am not any more able to hide all the other rows that do not have red text in column 2.
Ideas?
Anything between quotes "like this" is just a string. To perform arithmetic on x you need to do this first, then concatenate it to the other part of the string. Like this:
.Rows((x + i) & ":2")
BTW Isn't red 3..?
Sub ComandsCompactVisualization()
Dim x as long, i As Long 'You must declare ALL variables (x was as variant in your code)
Dim CellToAnalyse As Range
dim WS as Worksheet
'x = 2 'if x is always the same value, no need to calculate it each loop
'i = 0 'numbers are initialized to be 0, strings to be "", boolean to be false.
set WS=Sheets("Commandi")
For i = 0 To 5 ' Step 1
Set CellToAnalyse = WS.Cells(2 + i, 2)
If CellToAnalyse.Font.ColorIndex <> 2 Then
CellToAnalyse.entirerow.hidden=true
' WS.Rows(2+i).entirerow.hidden = true is the same result
End If
Next i
End Sub

Pass Excel Range in VBA Function, Process as Array, and Return Result

I have an Excel worksheet with some strings in a column. Sometimes all of the entries are the same, and sometimes not:
I wrote a function to pass the range as a parameter:
=Dent_WG(A1:A6)
The VBA function should determine which case is true (all entries = "Al", or at least one entry = "Ag"), then return 0 or 12 respectively:
Function DentWG(WG_Mat As Range) As Single
Dim dat As Variant, rw As Variant, temp As Single
dat = WG_Mat
temp = 0
For rw = LBound(dat, 1) To UBound(dat, 1)
If dat(rw, 1) = "Ag" Then
temp = 12
End If
Next
If temp = 12 Then
DentWG = 12
Else
DentWG = 0
End If
End Function
However, the function always returns 0, even for the 2nd case where "Ag" occurs in the range. I'm sure I'm failing to correctly convert the range into an array or correctly apply the intended logic to that array.
From your question...
The VBA function should determine which case is true (all entries = "Al", or at least one entry = "Ag"), then return 0 or 12 respectively:
This is what you need.
Function DentWG(WG_Mat As Range) As Long
Dim ClCount As Long
ClCount = WG_Mat.Cells.Count
If Application.WorksheetFunction.CountIf(WG_Mat, "Al") = ClCount Then
DentWG = 0
ElseIf Application.WorksheetFunction.CountIf(WG_Mat, "Ag") > 0 Then
DentWG = 12
End If
End Function
The same can be achieved using a formula
=IF(COUNTIF(A1:A6,"Al")=(ROWS(A1:A6)*COLUMNS(A1:A6)),0,IF(COUNTIF(A1:A6,"Ag") > 0‌​,12,""))
In case it will always be 1 Column then you don't need *COLUMNS(A1:A6). This will do.
=IF(COUNTIF(A1:A6,"Al")=ROWS(A1:A6),0,IF(COUNTIF(A1:A6,"Ag") > 0,12,""))
ScreenShot
You don't really need a UDF for this. You could just say:
=IF(COUNTIF(A1:A6,"Ag")>=1,12,0)
This works for me:
Function DentWG(WG_Mat As Range) As Single
Dim result As Single, cl as Range
result = 0
For Each cl In WG_Mat
If cl = "Ag" Then
DentWG = 12
Exit Function
End If
Next cl
DentWG = result
End Function

Resources