Problem with Update function on a ListObject - excel

This is was supposed to be a prof of concept, I have a much larger project with the same issue and have retied different approaches and always end up with the same results. I was creating additional errors in my main code so I wrote this to test myself on the functionality of what I am doing. So just a heads up, I am coming back into programming after a 8 Year stint doing Engineering/Tech Stuff in the field. And therefor I am making some Newbie mistakes again. I need help seeing my error.
When I attempt to update the .Value of a Cell in a listObject it evokes a event function on the UserForm.
To explain a bit more:
I have a table - Table1 with ID|Arg1|Arg2|Arg3|Arg4
I put a simple Int in ID ie 1, and simple text(string) Data in Arg1-Arg4 ie Test1, Data1, Data2, Data3
I created a Simple UserForm1(Default Name) to put a listBox and 4 TextBoxes
I have a Function ListBox1_Click() that populates the UserFrom1 Textboxes with the data from the ListObject(Table1). This function works just fine.
I also have a function that Updates the ListObject(Table1).
Here is the Problem. When you reference the ListObject(Table1)'s Cell Value to change it, Then ListBox1_Click() Event Function gets evoked and my TextBoxes Revert. I have Destructers for the my Object that I 'Set', But I have no idea why/how the function is called.
Private Sub cbUpdate_Click()
Dim myListObj As ListObject
Dim myRange As Range
Set myListObj = ActiveSheet.ListObjects("Table1")
Set myRange = myListObj.ListColumns(1).DataBodyRange.Find(What:= _
Trim(Me.ListBox1), SearchDirection:=xlNext, MatchCase:=False)
myRange.Offset(, 1).Value = Me.tbArg1
myRange.Offset(, 2).Value = Me.tbArg2
myRange.Offset(, 3).Value = Me.tbArg3
myRange.Offset(, 4).Value = Me.tbArg4
Set myRange = Nothing
Set myListObj = Nothing
End Sub
Private Sub ListBox1_Click()
Dim myListObj As ListObject
Dim myRange As Range
Set myListObj = ActiveSheet.ListObjects("Table1")
Set myRange = myListObj.ListColumns(1).DataBodyRange.Find(What:= _
Trim(Me.ListBox1), SearchDirection:=xlNext, MatchCase:=False)
Me.tbArg1 = myRange.Offset(, 1).Value
Me.tbArg2 = myRange.Offset(, 2).Value
Me.tbArg3 = myRange.Offset(, 3).Value
Me.tbArg4 = myRange.Offset(, 4).Value
Set myRange = Nothing
Set myListObj = Nothing
End Sub
I need to understand how to get the ListObject(Table1) Cells to receive updated values without evoking uncalled functions/Methods.

Ok, so them answer seems to be that I need a local variable to pass the value of the userForm controls thru. When I used the listObjects .Add function and passed data durectly from the controls(TextBoxs) it works great. But, When Updating if I pass values directly from the control it invokes the listbox's _Click() event function. If I 'Dim myArg1 As Variant' and assign the controls value to it and them when updating the ListObject Cell value from that, presto it takes the value with no issues. I'm note sure why this works, but, I get to code on! I hate my answer more than I hated my problem. I spent days on trouble shooting this issue before deciding to post it. My Answer is not very elegant but it seems to be functional. Thanks for reading.

Related

how to update chart with variable?

Hope someone can help I'm having trouble with something that should be really simple.
I'm trying to update a chart based on changing source data (number of rows can change)
this is my code:
lastRow = GetLastRow(ChartSourceSheet)
ActiveChart.SetSourceData Source:=Sheets("ChartSource").Range("A1:C" & lastRow)
for some reason it doesn't work (getlastrow function is just a function that returns the last row number of a sheet, in this case it's 5 with my test data)
but if i hard code the change it to:
lastRow = 5
ActiveChart.SetSourceData Source:=Sheets("ChartSource").Range("A1:C" & lastRow)
then it works absolutely fine, and it's driving me nuts trying to figure out why.
I've tried declaring lastRow as integer but it doesn't work.
Anybody have any ideas what I can try?
edit in response to replies:
doesn't work means the chart just doesn't update and when I look at the data range manually it hasn't updated (in comparison to when i hardcoded the value and it updated fine) Also I tried setting lastRow to long as well and that didn't help.
My function works fine, as it's returning the expected value (5) but for completion purposes here's the code to my function:
Function GetLastRow(sheetName As Worksheet)
GetLastRow = 1
On Error Resume Next
GetLastRow = sheetName.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
On Error GoTo 0
End Function
You should likely make sure that GetLastRow() returns a Long variable:
Sub doChart()
Dim lastRow As Long
lastRow = GetLastRow(Sheets("Sheet1")) ' Change to whatever you need
' Next line requires the chart to be active. You'd be better off actually directly
' calling it, but that's for you to tweak. If there's only one chart,
' then try Charts(1).SetSourceData ...
ActiveChart.SetSourceData Source:=Sheets("ChartSource").Range("A1:C" & lastRow)
End Sub
Function GetLastRow(sheetName As Worksheet) As Long
GetLastRow = 1
GetLastRow = sheetName.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
End Function
Also, I removed the On Error ... in the function, because there shouldn't be any errors, and if there are it'd be best to handle. (Worst case is that would return 1 as far as I can tell)
Thanks for the comments and advice
Managed to figure it out in the end.
It's because I was deleting the cells that the graph was referring to
ws.cells.delete
i changed this to
ws.cells.clear
and there was no longer a problem.

