User-defined Function to change color of a cell - vba

I've seen many users asking questions trying to change the colors of cells using User-defined functions. I was always under the impression that it was not possible to do so. My understanding was that a user-defined function cannot change any properties of a cell except the value of the cell that contains the formula. Subs are what change cells themselves.
However, when playing around with some code to test this, I found that it's not always the case.
Using the simple code:
Function ColorCell(rng As Range)
If rng.Value = 1 Then
ColorCell = False
Else
ColorCell = True
rng.Interior.ColorIndex = 3
End If
End Function
If I enter the function into a cell, I achieve expected results, no cells change colors. However, if I use the Formulas > Insert Function button and navigate to my formula to insert it this way, it does color the targeted cells.
How is this possible, and why did the function behave differently when entered in different ways?
EDIT: this was tested using Excel 2007

use this code...just replace sheet name and try
Sheets("sheet_name").range(j:j).clear
for j=2 to 15
if Sheets("sheet_name").Cells(j, 1).value=1 then
else
Sheets("sheet_name").Cells(j, 1).Interior.ColorIndex = 3
next j

As we all find out sooner or later, in user functions you can't access subs that change things in your spreadsheet directly.
But try this:
Dim ColorMeTarget As Range, ColorMeVal As Long
Public Function ColorMe(ByVal TargetRange As Range, ByVal ColVal As Long)
Set ColorMeTarget = TargetRange
ColorMeVal = ColVal
ColorMe = ColVal
End Function
Public Sub ColorMeSub()
Application.OnTime Now + TimeValue("00:00:05"), "ColorMeSub"
If ColorMeTarget.Interior.Color <> ColorMeVal Then ColorMeTarget.Interior.Color = ColorMeVal
End Sub
If you run the sub first, it will constantly scan the static variables ColorMeTarget and ColorMeVal to see if there is a change. The function ColorMe will set these values. Some additional code is needed in case ColorMeTarget is not yet initialized.
If you get smarter, you could have the function first check to see if there is indeed a change and add the new coloring requests to a stack. Your reoccurring sub can then 'catch up', especially if you have many functions like this.
You can then even have all kinds of additional controls added to your function/macro--EVEN STUFF NOT COVERED BY THE LATEST VERSIONS OF 'CONDITIONAL FORMATING'!!! YAY!!!!
Something to try: In some of my automated macros, I am able to set OnTime through a function but cannot make it work here. It would be cleaner to have the function set the OnTime and not have a reoccuring sub that needs initializing.

I use Worksheet_Change event to detect value change in working range. Example.I want to do something when range A1:A5 has been change. I use below event.
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range(<Your working range>)) Is Nothing Then 'Define range that you want to do
'Statement here
...
End If
End Sub
When the range value has been change. It will execute your code.
And other way. Use Conditional Formatting.

could also try the non-script method of auto-coloring a cell based on the condition of the cell (aka Conditional Formatting):

Related

VBA Function #VALUE and debugging disabled

Every time I try to put some arguments in a Function Excel would return #VALUE. Below is one of the examples. Also, I cannot debug when I put arguments in. What is the possible cause? Thank you.
Function lastrowC(SelectedCell As Range)
sc = SelcetedCell.Column
lastrowC = ActiveSheet.Cells(Rows.Count, sc).End(xlUp).Row
End Function
Your code does not work due to a typo. If you add Option Explicit to the top of your code, then try to calculate, VBA will
show you the problem (you misspelled Selected)
Either way, please consider the below code which will target the correct worksheet rather the active worksheet. Your code, as is, will likely look to the wrong sheet to determine the last row under certain circumstances. You need to look at the sheet where the range was selected, which is not always going to be the same as the active sheet
Paste the below code in a Module to call function from excel
Function lastrowC(Target As Range) As Long
With Target.Worksheet
lastrowC = .Cells(.Rows.Count, Target.Column).End(xlUp).Row
End With
End Function

How to pass a column of a selected range

