Column format converted to Number, but showing as text - vba

Ive a sheet which is generated using ADO select statement from another sheet. So I have to manually set some formatting. So here is the way I did.
With ThisWorkbook.Sheets("Invoice")
.Columns(1).Resize(.Rows.count - 1, 1).Offset(1, 0).NumberFormat = "yyyy-mmm"
.Columns(6).Resize(.Rows.count - 1, 1).Offset(1, 0).NumberFormat = "dd-mm-yyyy"
.Columns(10).Resize(.Rows.count - 1, 1).Offset(1, 0).NumberFormat = "0.00"
End With
Here column 1 and 6 am converting into two date formats. and column 10 I need to convert into Number format. But 1 and 6 converts fine. But 10 still showing as text and so alligned on left side. Screen grab disabled on our PC, otherwise I could share my actual screen. Hope its clear.

You need to parse it as General first using TextToColumns then apply your formatting. Something like below should work:
With .Columns(10).Resize(.Rows.count - 1, 1).Offset(1, 0)
.TextToColumns Destination:=.Range("A1"), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 1)
'DataType:=xlDelimited, _
'FieldInfo:=Array(1, 1)
.NumberFormat = "0.00"
End with
Or as #Whome commented, if it is all numbers, then you can simply:
With .Columns(10).Resize(.Rows.count - 1, 1).Offset(1, 0)
.Value2 = .Value2
.NumberFormat = "0.00"
End with
Edit1: Explained 1st code above. Using .Range("A1") as destination takes advantage of the Range.Range notation in Excel VBA. This means that you can re-index (not sure if this is the right term) a Range using Range Objects Range method. For example:
Dim r As Range: Set r = Range("B1:C10")
Debug.Print r.Range("A1").Address '/* this gives you $B$1 */
Degug.Print r.Range("B5").Address '/* this gives you $C$5 */
Illustration:
And applying that logic in your example:
.Columns(10).Resize(.Rows.count - 1, 1).Offset(1, 0) '/* refers to J2:J1048576 */
And Range("A1") of that range (or Cells(1) as Jeeped commented) is $J$2.
For your second inquiry, please refer to #Jeeped's comment:
"Sandeep brings up a valid point with his second inquiry. If you aren't going to force all delimiter parameters to false, perhaps DataType:=xlFixedWidth, FieldInfo:=Array(0, 1) would be better. You never know what has been left by the user in terms of TextToColumns delimiter(s) parameters."

Related

VBA-code does not look through hidden rows for adding a row with tracking number