How to loop Range.Find in VBA?

I am new to VBA so please don't judge too harsh. Having said that below is my issue with Range.Find.
I have a crosstab with a column that has "https" link to pictures; and I have a working VBA to turn these links into actual pictures in each cell for that column. However, my issue is when I add another column into the Crosstab or move column around, my VBA stops working and I end up with my links without actual pictures (since, the picture code is set to the initial column where my links reside).
I figured there should be a way to make it more dynamic by using Range.Find. I have managed to find information on Range.Find, but my code won't work. Is there anyway anyone could help out?
Here is the code:
Function picRng() As Range
Set picRng = ActiveSheet.Range("A1:Z1000")
Set rngFindValue = ActiveSheet.Range("A1:Z1000").Find(what:="http", Lookat:=xlPart)
Do
Set rngFindValue = Search.FindNext(rngFindValue)
Loop While Not rngFindValue is Nothing
End Function
if you want to loop thru all the instances of the search arguments here is the correction of your code
Function picRng() As Range
Set picRng = ActiveSheet.Range("A1:Z1000")
Set rngfindvalue = picRng.Find(what:="http", Lookat:=xlPart)
If Not rngfindvalue Is Nothing Then
rngFirstAddress = rngfindvalue.Address
Do
MsgBox rngfindvalue.Address
Set rngfindvalue = picRng.FindNext(rngfindvalue)
Loop Until rngfindvalue Is Nothing Or rngfindvalue.Address = rngFirstAddress
End If
End Function
You do not need a loop for Find(). If you need the last value in Find(), you need to refer it in the arguments (searchDirection) Something like this will give the last value:
Public Function LocateFind() As Range
Dim rngCell As Range
Dim rngRangeToLookAt As Range
Set rngRangeToLookAt = Range("A1:A100")
Set LocateFind = rngRangeToLookAt.Find("YourValueHere", searchdirection:=xlPrevious)
End Function
Set lx_rangeFind = ActiveSheet.UsedRange.Find(What:=strToFind, LookIn:=xlValues, LookAt:=xlPart)
Set lx_rangeFindFirst = lx_rangeFind
Do
Set lx_rangeFind = ActiveSheet.UsedRange.Find(What:=strToFind, LookIn:=xlValues, LookAt:=xlPart, After:=lx_rangeFind)
'Rest of the code
'now lx_rangeFind has the cell Number
Loop While lx_rangeFindFirst.Address <> lx_rangeFind.Address

Object Required Error VBA Function

