I have a problem that I hope someone can help with. I have a sheet that gives a total using a sumif and I want to popup a message box that contains the values of the cells that were called by the sumif.
I have the code that will list all the cells in the range but not just the ones called by the sumif.
I have attached a screenshot of the sheet and the VBA code is below.
I have looked at precedents but in my real spreadsheet the sumif criteria key is on another sheet.
In the picture attached A8 contains the value SUM=(A1:A7) but should contain =SUMIF(B1:B7,D1,A1:A7) and the msgbox should contain a list of the values picked up.
Private Function RangeToString(ByRef rngDisplay As Range, ByVal strSeparator As String) As String 'The string to separate elements on the message box, 'if the range size is more than one cell Dim strMessage As String Dim astrMessage() As String Dim avarRange() As Variant Dim varElement As Variant Dim i As Long 'If the range is only one cell, we will return that that If rngDisplay.Cells.Count = 1 Then strMessage = rngDisplay.Value 'Else the range is multiple cells, so we need to concatenate their values Else 'Assign range to a variant array avarRange = rngDisplay 'Loop through each element to build a one-dimensional array of the range For Each varElement In avarRange ReDim Preserve astrMessage(i) astrMessage(i) = CStr(varElement) i = 1 + i Next varElement 'Build the string to return strMessage = Join(astrMessage, strSeparator) End If RangeToString = strMessage End Function Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) If Target.Address(True, True, xlA1) = "$A$8" Then MsgBox RangeToString(Sheet2.Range("A1:A7"), vbCrLf), , "Items" End If End Sub
Last edited by installer69; 11-26-2010 at 04:23 PM.
If you're using code anyway, don't use SUMIF, loop through the values and build up a range where the condition is true. When you've processed the source range, you will be able to display the ouput range ... the cells that meet the criteria.
Regards
TMShucks has a very good point, but I didn't see it until I previewed my completed answer.
The following code will do what you want. It is specific to the sheet you have shown as an example. To generalize it, you will have to change the hard-coded references to cells A8 and D1 that you see in the first few lines.
You would put this code into the module for the sheet containing the data.
' This will show a message listing the values ' summed by the SUMIF in A8 every time the value ' in D1 is updated Public Sub Worksheet_Change(ByVal Target As Range) If Target.Address = Range("D1").Address Then ' Get formula Dim f As String ' the formula f = Range("A8").Formula Dim msg As String ' the message you will display to the user Dim a As Variant ' an array to capture the arguments of SUMIF Const fPart As String = "=SUMIF(" ' Break out the arguments of SUMIF a = Split(Mid(f, Len(fPart) + 1, Len(f) - Len(fPart) - 1), ",") Dim SUMIF_Range As String Dim SUMIF_Criteria As String Dim SUMIF_Sum_Range As String ' Assign arguments to variables for readability SUMIF_Range = a(0) SUMIF_Criteria = a(1) SUMIF_Sum_Range = a(2) Dim c As Range ' each cell in the SUMIF range Dim i As Long ' keep track of what row we are on within the range i = 0 For Each c In Range(SUMIF_Range) i = i + 1 If c.Value = Range(SUMIF_Criteria) Then msg = msg & CStr(Range(SUMIF_Sum_Range).Cells(i, 1)) & vbCrLf End If Next c MsgBox msg End If
Last edited by 6StringJazzer; 11-26-2010 at 07:55 PM. Reason: changed + to & in msg assignment, though I think + will work anyway
Making the world a better place one fret at a time
||||||
If someone helped you, please click on the star icon at the bottom of their post
If your problem is solved, please update the first post:
EDIT, Go Advanced button, set Prefix to SOLVED
[code]
' Enclose code in tags like this
[/code]
Don't attach a screenshot--just attach your Excel file! It's easier and will let us experiment with your data, formulas, and code.
Thankyou for your time and interest. I hadn't even thought about coding the SUMIF so you freshened my thought process. I am a user of many coding languages but a master of none.
As you may have guessed the included sample sheet is just that, a small blob of my actual project (a project that may save me all of 10 minutes a month!!)
I feel guilty asking you for further help as you have already been so gracious and given me a whole answer to my question but can you tell me if it is possible to also display the contents of B next to A in the Msgbox?
i.e. 1 dog
1 dog
in the case of this spreadsheet. In my actual sheet it would help to display the related entry date.
Also, could the msgbox be triggered by clicking in A8 instead of just when D1 is changed? I tried changing If Target.Address = Range("D1").Address Then for If Target.Address(True, True, xlA1) = "$A$8" Then but that didn't work
OK. I persevered by trial and error and got what I needed in bringing up the bit from a date column.
If it helps anyone else trying to do something similar this is what I added. First I stuck a column of dates in D then at the end of the code where it used the following to select the data to be displayed in the messagebox:
msg = msg & CStr(Range(SUMIF_Sum_Range).Cells(i, 1)) & vbCrLfIt wasn't much of a mod but it achieved exactly what I needed. Just need help doing the part where clicking in the SUMIF cell (in my case A8) brings the messagebox up.msg = msg & CStr(Range(SUMIF_Sum_Range).Cells(i, 1)) & " " & CStr(Range(SUMIF_Sum_Range).Cells(i, 3)) & vbCrLf
Last edited by installer69; 11-27-2010 at 08:10 AM.
This now works great for me thanks to your help but I need just a little more. If I click in cell E10 which contains the result of a SUMIF it pops up a messagebox with the items that made up that result. I have SUMIF formulas in E11 and E12 for the next 2 months. How can I add to this code so that clicking in those cells pops up a message box containing their items? Thanks in advance.
' This will show a message listing the values ' summed by the SUMIF in E10 every time the value ' in D1 is updated Public Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) If Target.Address = "$E$10" Then ' Get formula Dim f As String ' the formula f = Range("E10").Formula Dim msg As String ' the message you will display to the user Dim a As Variant ' an array to capture the arguments of SUMIF Const fPart As String = "=SUMIF(" ' Break out the arguments of SUMIF a = Split(Mid(f, Len(fPart) + 1, Len(f) - Len(fPart) - 1), ",") Dim SUMIF_Range As String Dim SUMIF_Criteria As String Dim SUMIF_Sum_Range As String ' Assign arguments to variables for readability SUMIF_Range = a(0) SUMIF_Criteria = a(1) SUMIF_Sum_Range = a(2) Dim c As Range ' each cell in the SUMIF range Dim i As Long ' keep track of what row we are on within the range i = 0 For Each c In Worksheets("jan-apr").Range(SUMIF_Range) i = i + 1 If c.Value = Range(SUMIF_Criteria) Then msg = msg & CStr(Worksheets("jan-apr").Range(SUMIF_Sum_Range).Cells(i, -3)) & " - " & CStr(Worksheets("jan-apr").Range(SUMIF_Sum_Range).Cells(i, 1)) & vbCrLf End If Next c MsgBox msg, , "Results" End If End Sub
You can use these two UDF's.
ConcatIf mirrors SUMIF, with additional (optional) Delimiter and NoDuplicate arguments.
ThreeFormulas takes the result of the three arguments (formulas) and puts them in the cell, the input validation Title and the input validation Message.
In the attachment, E2 contains "North" and the formula in F2
=ThreeFormulas(SUMIF(A:A,E2,C:C), Concatif(A:A,E2,B:B,","), Concatif(A:A,E2,C:C,"+"))
puts the sum of all amounts (colC) that come from the North region (column A) in F2.
When F2 is selected, box will pop up with "Bob,Bob,Dave,Bob,Dave" as the title (the people in column B who contributed to that sum) and "10+15+8+56+12" as the message (the amounts that made up the sum).
Function ConcatIf(ByVal compareRange As Range, ByVal xCriteria As Variant, Optional ByVal stringsRange As Range, _ Optional Delimiter As String, Optional NoDuplicates As Boolean) As String Dim i As Long, j As Long With compareRange.Parent Set compareRange = Application.Intersect(compareRange, Range(.UsedRange, .Range("a1"))) End With If compareRange Is Nothing Then Exit Function If stringsRange Is Nothing Then Set stringsRange = compareRange Set stringsRange = compareRange.Offset(stringsRange.Row - compareRange.Row, _ stringsRange.Column - compareRange.Column) For i = 1 To compareRange.Rows.Count For j = 1 To compareRange.Columns.Count If (Application.CountIf(compareRange.Cells(i, j), xCriteria) = 1) Then If InStr(ConcatIf, Delimiter & CStr(stringsRange.Cells(i, j))) <> 0 Imp Not (NoDuplicates) Then ConcatIf = ConcatIf & Delimiter & CStr(stringsRange.Cells(i, j)) End If End If Next j Next i ConcatIf = Mid(ConcatIf, Len(Delimiter) + 1) End Function Function ThreeFormulas(Formula1 As Variant, Optional Formula2 As Variant, Optional Formula3 As Variant) As Variant Dim formulaSnip As String, val2 As String, val3 As String Application.Volatile With Application.Caller formulaSnip = Mid(.Formula, 16) With .Validation .Delete .Add Type:=xlValidateCustom, AlertStyle:=xlValidAlertStop _ , Formula1:="=True", Operator:=xlBetween .ShowInput = True If Not IsMissing(Formula3) Then .InputTitle = CStr(Evaluate("CHOOSE(2," & formulaSnip)) .InputMessage = CStr(Evaluate("CHOOSE(3," & formulaSnip)) ElseIf Not IsMissing(Formula2) Then .InputMessage = CStr(Evaluate("CHOOSE(2," & formulaSnip)) End If End With End With ThreeFormulas = Evaluate("CHOOSE(1," & formulaSnip) End Function
_
...How to Cross-post politely...
..Wrap code by selecting the code and clicking the # or read this. Thank you.
mikerickson, that solution smells sweet. I will have to rewrite a fair bit of my sheet to test it but it looks like an elegant solution - I thank you for taking the time to provide it.
On initial playing I have a couple of questions. Where it displays bob,dave,bob,dave as a header and the amounts below as x+x+x+x is it possible to display as
bob - x
dave - x
bob - x
dave - x
The next question is that I will be looping through 4 sheets that represent each 1/4 of the year looking in the first instance for rent payments so I would like to display it as such
period ¦ rent
jan-mar 3,000
apr-jun 3,000
jul-sep 3,000
oct-dec 3,000
so that if for example you click in the first 3,000 it will show in the popup
11/1/09 - 1,000
12/2/09 - 1,000
10/3/09 - 1,000
Last edited by installer69; 11-27-2010 at 04:34 PM.
Actually, you shouldn't re-write either of those UDFs!
They are generic UDFs that aren't specific to any workbook.
You may have to alter the arguments passed to them (the formula entered into cells), but re-writing the code is not indicated.
North and south were just the values that I used in the example DB.
The criteria argument of ConcatIf can be as varied as any criteria argument of SUMIF.
The thing that it can't do is return a concatenation of sub-strings from each row.
Like "Bob:10, Bob:15, Dave:8, Bob:56, Dave:12"
(If one introduced a helper column it could concatenate the cells of that column based on A:A meeting the criteria)
One could alter ConcatIf for a special use, but I'd like to alter it in a general fashion to deal with the equivalent of
=ConcatIf(A:A, E2, B:B&":"&C:C, ", ") to get that result.
Last edited by mikerickson; 11-27-2010 at 04:35 PM.
_
...How to Cross-post politely...
..Wrap code by selecting the code and clicking the # or read this. Thank you.
ohhh, you show me how little I know! I will inspect the details and see if I can get it to do what I need. I understand what you mean, it just depends on if I have the ability to write the required arguments.
I will persevere down the new road you have sent me down. I thank you.
I have followed your advice and put a helper column in which CONCANETATEs column B and C then called it in the total cell's formula to display bob:10 etc in the pop up. All I need to do is action a vbCrLf line feed after each one and bob's your uncle. (or is it dave?)
Last edited by installer69; 11-27-2010 at 05:22 PM.
OK, in the result cell (F2 in your example) I used the formula:
in which D:D represents the CONCATINATED B:B and C:C and then used CHAR(10) to add a line feed after each result.=ThreeFormulas(SUMIF(A:A,E2,C:C),Concatif(A:A,E2,D:D,CHAR(10)))
Nearly there; thanks for your excellent pointer and example.
Glad to have helped.
Thanks for the insight on how ConcatIf can be improved. (Altered to accept an array as the third argument, not just a range.)
Thanks.
_
...How to Cross-post politely...
..Wrap code by selecting the code and clicking the # or read this. Thank you.
No, no - thank you. I had to Google UDF! Before this I had no idea what they were or what they could do.
Perseverance is enlightenment so I spent a few more hours on it last night and it does almost exactly what I want. This is the formula I used in the helper column to format them as date and currency in case it helps anyone who stumbles on this thread:
=CONCATENATE(TEXT(A3,"dd/mm/yy")," : ",TEXT(D3,"£##.00"))
Last thing at the moment is how to change the size of the pop up box? I've Googled for ages but can't see where the dimensions are declared.
Sorry to answer my own question but sometimes it helps others who are looking for a solution. I decided that you can't dictate the size of a data validation input message so I found a way to create a text box and display the data validation messages in that instead thus allowing me to format to my heart's content.
Her we go. Create a textbox on your sheet (in the insert>text tab in 2007) and size it and position it as desired. Right click in the border of the text box and select size and properties, properties tab, select 'don't move or size with cells' and remove the check mark from 'print object' Name the text box txtInputMsg.
Now right click on your sheet's tab and select 'view code' and past the following in:
Now you may wish to select the cells that have the data validation message and under the tab Data>Data tools>Data Validation>Data Validation>Input Message clear the tick from the show input message when cell is selected so that you don't get 2 messages.Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim strTitle As String Dim strMsg As String Dim lDVType As Long Dim sTemp As Shape Dim ws As Worksheet Application.EnableEvents = False Set ws = ActiveSheet Set sTemp = ws.Shapes("txtInputMsg") On Error Resume Next lDVType = 0 lDVType = Target.Validation.Type On Error GoTo errHandler If lDVType = 0 Then sTemp.TextFrame.Characters.Text = "" sTemp.Visible = msoFalse Else If Target.Validation.InputTitle <> "" Or _ Target.Validation.InputMessage <> "" Then strTitle = Target.Validation.InputTitle & Chr(10) strMsg = Target.Validation.InputMessage With sTemp.TextFrame .Characters.Text = strTitle & strMsg .Characters.Font.Bold = False .Characters(1, Len(strTitle)).Font.Bold = True End With sTemp.Visible = msoTrue Else sTemp.TextFrame.Characters.Text = "" sTemp.Visible = msoFalse End If End If errHandler: Application.EnableEvents = True Exit Sub End Sub
When one of your cells that contains a data message is now selected the text box should appear with your message in it an go when you select any other cells.
My thanks to contextures.com for this end of solution.
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks