Im working on a speadsheet with multiple large tables whose info is entered via spreadsheet based userforms i have created, as i am not familiar or comfortable using vba userforms, and i am trying to call entries back up in order to edit them.
The easiest way i can think of to do this is to open the default excel dataform. What i need to know is; is there any way to open this dataform for a specific table with criteria already entered?
eg. I need to edit a product table, so i already know that im editing the product table and i know the name for the product. I have a little userform, again spreadsheet based, where i select the entry type(in this case a product) and then enter the product name. What i want to do is use that info to open a dataform for the product table with the criteria field of the name already filled out so as to reduce time spent searching for the entry as these tables have hundreds of entries.
For this simple example I take your Worksheet with the products is called "Products". There are two columns with Names in column A and IDs in column B
On the Userform I place two textboxes called
TextBox_Productname
TextBox_ProductID
I then added the following code to the Userform that will trigger whenever the value of the productname textbox changes and it looses focus.
Private Sub TextBox_Productname_Change()
' Clear the content of all Textboxes but the Productname
TextBox_ProductID.Value = ""
' Analyse the Cells in the Worksheet called "Products"
With Worksheets("Products")
' Assume in the first row are the headers so start
' looking at the second row and go to the final one
For i = 2 To .UsedRange.Rows.Count
' check whether the value in the first column
' matches the value of the Textbox_Productname
If .Cells(i, 1) = TextBox_Productname.Value Then
' If there is a match copy the values of all
' the other columns to the textboxes
TextBox_ProductID.Value = .Cells(i, 2)
End If
Next i
End With
End Sub
Related
First - I am NOT a programmer, so please be patient and know I may not use the correct terminology. :)
I am 95% done with a project using VBA in Excel Developer (first time ever!) to create a userform to populate a spreadsheet with organizational risks. At the bottom of the userform, I have a list that displays the risks already entered into the spreadsheet for reference, but I want to exclude "closed" risks from list view on the userform (but I want to retain them in the source spreadsheet).
Column J (column 10) contains the status, so I need something like Column J <> "Closed" (wildcards because there are different types of "closed") in the if/then statement, but I don't have the right syntax or maybe I need to define Column J so the filter knows where to look?? A friend suggested I may need to filter in a completely separate sub - but not sure how to accomplish that. Excerpt below.
'define number of columns (fields) in the database to display at the bottom of screen and identify column headers
.lstRiskDatabase.ColumnCount = 11
.lstRiskDatabase.ColumnHeads = True
'Assign column widths - note summary column is intentionally 0
.lstRiskDatabase.ColumnWidths = "35,55,75,175,150,160,0,55,55,100,100"
'assign control source for rows on the database table
If iRow > 1 Then
.lstRiskDatabase.RowSource = "RiskDatabase!A2:K" & iRow
Else
.lstRiskDatabase.RowSource = "RiskDatabase!A2:K2"
End If
I have an excel sheet that functions as an "input sheet". Users will go in and type things like their name and phone number etc into specific cells. As shown below.
,
From there I have code that will store the data from the cell into a new table row.
Set NewTableRow = DatabaseTable.ListRows.Add
NewTableRow.Range(1, 1).Value = InputSheet.Range("D5").Value
NewTableRow.Range(1, 2).Value = InputSheet.Range("I5").Value
Unfortunately if I insert a row or move a cell on the input sheet I am forced to change potentially hundreds of lines of code every time, which can be very time consuming as there are over 135 inputs on this sheet. Is there a better way I could be doing this?
looking through the forum and can't find what I need. I have 100+ sheets with unique sheet names and data in column B. Column B will contain various END DATES. some sheets will have 1 or 2 end dates, others have upwards of 30 end dates. . I would like to create a summary page containing a table that will update itself to show all sheet names that have END dates in column B expiring within the next 30 days. is this something that requires coding? or use of the excel indirect formula with maybe a vlookup wrapped around it?
Private Sub ChkEndDates()
Dim ws As Worksheet
Dim cell As Range
Dim rowCount as Long
For Each ws in Worksheets
If ws.Name <> '"summary page" Then
rowCount = WorksheetFunction.CountA(ws.Range("B:B"))
For i = 1 To rowCount
If (ws.Cells(i, 2).Value - Date < 30) And (ws.Cells(i, 2).Value > Date) Then
MsgBox ws.Name
'And maybe push into the summary page
End If
Next i
End If
Next ws
End Sub
Neither VLOOKUP nor INDIRECT are required for this problem.
Assuming your dates in the B columns are in Excel's serial number format, this formula will calculate the amount of cells in the B column of Sheet2 that are within 30 days from today's date:
= SUMPRODUCT((Sheet2!B:B>=TODAY())+0,(Sheet2!B:B<=(TODAY()+30))+0)
You can just duplicate this formula for however many sheets you need, e.g.
= SUMPRODUCT((Sheet2!B:B...))+SUMPRODUCT((Sheet3!B:B...))
EDIT
Per #Jeeped's comment, it is recommended to narrow down this range (B:B) just to the end of your data. (e.g. if you have at most 30 rows of data, change to B1:B30)
For completeness, here's how you would do this using PowerQuery/Get & Transform if you had Excel 2013 or later.
First, you would turn each of the data areas in your workbook into Excel Tables (aka ListObjects), by selecting them and either using the Insert>Table command from the ribbon, or simply by using the keyboard shortcut [Ctrl] + [T]. Ideally you would give them all a name that denotes what they are. (I've used the prefix "Input_" and then a running count, because later this 'handle' will help me to ignore any Tables that I don't want in my end result, simply by seeing if it's name is prefixed with "Input_").
Then from the Data tab, select New Query>From Other Sources>Blank Query:
A mysterious looking UI called the PowerQuery window will open with nothing much of interest in it. If you type =Excel.CurrentWorkbook into the formula bar and press Enter, then a list of the various Tables in your workbook will populate in that window, as shown below. And if you click that twin arrow icon to the right of the Content column, a menu will appear that lets you expand those tables to include the actual columns in each of them, and bring them into the PowerQuery window:
And here's how that data dump looks, when you push OK:
This next bit is only required if you have other Tables in your workbook already that you don't want to show in your end mashup. In the screenshot below, I'm filtering the result set to only include tables with the Input_ prefix:
And then I'm going to change the data type of that DateTime column to a simple Date, by selecting the column concerned, and then choosing Data Type>Date from the Transform tab.
Then I'm going to select Close and Load To from the menu at top right:
..and then select the "Only Create Connection" and "Add to Data Model" options from the resulting dialog:
Now back in Excel, I create a PivotTable, and leave the "Use this workbook's Data Model" option checked:
...which brings up a slightly different looking PivotTable Fields List than you usually see:
I now add the Name (Table Name) and End Date fields to my Pivot, and set a date filter to just show dates within the next 30 days:
And here's the end result, a PivotTable that shows just those tables with end dates within the next 30 days:
The beauty of this approach is that if you later add more tabs with more input tables, then provided you prefix them with "Input_" then they will automatically appear next time you refresh the PivotTable. And furthermore, instead of having a report that merely tells you which tabs have applicable End Dates, the PivotTable also tells you which individual records in those tabs are involved.
There's more stuff I would do along the way that I haven't shown, such as give the columns friendlier names by ditching the "Content." suffix, and I'd probably write a simple macro to automatically refilter the PivotTable based on 30 days from the current date. But this at least shows you the benefits of upgrading to Excel 2013 and using PowerPivot/Get and Transform to radically automate tasks such as this.
Starting with screenshot:
http://i.imgur.com/Isj9MER.png
(I'm a new user, can't post images)
Working for a call center. We have a program that tracks our time spent in various phone states (so when we're on calls, out to lunch, etc) that can export data for a given team and date range as a CSV.
I'm working on automating this report. The way it works is that the team lead will pull the CSV, copy-paste into another tab, and then I've got a bunch of array formula If functions and Indirect references to pull all the data as shown. The data analysis and everything is working great.
My problem is the graph. Right now, I've got column B with an If function that either outputs the agent's email (which is how the system tracks it) or "" if all emails have been used. The rest of the columns have If(B2="","", [relevant formula]). That way, we can have all the team leads with various (and fluctuating) team sizes use the same report with a simple copy-paste.
My problem is the stupid bar chart. It pulls data from rows 2-32 (A2:A32). Our current largest team is 28, and I left room for new hires showing up soon. My problem happens when I use data from one of our smaller teams. As you can see, even though the blank rows are filled with "" in every cell, it's still displaying those rows. In the chart. This means that with the smallest team (shown), the chart is half wasted whitespace.
Is there a way to make the column chart only show rows that have actual data in them?
One thing I tried was putting an Indirect reference for Series Values. So I had a cell (AA1) with {=MAX(IF(B2:B31="","",ROW(B2:B31)))}. That outputs the row number of the last non-blank row. Then for the Series Values I put =Indirect("Report!A2:A"&AA1), but Excel gave me an error saying the function was not valid. I guess you can only have an actual range (and not a formula) in the data input for a chart.
Excel 2016, by the way.
I come up with three possible solutions for this problem.
Convert your data table to a pivot table and use a pivot chart (currently only available in Windows version, sorry if you are using a Mac).
Use a bit of VBA to hide the empty rows.
Use a bit of VBA to modify the data displayed on the chart.
Sample Data Setup
Although you provided a screen shot of your data, it is not simple to convert that into a test case to demonstrate the three solutions. Therefore, I threw together this very simple set up.
Column A contains a list of possible "users".
Cell D1 is a user entry to change the contents of column B and C.
Column B contains the actual "users". It is calculated with =IF(A2<=$D$1,A2,"")
Column C contains the data that goes with the "users". It is calculated with =IF(B2<>"",10,"")
A chart is added to the sheet.
Below is a screenshot of the sample setup, where all potential users are included. (Note: It is Sheet1)
Below is a screenshot of the sample setup, where only potential users A through E are included.
The white space in the second image is the problem we are trying to address.
SOLUTION 1: Make a pivot table of the data
Select all of the pertinent data, in this case B1:C12.
Select Insert -> Pivot Table
In the Create Pivot Table dialog, make sure "New Worksheet" is selected, and click OK.
On the Pivot Table, place "User" field in Rows, and "Total" field in Values. Select the Value Field Settings... for the "Total" field and make sure it uses Sum.
In the Pivot Table, select the Row Labels drop down, Label Filters, Greater Than... . Type "" into the dialog and select OK.
From Pivot Table Tools -> Analyze, select PivotChart. Choose Bar Chart from the dialog.
Below is a screen shot of the Pivot Table.
On the tab with the data, change the last potential user from E to G. On the Pivot Table, refresh the data.
Below is a screen shot of the refreshed pivot table.
SOLUTION 2: Use VBA to hide empty rows
The below code is attached to a button made visible on the worksheet. (n.b. There are other ways to activate the code such as change events, but this will depend on a number of factors outside the scope of this answer).
Sub HideRows()
Dim AllCatRange As Range
Set AllCatRange = Worksheets("Sheet1").Range("B2:B12")
Dim iLoop As Long
For iLoop = 1 To AllCatRange.Rows.Count
If AllCatRange.Cells(iLoop, 1) = "" Then
AllCatRange.Cells(iLoop, 1).EntireRow.Hidden = True
Else
AllCatRange.Cells(iLoop, 1).EntireRow.Hidden = False
End If
Next iLoop
Set AllCatRange = Nothing
End Sub
Below is a screen shot of the data tab with the button added.
After clicking the button, it now looks like this ...
This code will expand rows when they contain data, and collapse rows when they do not.
A potential problem with this approach is that expanding/collapsing rows will change the size of the chart, if the chart lays over those rows.
SOLUTION 3: Use VBA to modify the chart
The below code is attached to a button, and used to modify the chart after the source data is changed.
Sub ModifyChart()
Dim AllCatRange As Range
Set AllCatRange = Worksheets("Sheet1").Range("B2:B12")
Dim lastRow As Long
lastRow = 1
Dim iLoop As Long
For iLoop = 1 To 11
If AllCatRange.Cells(iLoop, 1) <> "" Then
lastRow = lastRow + 1
End If
Next iLoop
Dim PlotCatRange As Range
Set PlotCatRange = Worksheets("Sheet1").Range("B2:B" & lastRow)
Dim PlotSerRange As Range
Set PlotSerRange = Worksheets("Sheet1").Range("C2:C" & lastRow)
Worksheets("Sheet1").ChartObjects(1).Chart.FullSeriesCollection(1).XValues = "=Sheet1!" & PlotCatRange.Address
Worksheets("Sheet1").ChartObjects(1).Chart.FullSeriesCollection(1).Values = "=Sheet1!" & PlotSerRange.Address
Set AllCatRange = Nothing
Set PlotCatRange = Nothing
Set PlotSerRange = Nothing
End Sub
Below is a screenshot of the data tab with the new button in place.
Below is a screenshot of the tab after clicking the button.
Wrap up
My personal preference is to use VBA to modify the chart after the data is modified, as this reflects what you would do manually.
I found a solution! Thanks largely to this page. Here's what I did:
I created made a named range using this formula: =OFFSET(Report!$B$1,1,0,COUNTIF(Report!$B$2:$B$30,"<>-"),1)
For that to work, I had to change all the empty cells to output "-" when empty in stead of "". I couldn't get COUNTIF to accept "<>""" or "<>" or any other weird tricks I tried. Using "-" was easier.
That makes a dynamic named range that changes size as I put data into the sheet. I named a similar range for everything I'm trying to chart (Approved Status, Call Ready, Not Ready). The chart was able to accept those names and now it's dynamically sized. If I only have three agents on the sheet, it shows three huge bars. With twenty, it shows twenty bars (exactly what I was looking for).
One other tip: I changed my first row to output "" when empty, so that COUNTIF(Report!$B$2:$B$30,"<>-") always returns at least 1 (otherwise you get annoying errors because you have a named range referencing a 0-length array).
I'm a Graphic Designer by trade and while I know basic Excel this is getting into foreign territory for me.
So I have been tasked with putting together our company's product price list (with +800+ products that are all broken into categories/tabs) into a version that users can select quantities per item and see a running list on the page of their total. No problem, I have this figured out.
Each item has a column for a part number, description, price, and quantity...with the quantity defaulted to zero. I need to get this so that if user wants an item they can change the quantity to whatever they want, and press the add/update purchase order button and have this values of that item's row add to a separate sheet. Additionally, if they were to change value to zero and press the button again, it would remove the item.
So right now each worksheet in the book is like this:
Ideally it would be great to just have one macro/button that once a user enters whatever quantity of item(s) they want, it would copy the row (but as column-order G B C F H) to a sheet in that book named "Purchase Order". If the user changes a quantity of an item to 0, and reclicks the button, it removes said row from the other worksheet. This would need to work through the 30+ other tabs, and append the rows below already entered values as the user continues to add products to their purchase order (starting on B22 on the Purchase Order worksheet)
My concerns are adding/deleting products in the future/other staff having to update this and not needing to do crazy changes to the macro if, say I need to add 5 more products to a tab.
I will take ANY help, insight, direction, or guidance anyone can shed on this issue. I greatly appreciate it!
Ok I don't know if I understood well your question, but here's what I'm thinking: You could create 2 buttons on the Worksheet, one with a big "+" on it, the other with a "-" and link them to 2 different subroutines. Those items could be positioned at the end or beginning of a product row. Right click on the buttons, go to properties and be sure that those are dependant on cell position and size. The "+" one could check if your "Purchase Order" is currently open (in case it isn't, could create a new one), and add all the content on the same row in the new worksheet (you can use Application.Caller to address the buttons and .Top / .Left to address its row/column). Naturally you'll want to search if the name of the product is already in the "Purchase Order", in that case increase quantity, otherwise copy all the row. Same thing with the "-" button eliminating the product row if quantity is 0. The fun part: you can create 2 buttons and the macros, and then copy the buttons for each row, since they each address rows on their right/left!
EDIT:
Sub Add()
Dim Button_cell_row As Integer
Dim ws As Worksheet, Target As Worksheet
Dim ProductName As String
Dim i As Integer
Set ws = ThisWorkbook.ActiveSheet
Set Target = Workbooks("Purchase_Order.xls").worksheets(1) ' Here you need to choose the file (Workbook) and the Worksheet (Tab) you want
Button_cell_row = ws.Shapes(Application.Caller).TopLeftCell.Row
Do Until IsEmpty(Target.Cells(i,4)) ' for example your product name is in the 4th column
If Target.Cells(i,4) = ProductName Then
Target.Cells(i,7) = cInt(Target.Cells(i,7)) + 1 ' say that the quantity is stored in the 7th cell
Exit Do
End If
i = i+1
Loop
If IsEmpty(Target.Cells(i,4)) Then ' meaning that the whole sheet has been searched but there's no product with a similar name on the whole "i" loop-range
' in this case "i" is now the index of your first empty row
ws.Range(Cells(Button_cell_row,3), Cells(Button_cell_row,8)).Copy _ ' For example you need to copy from 3rd column to 8th column...
Destination:=Target.Cells(i,1) ' ... on the first empty row, from column 1
End If
Set ws = Nothing
Set Target = Nothing
End Sub
This wasn't tested and may not work. But you get the idea :) For the "Remove" Sub, it is gonna be very similar.