I've started to use Macros this weekend (I tend to pick up quickly in regards to computers). So far I've been able to get by with searching for answers when I have questions, but my understanding is so limited I'm to a point where I'm no longer understanding the answers. I am writing a function using VBA for Excel. I'd like the function to result in a range, that can then be used as a variable for another function later. This is the code that I have:
Function StartingCell() As Range
Dim cNum As Integer
Dim R As Integer
Dim C As Variant
C = InputBox("Starting Column:")
R = InputBox("Starting Row:")
cNum = Range(C & 1).Column
Cells(R, cNum).Select
The code up to here works. It selects the cell and all is well in the world.
Set StartingCell = Range(Cell.Address)
End Function
I suppose I have no idea how to save this location as the StartingCell(). I used the same code as I had seen in another very similar situation with the "= Range(Cell.Address)." But that's not working here. Any ideas? Do I need to give more information for help? Thanks for your input!
Edit: I forgot to add that I'm using the InputBox to select the starting cell because I will be reusing this code with multiple data sets and will need to put each data set in a different location, each time this will follow the same population pattern.
Thank you A.S.H & Shai Rado
I've updated the code to:
Function selectQuadrant() As Range
Dim myRange As Range
Set myRange = Application.InputBox(Prompt:="Enter a range: ", Type:=8)
Set selectQuadrant = myRange
End Function
This is working well. (It appears that text is supposed to show "Enter a range:" but it only showed "Input" for the InputBox. Possibly this could be because I'm on a Mac?
Anyhow. I was able to call the function and set it to a new variable in my other code. But I'm doing something similar to set a long (for a color) so I can select cells of a certain color within a range but I'm getting all kinds of Object errors here as well. I really don't understand it. (And I think I'm dealing with more issues because, being on a mac, I don't have the typical window to edit my macros. Just me, basically a text box and the internet.
So. Here also is the Function for the Color and the Sub that is using the functions. (I've edited both so much I'm not sure where I started or where the error is.)
I'm using the functions and setting the variables to equal the function results.
Sub SelectQuadrantAndPlanets()
Dim quadrant As Range
Dim planetColor As Long
Set quadrant = selectQuadrant()
Set planetColor = selectPlanetColor() '<This is the row that highlights as an error
Call selectAllPlanets(quadrant, planetColor)
End Sub
This is the function I'm using to select the color that I want to highlight within my range
I would alternately be ok with using the interior color from a range that I select, but I didn't know how to set the interior color as the variable so instead I went with the 1, 2 or 3 in the input box.
Function selectPlanetColor() As Long
Dim Color As Integer
Color = InputBox("What Color" _
& vbNewLine & "1 = Large Planets" _
& vbNewLine & "2 = Medium Planets" _
& vbNewLine & "3 = Small Planets")
Dim LargePlanet As Long
Dim MediumPLanet As Long
Dim smallPlanet As Long
LargePlanet = 5475797
MediumPlanet = 9620956
smallPlanet = 12893591
If Color = 1 Then
selectPlanetColor = LargePlanet
Else
If Color = 2 Then
selectPlanetColor = MediumPlanet
Else
If Color = 3 Then
selectPlanetColor = smallPlanet
End If
End If
End If
End Function
Any help would be amazing. I've been able to do the pieces individually but now drawing them all together into one sub that calls on them is not working out well for me. Thank you VBA community :)
It's much simpler. Just
Set StartingCell = Cells(R, C)
after getting the inputs, then End Function.
The magic of the Cells method is it accepts, for its second parameter, both a number or a character. That is:
Cells(3, 4) <=> Cells(3, "D")
and
Cells(1, 28) <=> Cells(3, "AB")
One more thing, you can prompt the user directly to enter a range, with just one input box, like this:
Dim myRange as Range
Set myRange = Application.InputBox(Prompt:="Enter a range: ", Type:=8)
The Type:=8 specifies the input prompted for is a Range.
Last thing, since you are in the learning process of VBA, avoid as much as possible:
using the Select and Activate stuff
using unqualified ranges. This refers to anywhere the methods Cells(..) or Range(..) appear without a dot . before them. That usually leads to some random issues, because they refer to the ActiveSheet, which means the behavior of the routine will depend on what is the active worksheet at the moment they run. Avoid this and always refer explicitly from which sheet you define the range.
Continuing your line of thought of selecting the Range bu Selecting the Column and Row using the InputBox, use the Application.InputBox and add the Type at the end to restrict the options of the user to the type you want (Type:= 1 >> String, Type:= 2 >> Number).
Function StartingCell Code
Function StartingCell() As Range
Dim cNum As Integer
Dim R As Integer
Dim C As Variant
C = Application.InputBox(prompt:="Starting Column:", Type:=2) '<-- type 2 inidcates a String
R = Application.InputBox(prompt:="Starting Row:", Type:=1) '<-- type 1 inidcates a Number
Set StartingCell = Range(Cells(R, C), Cells(R, C))
End Function
Sub TestFunc Code (to test the function)
Sub TestFunc()
Dim StartCell As Range
Dim StartCellAddress As String
Set StartCell = StartingCell '<-- set the Range address to a variable (using the function)
StartCellAddress = StartCell.Address '<-- read the Range address to a String
End Sub

Searching a range of columns defined by variables in VBA

I'm trying to search a range of columns on a different worksheet, where the range is defined by two separate variables. I have successfully been able to use the same code to search a range of columns that I manually inputted, but using variables result in an error:
Run-time error '1004':
Application-defined or object-defined error
I am using the code to search a separate worksheet for the column number of the first instance of the month and then search a range beginning with that column number for the specific day.
An example of the worksheet I'm searching through:
http://i.imgur.com/ljmmGGi.png
Below is the code. Specifically, the MonthFind function has worked perfectly, but the subsequent DayFind function, which uses output from MonthFind is acting up.
Private Sub ComboBox21_Change()
Dim i As String
Dim j As String
i = "February"
j = 9
Dim MonthFind As Variant
With Sheets("Project Schedule").Range("A1:ZZ1")
Set MonthFind = .Find(i, LookAt:=xlWhole, MatchCase:=False)
End With
Dim Month1 As Integer
Dim Month2 As Integer
Month1 = MonthFind.Column
Month2 = MonthFind.Column + 12
Dim DayFind As Variant
With Sheets("Project Schedule").Range(Columns(Month1), Columns(Month2))
Set DayFind = .Find(j, LookAt:=xlWhole, MatchCase:=False)
End With
End Sub
Any help would be much appreciated, I've been trying so many different variations of this code to no avail!
Edit - Link to Excel file: https://www.dropbox.com/s/275fo0uucfeum3y/Project%20Scheduling%20SO.xlsm?dl=0
I almost gave up on this, but I found out what the problem was.
Your ComboBox_21 object has an input range (which will fill in the combobox with the selectable values) on the Inputs sheet that uses a bunch of formulas that reference the Project Schedule sheet. Whenever you do all those copy/paste functions against the range in Project Schedule that the combobox relies on, you are effectively changing the data for the dropdown box, and in turn, causing the _Change() event to fire off with every paste that effects that area.
This isn't always a problem (though in my opinion, it's causing a lot of unnecessary code execution), but there's this bit of code that is causing an issue in your AddJob1_Click() event:
Range(Sheet1.Cells(erow, 5), Sheet1.Cells(erow + 3, 10000)).ClearContents
Apparently you're not allowed to perform the Range.Find() method when the contents of the cells that the combobox rely on are being changed.
Here's some info about that, though it's not terribly helpful:
https://msdn.microsoft.com/en-us/library/office/aa221581(v=office.11).aspx
So that's the why, now how to fix it:
Defer automatic calculation until your UserForm code is finished doing whatever it needs to do.
This will ensure that you are allowed to do the Find's and referencing you need to do.
Private Sub AddJob1_Click()
' turn off automatic calculation
Application.Calculation = xlCalculationManual
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
' ... other stuff
' turn calculation back on and perform a calculate, which will fire off the ComboBox21_Change() event
Application.Calculation = xlCalculationAutomatic
Application.Calculate
Unload Me
End Sub