I have another question which I hope to resolve with your help.
What do I want to do.
I use Excel to track my work, activities, contacts, et cetera. While doing that I found I was doing a lot of repetitive work in adding rows at the end of a sheet called "Activities".
What I want to do is this:
- Press a button and adding a row.
- Increase the trackingnumber with 1
- Insert default values
The code.
To automate this, I have found (copy, pasted, adjusted it to my needs) the following code:
Sub AddRowActiviteiten_NewAtEnd()
'Add's a new row at the end of the sheet.
Dim wsActiviteiten As Worksheet
Set wsActiviteiten = Sheets("Activiteiten")
DefType = "Daily"
DefStatus = "Open"
DefIssue = "*****"
DefImpact = "*****"
DefPrio = "Laag"
MyDate = Date
wsActiviteiten.Range("A4").Value = "1"
'Copy the "One Row To Rule Them All"
wsActiviteiten.Range("A3:Q3").Copy
wsActiviteiten.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial (xlPasteAll)
'Stop the "copy-action"
Application.CutCopyMode = False
'Increase the tracking number with "one"
LastNumber = wsActiviteiten.Range("A" & Rows.Count).End(xlUp).Value
wsActiviteiten.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = LastNumber + 1
'Insert default values
LastRow = wsActiviteiten.Range("A" & Rows.Count).End(xlUp).Offset(-1, 0).Row
Cells(LastRow + 1, 2) = DefType
Cells(LastRow + 1, 3) = DefStatus
Cells(LastRow + 1, 4) = DefIssue
Cells(LastRow + 1, 5) = DefImpact
Cells(LastRow + 1, 6) = DefPrio
Cells(LastRow + 1, 8) = MyDate
'Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
End Sub
The problem.
In this sheet I open new items, but I also close them. I do this by changing their status and hide them from view. And this is the point where it goes wrong. When I close the last item on the list and want to add a new row, the macro adds a new row below the last visible entry. It does not find the last entry I have just hidden. And also, when this happens, adding the default values to the new row does not work. It adds them at the row above the added one.
Somehow this makes perfect sense. I tell the macro to look for the last entry, but what I don't understand is why it looks at the last visible entry and why it does not look in the hidden rows.
To replicate. Copy the code into a sheet (maybe you need to change the name of the sheet) and add a few lines. Put some info in the last row and hide it. Add another few lines and see what happens.
The solution. Is there a way to resolve this? Maybe there is a smarter way of doing things? I looked into things, but mostly I got results using "("A" & Rows.Count).End(xlUp)". A loop could work, but I am afraid that 1) It does not search through hidden rows and 2) it makes the sheet (somewhat) sluggish. I must say I have tried to make a loop, first I want to see if my first solution is salvageable.
Thank you for your input, if there are any questions please let me know.
Simon
EDIT: Working code for anyone interested
Sub AddRowActiviteiten_NewAtEnd()
'Add's a new row at the end of the sheet.
Dim wsActiviteiten As Worksheet
Set wsActiviteiten = Sheets("Activiteiten")
DefType = "Daily"
DefStatus = "Open"
DefIssue = "*****"
DefImpact = "*****"
DefPrio = "Laag"
MyDate = Date
'Copy the One Row To Rule Them All
wsActiviteiten.Range("A3:Q3").Copy
'Offset(y,x)
'De -16 is een getal dat iets doet, maar ik weet niet wat.
wsActiviteiten.Range(Split(ActiveSheet.AutoFilter.Range.Address, ":")(1)).Offset(1, -16).PasteSpecial (xlPasteAll)
'Stop the "copy-action"
Application.CutCopyMode = False
'Het volgnummer verhogen met 1
'Het laatste getal selecteren (LastNumber) en dan plus 1.
LastNumber = wsActiviteiten.Range(Split(ActiveSheet.AutoFilter.Range.Address, ":")(1)).Offset(0, -16).Value
wsActiviteiten.Range(Split(ActiveSheet.AutoFilter.Range.Address, ":")(1)).Offset(1, -16).Value = LastNumber + 1
'Insert default values
LastRow = wsActiviteiten.Range(Split(ActiveSheet.AutoFilter.Range.Address, ":")(1)).Offset(-1, 0).Row
Cells(LastRow + 1, 2) = DefType
Cells(LastRow + 1, 3) = DefStatus
Cells(LastRow + 1, 4) = DefIssue
Cells(LastRow + 1, 5) = DefImpact
Cells(LastRow + 1, 6) = DefPrio
Cells(LastRow + 1, 8) = MyDate
'Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
End Sub
Update
I see your sheet has an autofilter "hiding" the status rows - which Find wont detect, unlike hidden rows.
Suggest you try this updated code below:
Sub Test()
Dim rng1 As Range
If ActiveSheet.AutoFilterMode Then
MsgBox ActiveSheet.Range(Split(ActiveSheet.AutoFilter.Range.Address, ":")(1)).Row
Else
Set rng1 = Columns("A:A").Find("*", [a1], xlFormulas, , xlByRows, xlPrevious)
If Not rng1 Is Nothing Then MsgBox rng1.Row
End If
End Sub
initial post
If you are hiding rows then you can use Find with the xlFormulas option to find entries in hidden rows (unlike xlValues).
Dim rng1 As Range
Set rng1 = Columns("A:A").Find("*", [a1], xlFormulas, , xlByRows, xlPrevious)
MsgBox rng1.Address
Say we have a status column AB and we currently close an item by placing the word "Closed" in that column and then hiding the row.
Instead:
Unhide all rows
Perform any required inserts and edits
Via a loop, hide all rows marked "Closed"
Use this for getting the last row and it will see the last row, even if it is hidden.
LastRow = wsActiviteiten.UsedRange.Rows.Count
I've just found solution:
If you have at least one column in your range with 'consistent' data (all cells in that column are not empty/blank) you can use formula COUNTA and you can reference you code to value of that COUNTA formula.
For example:
Insert formula '=COUNTA(A1:A100000)' in 'B1' cell.
In B1 you will get how many rows you have they are hidden or not.
In your code change:
wsActiviteiten.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial (xlPasteAll)
to
wsActiviteiten.Range("A" & Range("B1").Value).Offset(1, 0).PasteSpecial (xlPasteAll)
Of course, if, for example, cell 'A2' is blank and this is the only blank cell you will need to adjust your formula to '=COUNTA(A1:A100000) +1'.
If you have more blank/empty cells and you don't know the exact number of them (blank cells have been changed dynamically) this method will not work.
As I said previously you need to have at least one column with 'consistent' data (with known number of empty cells in advance if any).

