Background:
I'm trying to write a module to concatenate strings with it's formatting. Therefor I'm looking in all Font properties that could matter, including Subscript and Superscript.
Sample Data:
Imagine in A1:
Sample Code:
Sub Test()
With Sheet1.Range("B1")
.Value = .Offset(0, -1).Value
For x = 1 To .Characters.Count
.Characters(x, 1).Font.Subscript = .Offset(0, -1).Characters(x, 1).Font.Subscript
.Characters(x, 1).Font.Superscript = .Offset(0, -1).Characters(x, 1).Font.Superscript
Next x
End With
End Sub
Result:
Question:
If I would go through this code step-by-step using F8 I can see the characters that are supposed to be subscript become subscript, but will loose it's properties value when the superscript value is passed. The other way around works fine, meaning the superscript properties stay intact.
This procedure is part of a larger procedure where for example I tried to convert this:
Sub ConcatStringsWithFormat()
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim props(9) As Variant, arr As Variant
Dim rng As Range
Dim x As Long, y As Long: y = 0
Set rng = Sheet1.Range("A1:A3")
With Application
.Trim (rng)
arr = rng: arr = .Transpose(.Index(arr, 0, 1))
End With
For Each cell In rng
If Len(cell) > 0 Then
y = y + 1
For x = 1 To cell.Characters.Count
props(0) = cell.Characters(x, 1).Font.Bold
props(1) = cell.Characters(x, 1).Font.ColorIndex
props(2) = cell.Characters(x, 1).Font.FontStyle
props(3) = cell.Characters(x, 1).Font.Italic
props(4) = cell.Characters(x, 1).Font.Size
props(5) = cell.Characters(x, 1).Font.Strikethrough
props(6) = cell.Characters(x, 1).Font.Subscript
props(7) = cell.Characters(x, 1).Font.Superscript
props(8) = cell.Characters(x, 1).Font.TintAndShade
props(9) = cell.Characters(x, 1).Font.Underline
dict.Add y, props
y = y + 1
Next x
End If
Next cell
With Sheet1.Cells(1, 2)
.Value = Application.Trim(Join(arr, " "))
For x = 1 To .Characters.Count
If Mid(.Value, x, 1) <> " " Then
.Characters(x, 1).Font.Bold = dict(x)(0)
.Characters(x, 1).Font.ColorIndex = dict(x)(1)
.Characters(x, 1).Font.FontStyle = dict(x)(2)
.Characters(x, 1).Font.Italic = dict(x)(3)
.Characters(x, 1).Font.Size = dict(x)(4)
.Characters(x, 1).Font.Strikethrough = dict(x)(5)
.Characters(x, 1).Font.Subscript = dict(x)(6)
.Characters(x, 1).Font.Superscript = dict(x)(7)
.Characters(x, 1).Font.TintAndShade = dict(x)(8)
.Characters(x, 1).Font.Underline = dict(x)(9)
End If
Next x
End With
End Sub
Resulting in:
As you can see, it's just the subscript properties that get lost. Any thought on why this happens and also on how to overcome this? It's apparent that a cell will allow both properties to be true on different characters if you manually tried this.
Just test before setting those properties:
Sub Test()
With Sheet1.Range("B2")
.Value = .Offset(0, -1).Value
For x = 1 To .Characters.Count
If .Offset(0, -1).Characters(x, 1).Font.Subscript Then
.Characters(x, 1).Font.Subscript = True
ElseIf .Offset(0, -1).Characters(x, 1).Font.Superscript Then
.Characters(x, 1).Font.Superscript = True
End If
Next x
End With
End Sub
Just found out that swapping the lines will give the correct result:
Wrong
With Sheet1.Range("B1")
.Value = .Offset(0, -1).Value
For x = 1 To .Characters.Count
.Characters(x, 1).Font.Subscript = .Offset(0, -1).Characters(x, 1).Font.Subscript
.Characters(x, 1).Font.Superscript = .Offset(0, -1).Characters(x, 1).Font.Superscript
Next x
End With
Right
With Sheet1.Range("B1")
.Value = .Offset(0, -1).Value
For x = 1 To .Characters.Count
.Characters(x, 1).Font.Superscript = .Offset(0, -1).Characters(x, 1).Font.Superscript
.Characters(x, 1).Font.Subscript = .Offset(0, -1).Characters(x, 1).Font.Subscript
Next x
End With
Swapping the lines around worked. With no other explaination than that these properties are also below eachother under cell settings.
Related
I use the following loops to add data to a Listbox, if the value in a certain range includes "(FS)".
The search range is defined as AllAreas(12). Option buttons on the Userform define which of the 12 areas to search, these are defined by three Integers: Upr, Lwr and Idx.
Plybooker is a String from a Combobox value. If this is not blank then the data entered in to the Listbox is filtered with this in mind, hence the If, ElseIf statement.
The code works absolutely fine, but is very slow when searching the full range (i.e. when all 12 ranges contained within AllAreas are searched).
I need an alternative method for adding the data to the Listbox that is much faster than my For Each Loop. Any suggestions welcome!
Many thanks
Public Sub PlybookListboxAll()
Dim Plybooker As String
Plybooker = Plybooks.ComboBox1.Value
Dim Upr As Integer, Lwr As Integer
If Plybooks.OptionButton12.Value = True Then
Lwr = 0
Upr = 12
End If
If Plybooker = "" Then
For Idx = Lwr To Upr
For Each MyCell In AllAreas(Idx).Cells
If InStr(1, MyCell.Value, "(FS)") > 0 Then 'And IsNumeric(MyCell.Offset(0, 6).Value) Then
Plybooks.ListBox1.AddItem
Plybooks.ListBox1.List(i, 0) = MyCell.Offset(, -1).Value
Plybooks.ListBox1.List(i, 1) = MyCell.Value
Plybooks.ListBox1.List(i, 2) = MyCell.Offset(, 2).Value
Plybooks.ListBox1.List(i, 3) = MyCell.Offset(, 3).Value
Plybooks.ListBox1.List(i, 5) = MyCell.Offset(, 8).Value
If IsNumeric(MyCell.Offset(, 6).Value) = True Then
Plybooks.ListBox1.List(i, 4) = CInt(MyCell.Offset(, 6).Value)
Else: Plybooks.ListBox1.List(i, 4) = "TBC"
End If
i = i + 1
End If
Next MyCell
Next Idx
ElseIf Plybooker <> "" Then
For Idx = Lwr To Upr
For Each MyCell In AllAreas(Idx).Cells
If InStr(1, MyCell.Value, "(FS)") > 0 And MyCell.Offset(0, 2).Value = Plybooker Then 'And IsNumeric(MyCell.Offset(0, 6).Value)
Plybooks.ListBox1.AddItem
Plybooks.ListBox1.List(i, 0) = MyCell.Offset(, -1).Value
Plybooks.ListBox1.List(i, 1) = MyCell.Value
Plybooks.ListBox1.List(i, 2) = MyCell.Offset(, 2).Value
Plybooks.ListBox1.List(i, 3) = MyCell.Offset(, 3).Value
Plybooks.ListBox1.List(i, 5) = MyCell.Offset(, 8).Value
If IsNumeric(MyCell.Offset(, 6).Value) = True Then
Plybooks.ListBox1.List(i, 4) = CInt(MyCell.Offset(, 6).Value)
End If
i = i + 1
End If
Next MyCell
Next Idx
End If
End Sub
Here's a sample of using arrays inside the loop that you were using to populate the listbox (note the array is flipped as you can only change the last dimension when resizing it at the end, so you then use the Column property to flip it back into the control):
Dim myArray()
ReDim myArray(5, 1000) '1000 is a number intended to be larger than the amount of data you expect
For Idx = Lwr To Upr
For Each MyCell In AllAreas(Idx).Cells
If InStr(1, mycell.value, "(FS)") > 0 Then 'And IsNumeric(MyCell.Offset(0, 6).Value) Then
myarray(0, i) = MyCell.Offset(, -1).Value
myarray(1, i) = MyCell.Value
myarray(2, i) = MyCell.Offset(, 2).Value
myarray(3, i) = MyCell.Offset(, 3).Value
myarray(5, i) = MyCell.Offset(, 8).Value
If IsNumeric(MyCell.Offset(, 6).Value) = True Then
myarray(4, i) = CInt(MyCell.Offset(, 6).Value)
Else: myarray(4, i) = "TBC"
End If
i = i + 1
End If
Next MyCell
Next Idx
If i > 0 then
Redim Preserve myarray(5, i - 1)
Plybooks.ListBox1.Column = myarray
End If
It would likely be a lot more efficient to load your source ranges into an array and process that, rather than but I leave that to you. :)
(Note: this is air code so I may have missed something, but it was too long to put into a comment)
I have an excel macro used to change formula. The problem is the although the macro works it makes updating the Excel sheet rather laggy. Any suggestion?
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Columns.Count < Me.Columns.Count Then
If Target.Column = 4 Then
If Target.Row >= 49 And Target.Row <= 178 Then
Dim r As Integer
For r = 49 To 178
'AD = 30
Dim MatType As String
MatType = Cells(r, 4).Value
If MatType = "" Then
Cells(r, 30).Value = "0"
Else
MatType = LCase(MatType)
'Plechy
'Trubky
'Jine
If MatType = "pzs" Or MatType = "pzt" Or MatType = "Tahokov" Then
Cells(r, 30).Value = "=(I" & r & " * J" & r & "*L" & r & ") * 2/1000000"
ElseIf MatType = "jac" Or MatType = "jao" Or MatType = "tr" Or MatType = "u" Or MatType = "kr" Or MatType = "L" Or MatType = "op" Or MatType = "Trubky_spec" Then
Cells(r, 30).Value = "=(F" & r & "*I" & r & "*L" & r & ")/1000000"
Else
Cells(r, 30).Value = "0"
End If
End If
Next
End If
End If
End If
Application.EnableEvents = True
End Sub
this will only loop those that change:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo safeout
Application.EnableEvents = False
Dim rng As Range
Set rng = Intersect(Range("D49:D178"), Target)
If Not rng Is Nothing Then
Dim rngCell As Range
For Each rngCell In rng
Dim r As Long
r = rngCell.Row
'AD = 30
Dim MatType As String
MatType = LCase$(rngCell.Value)
'Plechy
'Trubky
'Jine
Select Case MatType
Case "pzs", "pzt", "Tahokov"
Cells(r, 30).Value = CDbl(Cells(r, "I")) * Cells(r, "J") * Cells(r, "L") * 2 / 1000000
Case "jac", "jao", "tr", "u", "kr", "L", "op", "Trubky_spec"
Cells(r, 30).Value = CDbl(Cells(r, "I")) * Cells(r, "F") * Cells(r, "L") / 1000000
Case Else
Cells(r, 30).Value = 0
End Select
Next
End If
safeout:
Application.EnableEvents = True
End Sub
I am trying to create a chart with VBA, collecting data with the code from a table (not selecting directly the data in the worksheet but doing some internal calculation). My problem is that it does not draw correctly the X-axis (it should be showing just 12.19, 01.20, 02.20):
Do you know how to adjust this (as each month has a different amount of days...)?
This is my code:
Function ChartGenerator(mes As Double, contrato As String, kpi As String)
Dim listax() As Double
Dim listay() As Double
Dim numeelem As Integer
numeelem = 0
last_row = Worksheets("Data").Range("A" & Rows.Count).End(xlUp).row
For i = 2 To last_row
If Worksheets("Data").Cells(i, 8).value <= mes And Worksheets("Data").Cells(i, 4).value = contrato _
And Worksheets("Data").Cells(i, 5).value = kpi Then
numeelem = numeelem + 1
End If
Next i
ReDim listax(numeelem - 1)
ReDim listay(numeelem - 1, 1)
numeelem = 0
For i = 2 To last_row
If Worksheets("Data").Cells(i, 8) <= mes And Worksheets("Data").Cells(i, 4) = contrato _
And Worksheets("Data").Cells(i, 5) = kpi Then
numeelem = numeelem + 1
listax(numeelem - 1) = Worksheets("Data").Cells(i, 8).value **'Those are dates**
listay(numeelem - 1, 0) = Worksheets("Data").Cells(i, 6).value
listay(numeelem - 1, 1) = Worksheets("Data").Cells(i, 7).value
End If
Next i
Dim ydata As Variant
ReDim ydata(numeelem - 1)
Charts.Add
With ActiveChart
.ChartArea.ClearContents
.ChartType = xlXYScatterLines
.ChartStyle = 241 'para cambiar el estilo, usar este
For k = 1 To 2
For j = 0 To numeelem - 1
ydata(j) = listay(j, k - 1)
Next j
.SeriesCollection.NewSeries
.SeriesCollection(k).XValues = listax
.SeriesCollection(k).Values = ydata
.SeriesCollection(k).Name = Worksheets("Data").Cells(1, 6 - 1 + k).value '"prueba" & i 'ID(i, 1)
If k = 2 Then
.SeriesCollection(k).Format.Line.DashStyle = msoLineSysDash
End If
For j = 1 To numeelem
With .SeriesCollection(k).Points(j)
.ApplyDataLabels
.DataLabel.Text = listay(j - 1, k - 1)
End With
Next j
Next k
.HasTitle = True
.Legend.Format.TextFrame2.TextRange.Font.Size = 14
.ChartTitle.Text = contrato & " - " & kpi
With .Axes(xlCategory, xlPrimary)
.HasTitle = True
.AxisTitle.Characters.Text = "Date"
.AxisTitle.Font.Size = 14
.AxisTitle.Font.Name = "calibri"
.CategoryType = xlTimeScale
.MinimumScale = listax(0) - 0.0000000001
.MaximumScale = listax(numeelem - 1) + 1
.MinorUnit = 31
.MajorUnit = 31
.TickLabels.NumberFormat = "mmmm-yy"
End With
With .Axes(xlValue, xlPrimary)
.HasTitle = True
.AxisTitle.Text = "Amount of €"
.AxisTitle.Font.Size = 14
.AxisTitle.Font.Name = "calibri"
End With
End With
End Function
I have a code which builds a table based on the data in another sheet. In this sheet there are three columns - Time, URN and Location. Time is shown as HH:MM:SS, URN is a 4 digit number and Location is a postcode displayed in the usual format.
I have normally used this code with a Date instead of time, but I have been trying to use it with time. I have made a slight adjustment after declaring the date as a variable, adding in the time value part.
I am now getting a
Run-time error '91': Object variable or With block variable not set,
with the following highlighted:
.Cells(FndDt.Row, FndNum.Column) = "P"
I have tried removing this piece of code and adding in a On Error Resume Next but I then get an error on the lines above or below it.
Option Explicit
Sub chrisellis250()
Dim Dt, Urn, i As Long, x As Long, lr As Long, lc As Long: x = 2
Dim colwidth As Long
Dim FndDt As Range, FndNum As Range, Dat As Date, Num As String, Loc As String
Dat = TimeValue("00:00:00")
Application.ScreenUpdating = False
With Sheet2
lr = .Cells(.Rows.Count, 2).End(xlUp).Row
.Range(.Cells(2, 1), .Cells(.Rows.Count, 1)).AdvancedFilter xlFilterCopy, , .Range("E1"), True
With .Range("E1").CurrentRegion: Dt = .Value: End With
Sheet1.Range("A3").Resize(UBound(Dt) - 1) = .Range("E2:E" & UBound(Dt)).Value: .Columns(5).Clear
Sheet1.Range("A3").Resize(UBound(Dt) - 1).Interior.ColorIndex = 15
.Range(.Cells(2, 2), .Cells(.Rows.Count, 2)).AdvancedFilter xlFilterCopy, , .Range("E1"), True
With .Range("E1").CurrentRegion: Urn = .Value: End With
For i = 1 To 2
Sheet1.Cells(2, x).Resize(, UBound(Urn) - 1) = Application.WorksheetFunction.Transpose(.Range("E2:E" & UBound(Urn)).Value)
If i = 1 Then colwidth = 8.3 Else colwidth = 55
Sheet1.Cells(2, x).Resize(, UBound(Urn) - 1).ColumnWidth = colwidth
If x = 2 Then Sheet1.Cells(1, x) = "URN" Else Sheet1.Cells(1, x) = "XXXXX"
Sheet1.Cells(1, x).Resize(, UBound(Urn) - 1).MergeCells = True
Sheet1.Cells(1, x).Resize(, UBound(Urn) - 1).Interior.ColorIndex = 15
x = x + UBound(Urn) - 1
Next i
.Columns(5).Clear
For i = 2 To .Cells(.Rows.Count, 1).End(xlUp).Row
If .Range("B" & i) <> "" Then
Dat = .Range("A" & i): Num = .Range("B" & i): Loc = .Range("C" & i)
With Sheet1
.Range("B3").Resize(lr, UBound(Urn) - 1).Font.Name = "Wingdings 2"
lc = .Cells(2, .Columns.Count).End(xlToLeft).Column
Set FndDt = .Range("A:A").Find(Dat, LookIn:=xlValues, lookat:=xlWhole)
Set FndNum = .Range(.Cells(2, 1), .Cells(2, lc)).Find(Num, LookIn:=xlValues, lookat:=xlWhole)
.Cells(FndDt.Row, FndNum.Column) = "P": .Cells(FndDt.Row, FndNum.Column).Font.Color = vbGreen
On Error Resume Next
If Not .Cells(FndDt.Row, FndNum.Column + UBound(Urn) - 1) Like "*" & Loc & "*" Then
.Cells(FndDt.Row, FndNum.Column + UBound(Urn) - 1) = IIf(.Cells(FndDt.Row, FndNum.Column + UBound(Urn) - 1) = "", Loc, .Cells(FndDt.Row, FndNum.Column + UBound(Urn) - 1) & "," & Loc)
End If
End With
End If
Next i
With Sheet1
With .Range("B3").Resize(UBound(Dt) - 1, UBound(Urn) - 1)
.SpecialCells(xlCellTypeBlanks).Font.Color = vbRed: .SpecialCells(xlCellTypeBlanks).Value = "O":
End With
With .Range("B3").Offset(, UBound(Urn) - 1).Resize(UBound(Urn) - 1, UBound(Urn) - 1)
.SpecialCells(xlCellTypeBlanks).Interior.ColorIndex = 15
End With
AddOutsideBorders .Range("A1").Resize(UBound(Dt) + 1, 1 + ((UBound(Urn) - 1) * 2))
With .Cells
.Columns.AutoFit
.HorizontalAlignment = xlCenter
.RowHeight = 25
End With
End With
End With
Application.ScreenUpdating = True
End Sub
Public Function AddOutsideBorders(rng As Range)
With rng.Borders
.LineStyle = xlContinuous
.Color = vbBlack
.Weight = xlThin
End With
End Function
Recently, I have been trying to code a VBA to assist me in summing a column and divide by counter to get average. However, I have a new requirement that is it is only going to sum up those that are visible. Any idea on how should I proceed? Below is my code,
Sub test3()
Dim FinalRow As Long
Dim Row As Long
Dim counter As Integer
Dim total As Double
counter = 3
total = 0
Dim i As Double
FinalRow = Range("C65536").End(xlUp).Row
For Row = 3 To FinalRow
If Not IsEmpty(ActiveSheet.Cells(counter, "C")) And Not IsEmpty(ActiveSheet.Cells(Row + 1, "C")) Then
If ActiveSheet.Cells(counter, "B").Value = True Then
ActiveSheet.Cells(Row, "M").Value = 100
For i = counter To Row
If IsEmpty(ActiveSheet.Cells(i, "F")) Then
With ActiveSheet.Cells(i, "F")
.Value = Now
.NumberFormat = "dd/mm/yy"
If (.Value - .Offset(0, 2).Value) >= 0 Then
.Font.color = vbRed
Else
.Font.color = vbBlack
End If
End With
End If
Next i
End If
If (ActiveSheet.Cells(Row, "L").Value = 100) Then
For i = counter To Row
If IsEmpty(ActiveSheet.Cells(i, "F")) Then
With ActiveSheet.Cells(i, "F")
.Value = Now
.NumberFormat = "dd/mm/yy"
If (.Value - .Offset(0, 2).Value) >= 0 Then
.Font.color = vbRed
Else
.Font.color = vbBlack
End If
End With
End If
Next i
End If
If Not (ActiveSheet.Cells(counter, "B").Value) = True Then
ActiveSheet.Cells(counter, "M").Value = (Application.Sum(Range(ActiveSheet.Cells(counter, "L"), ActiveSheet.Cells(Row, "L")))) / (Row + 1 - counter)
End If
counter = Row + 1
End If
Next
End Sub
This testcode works for me, just change it as you need it:
Sub TestSumme()
Dim Summe As Long
Summe = Application.WorksheetFunction.Sum(ThisWorkbook.Sheets(1).Range("A1:A6").SpecialCells(xlCellTypeVisible))
MsgBox (Summe)
End Sub