selection.MergeCells property changes the original selection in excel

I want to check if my selection contains MergeCells and if so I do not want to process that selection in excel.
But whenever I call selection.MergeCells the original selection changes and it wraps the areas bounded by MergeCells which I DO NOT want.
I have checked the below MSDN link, which tell that it is a limitation/bug and can not be solved , so can anyone please help me?
http://social.msdn.microsoft.com/Forums/en-US/exceldev/thread/63920347-c731-4046-b96f-3f3e7eabddd8
The problem in your case is slightly different from the link that you point to (because they are in the SelectionChanged event). In your case, the problem is that you are using Selection.MergeCells in you VBA code. Don't do that.
You should always avoid using Select-related actions in your own VBA code (because it is slow and worse, has tons of unintended side-effects, like this). Instead, use range objects. But because the Selection Range itself is so closely bound to the Selection object, you may need to disassociate it, like so:
Dim rng As Range, ws As Worksheet
'get the current worksheet
Set ws = ActiveSheet
'get the selection-range
Set rng = Selection
'get the same range, but disassociated from the selection
'(I think this works?)
Set rng = ws.Range(rng.AddressLocal)
If rng.MergeCells Then ...
Let me know if this does not work (I cannot test it right now), as there is a more complicated approach that can be used intead.
I ran across this post and discovered that the answer does not work.
To see why, try this code
Cells.UnMerge
Cells.ClearFormats
[4:4,11:11,21:21].Select
Selection.Interior.Color = vbYellow
[a2:b5].Merge
[a19:b22].Merge
Debug.Print Selection.Address
Debug.Print [a1].MergeCells
Debug.Print Selection.Address
End Sub
The only solution is something like the following. Sorry I don't have time to flesh it out anymore.
dim mergedCells as collection, oldSelAddress as string, var as variant
oldSelAddress = selection.address
debug.print [a1].mergecells
if oldSeladdress <> selection.address then
set mergedCells = collectMergedCells(selection)
selection.unmerge
range(oldseladdress).select
for each var in mergedCells
var.merge
next
end if

Resources