I did a little research and I found my question. The problem is it is written for some language called C that I am not familiar with. Not that I am all that familiar with VBA either but that is what I am working in and doing a very nice job of screwing things up. So in an effort to thwart my abilities to screw things up and instead make things work, how do I pass a sub range or column from a user selected column?
so in my example code I was trying to pass the entire first column of whatever was selected. Lets say user range was B34:D40, I want to pass B34:B40 such that arrange is B34:B40.
Sub initialsub (userrange as range)
Call secondfunction (userrange([],1)
end sub
----------------------------------------------------------------
function secondfunction (arrange as range) as long
some function that counts a variety of things in a single column
secondfunction = 45
end function
One way:
Sub dural()
Dim FirstColumn As Range
Set FirstColumn = Intersect(Selection(1).EntireColumn, Selection)
MsgBox FirstColumn.Address
End Sub
Also:
Sub dural()
MsgBox Selection.Columns(1).Address
End Sub
seems to do the trick.

Run VBA code in Excel based on a cell value change by a function

Kindly I need help after trying for hours myself.
I have this code to run a code base on a cell value change, but works only if I type myself the change.
I need to be automatic with the function I have inside the cell, but I can get it right.
This is the code I have now that works well manually:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Application.EnableEvents = False
Range("D10").Select
Selection.ClearContents
Application.EnableEvents = True
End If
End Sub
Anything that I should add to make it automatic with the formula inside?
This is a tricky one since there is no built in worksheet event that triggers when a particular cell has a value change due to a formula. There is, however, a Worksheet.Calculate event that could be used.
First we make a global string variable. This is a variable that is declared outside of a subroutine or function. It will hold it's value between executions of subroutines and functions. So we will declare it, and then set it to the value of A1 when A1 is changed.
Dim lastValue As String
Private Sub Worksheet_Calculate()
If Range("A1").Value <> lastValue Then
lastValue = Range("A1").Value
Range("D10").ClearContents
End If
End Sub
lastValue here is our Global that will hold the last known value of A1. When worksheet.calculate is triggered it will compare that value against A1 to see if it's changed, if it is then it executes your code to clear the contents of D1
Instead of a Global you could also just stick that last known value of A1 into a Cell somewhere too. Then do you comparison to that. The advantage of using a cell over a global variable, is that the cell value will hold it's value even if you close and reopen the sheet. So, depending on your needs, it may be a better option:
Private Sub Worksheet_Calculate()
lastValue = Range("Z10").value
If Range("A1").Value <> lastValue Then
Range("Z10").value = Range("A1").Value
Range("D10").ClearContents
End If
End Sub
Where Range Z10 is the spot that holds the last known value.
Lastly I reduced your code to clear contents of D10 to a single line. Anytime you see Something.Select and then in the next line see Selection.DoSomething you can just get rid of that Select/Selection bit. Selecting a cell is generally something that only a human needs to do to interact with the sheet. VBA can do whatever it likes to cells without have to Select them.

Excel macro select two ranges and compare

This is a question that was asked to me in an interview. I have a excel list. It is copied to another location and then by mistake a row in the new location gets deleted.
Now I need to write a macro to compare the old and new ranges and then provide the missing data as result.
I can perhaps perform the comparison part. But the problem is I don't know how to get the selected range as input in a macro.
For eg. as soon as I select a range, it should be sent as input to the macro, then the macro should wait for another selection. As soon as I select the new range, the macro should compare and find the missing lines in new range.
Regarding the selection per mouse click you could look at the link I sent in the comments of the other answer. Selection_Change is an event which gets triggered when you change the selection of a worksheet (not only mouseclick but move-by-keys as well). The target coming in is the cell which you have selected. You can pass this as a range on to a function.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
showMsg Target
End Sub
Private Function showMsg(r As Range)
MsgBox r.Address
End Function
You can just as well use another event like BeforeDoubleClick or BeforeRightClick. Check out the events of Excel and choose the one you feel fits best.
If you only want the function to be triggered for a certain range you can filter it.
If target.column <> 1 then exit function
If you don't want the event to trigger your function each time you change a selection you can choose one cell to be the switch which gets triggered by the same event.
If target.address = "$A$1" Then Call toggleSearch()
with toggleSearch being the switching function.
This is a classical diff (and a simple one at that), you shouldn't select by hand or anything. Just sort the two lists in an identical way, then run a Sub which loops over the number of rows in the source sheet comparing each row with the same row in the target sheet. The first mismatch you get is the missing line.
This example assumes both sheets are in the same workbook but you can easily adapt it
Public Sub diffThem()
Dim src as Worksheet, trg as Worksheet
Dim r as Range, i as Integer
Set src = ThisWorkbook.Sheets("Source")
Set trg = ThisWorkbook.Sheets("Destination")
Set r = src.Range("A1")
For i = 1 to ThisWorkbook.Sheets("Source").UsedRange.Rows.Count
If r.EntireRow <> trg.Range("A" & r.Row).EntireRow Then
MsgBox("The missing row is " & r.Row)
Exit Sub
End if
Set r = r.Offset(1,0)
Next i
End Sub
If EntireRow cannot be run due to different layouts or whatever then loop the columns at that point.

Making excel graphs appear/disappear

I want a graph only to appear when a condition is fulfilled. To be more precise: I have a drop down menu that changes the content of a graph. If the menu point "Total revenue" is clicked, I want a second graph to appear. I am new to VBA and this is what I came up with so far:
Sub Iffuntion()
Dim SelectedChart As Range
Dim notVisible As Boolean
If Range("D100").Value = Range("E100").Value Then
ActiveSheet.ChartObjects("Testchart").Visible = True
Else
ActiveSheet.ChartObjects("Testchart").Visible = notVisible
End If
End Sub
It works, but I have to execute the VBA to make the graph appear/disappear and I would like that to happen automatically. Also the condition should eventually be in another worksheet to keep the sheet with the graphs nice and tidy. I read that to achieve this I have toI have to activate the other worksheet. Would you recommend this way or is there a better solution?
Thanks for the help and best regards!
Pete
EDIT: Here is the link to a sample file with the proposed solution of Cor_Blimey, that I couldn't get to work properly. The interconnections in the excel are more complicated than they would have to be, but I wanted to be as accurate ad possible in displaying what is actually happening in my excel. Thanks for taking a look!
https://dl.dropboxusercontent.com/u/18406645/sample.xlsm
Assuming you mean that they change, from a data validation drop down list, the contents of a cell then you can put your code into the Worksheet's Worksheet_Change event. This event is fired when the user changes the content of a cell (or by an external link).
Then test for the cell being the right cell then run your code.
Like:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Intersect(Target, Me.Range("D100"))
If Not rng Is Nothing Then
If rng.Value = Me.Range("E100").Value Then
Me.ChartObjects("Testchart").Visible = True
Else
Me.ChartObjects("Testchart").Visible = False
End If
End If
End Sub