VBA using like operator with wildcards

I'm trying to compare address data. My current macro compares two columns and inputs "Dropped Data" when they don't match. The problem is that a large number of these values haven't been dropped but integrated into another cell. I want to change my macro to be able to find the missing value using VBA's like operator. For example it would find "Bldg 3" in "9825 Spectrum Dr Bldg 3". I was able to get this code from looking around the web and I'm not sure what range Range("C65536") is selecting.
Edit: I see people are suggesting I use the Instr function which does seem to do what I want to do. I'm not sure how I would get it to work in my macro/ get it to reference the correct cells. It also (from what I understand) returns values equal to the number of characters found. So in the example I gave it would return a value of 6 if you include the space.
Sub droppeddata()
Application.ScreenUpdating = False
lr = Range("C65536").End(xlUp).Row
For a = lr To 1 Step -1
If Not IsEmpty(Cells(a, 13).Value) And IsEmpty(Cells(a, 19)) Then
Cells(a, 10).Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "N"
Cells(a, 11).Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "Dropped Data"
End If
Next a
Application.ScreenUpdating = True
End Sub
Your current macro doesn't compare anything the way you want it to, it just checks whether or not two columns are empty.
You haven't been very specific with what you are trying to do, so this code is done with a bit of guess-work:
Sub droppeddata()
Dim lr As Long ' Declare the variable
lr = Range("C65536").End(xlUp).Row ' Set the variable
' lr now contains the last used row in column C
Application.ScreenUpdating = False
For a = lr To 1 Step -1
If IsEmpty(Cells(a, 19)) Or InStr(1, Cells(a, 13).Value, Cells(a, 19).Value, vbTextCompare) > 0 Then
' If Cells(a, 19) is empty OR
' Search Cells(a, 13) for the value contained in Cells(a, 19)
' If INSTR returns a match greater than 0, it means the string we're looking for is present
' Enter the loop if either condition is true
' In this section, avoiding SELECT is preferable. Working directly on the ranges is good.
With Cells(a, 10)
.NumberFormat = "General"
.Value = "N"
End With
With Cells(a, 11)
.NumberFormat = "General"
.Value = "Dropped Data"
End With
End If
Next a
Application.ScreenUpdating = True
End Sub
Change the ranges/cells to your need - the current ones aren't meant to work, I merely guessed based on your existing code.

VBA Right-Function returning wrong data type

I have written a very simple code which returns the last 6 characters of every active cell within a range.
The code works pretty good until it finds a particular cell in which the characters to be returned should be: "MARC01". Unfortunately it returns a date type character (01.Mrz).
By using the normal excel formula it works fine, that is why I would expect it to work with a Macro as well.
Here you can see my code which takes the strings from column "A" and enters it in column "B":
Range("B12").Activate
Do
ActiveCell.Value = Right((ActiveCell.Offset(0, -1).Value), 6)
ActiveCell.Offset(1, 0).Activate
Loop Until ActiveCell.Offset(0, -1).Value = 0
Excel likes to change anything that looks like a possible date to a date. To force this not to happen put a "'" in front of the formula.
ActiveCell.Value = "'" & Right((ActiveCell.Offset(0, -1).value), 6)
This will force it to stay text. The down side to this is, if it is a number it will be saved as text.
Excel likes to try to interpret certain data, rather than just leaving it as is. It especially does that with strings that look like dates, and with numeric entries.
Two ways to workaround are
Put the text prefix character in front of your string. This is usually a single quote. (see Scott's answer for code)
Format the cell as Text before you place the value there.
Sub foo()
Range("B12").Activate
Do
ActiveCell.NumberFormat = "#"
ActiveCell.Value = Right((ActiveCell.Offset(0, -1).Formula), 6)
ActiveCell.Offset(1, 0).Activate
Loop Until ActiveCell.Offset(0, -1).Value = 0
End Sub
With this simple goal, I don't know why you need VBA looping.
You can just mass set the formular1c1 to =RIGHT(RC[-1],6).
Option Explicit
Sub Right6()
Const R6LeftCol = "=RIGHT(RC[-1],6)"
Dim oRng As Range, lRow As Long
lRow = Cells(Rows.Count, "A").End(xlUp).Row
Set oRng = Range("B12")
Range(oRng, Cells(lRow, "B")).FormulaR1C1 = R6LeftCol
Set oRng = Nothing
End Sub

Numbers to dates

I have a date in number form which looks like this: 20150529 is there a way to convert this into 29/05/2015 ?
I need to write a macro for this.
I have this code but it doesn't work:
Dim Current_Date As Date
Dim Date_String As String
Range("K2").Select
Date_String = Range("K2").Value
Current_Date = CDate(Date_String)
Range("Q2").Select
Range("Q2").Value = Current_Date
The Range.TextToColumns method can make quick work of one or more cells in a selection or an entire column of 20150629 like dates.
sub YMD_2_Date
with selection
.TextToColumns Destination:=.cells(1), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 5)
end with
end sub
The xlColumnDataType property of xlYMDFormat (e.g. 5) forces Excel to consider the number as Year-Month-Day.
This could be expanded from a selection (one or more cells in a single column) to multiple columns by cycling through them.
You have to rearrange your string in American format first (mm/dd/yyyy).
Then you can use CDate().
r = Mid(Date_String , 5, 2) & "/" & Right(Date_String , 2) & "/" & Left(Date_String , 4)
current_date = CDate(r)
This also works now
Range("K2", Range("K2").End(xlDown)).Select
For Each c In Selection.Cells
c.Value = DateSerial(Left(c.Value, 4), Mid(c.Value, 5, 2), Right(c.Value, 2))
'Following line added only to enforce the format.
c.NumberFormat = "mm/dd/yyyy"
Next

Runtime Error 1004 using Select with several workbooks

I have an Excel workbook which pulls out data from two other workbooks.
Since the data changes hourly there is the possibility that this macro is used more than one time a day for the same data.
So I just want to select all previous data to this date period and want to delete them.
Later on the data will be copied in anyway.
But as soon as I want to use
WBSH.Range(Cells(j, "A"), Cells(lastRow - 1, "M")).Select
the code stopes with Error 1004 Application-defined or object-defined error.
Followed just a snippet of the code with the relevant part.
What is wrong here?
'Set source workbook
Dim currentWb As Workbook
Set currentWb = ThisWorkbook
Set WBSH = currentWb.Sheets("Tracking")
'Query which data from the tracking files shoud get pulled out to the file
CheckDate = Application.InputBox(("From which date you want to get data?" & vbCrLf & "Format: yyyy/mm/dd "), "Tracking data", Format(Date - 1, "yyyy/mm/dd"))
'states the last entry which is done ; know where to start ; currentWb File
With currentWb.Sheets("Tracking")
lastRow = .Range("D" & .Rows.Count).End(xlUp).Row
lastRow = lastRow + 1
End With
'just last 250 entries get checked since not so many entries are made in one week
j = lastRow - 250
'Check if there is already data to the look up date in the analyses sheet and if so deletes these records
Do
j = j + 1
'Exit Sub if there is no data to compare to prevent overflow
If WBSH.Cells(j + 1, "C").Value = "" Then
Exit Do
End If
Loop While WBSH.Cells(j, "C").Value < CheckDate
If j <> lastRow - 1 Then
'WBSH.Range(Cells(j, "A"), Cells(lastRow - 1, "M")).Select
'Selection.ClearContents
End If
Thank you!
Actually you can't use Select for on Range on a Sheet which is not selected/activated. Select only works on the active worksheet.
You should have something like
WBSH.Activate
before
WBSH.Range(Cells(j, "A"), Cells(lastRow - 1, "M")).Select
WBSH.Range(WBSH.Cells(j, "A"), WBSH.Cells(lastRow - 1, "M")).Select
Take a look here: Selecting and activating Cells
NOTE: Avoid using Select as much as you can
EDIT: To clear the content (As asked in the comments), you should use someting like
WBSH.Range(WBSH.Cells(j, "A"), WBSH.Cells(lastRow - 1, "M")).Clear
instead of (I imagine)
WBSH.Range(Cells(j, "A"), Cells(lastRow - 1, "M")).Clear
because the cells in your range needs to be qualified.
You can use Clear which deletes the formatting of the cell and the value in that cell, and ClearContent which deletes only the value in the cell.
Note that you can use With:
With WBSH
.Range(.Cells(j, "A"), .Cells(lastRow - 1, "M")).Clear
End With