Remove objects like table row before rendering - vb.net

I am generating a document with itext7 in vb.net. The document is a payment reminder. The document contains 1 to many positions (I do not know how many positions the document will have at the end). Sometimes it could happen, that a position contains no amount. And I want to remove this position.
The reminder positions looks for example like this:
CustomerName | Amount |
---------------|-------------|
Jupiter Jones | 1,100.12 |
Peter Crenshaw | 0.00 |
Bob Andrews | 231.02 |
---------------|-------------|
Sum | 1,331.14 |
The lines are added to the table with
table.AddCell(cell1)
table.AddCell(cell2)
This example is very simplified...sadly it is because of the data structure and the calculations that have to be made, that I do not know whether the position line has to be printed before starting the line (the first cell of the line) or not.
So is it possible to "remember" a kind of index or handle or anything else, so that I am able to remove certain objects from the itext7 document before I "send" it to the renderer?
The table should look like this after the "removement":
CustomerName | Amount |
---------------|-------------|
Jupiter Jones | 1,100.12 |
Bob Andrews | 231.02 |
---------------|-------------|
Sum | 1,331.14 |
I hope you understand, what I mean...
To clarify some facts and answer the questions blow:
I wrote a wrapper-class (dll) for itext7 to create different types of pdfs with it. A whole table-creation contains of
obj.TableBegin({Cell1Width, Cell2Width})
while objMySqlDataReader.Read
Dim calculatedAmount as Single = 0
//do some calculations
objCell1.Text = objMySqlDataReader("CustomerName")
objCell2.Text = calculatedAmount
obj.TableCreateRow({objCell1, objCell2})
End While
obj.TableEnd()
Method TableBegin
Private Sub TableBegin(ByVal ColWidths as Single())
table = new Table(ColWidths)
//some formattings and so on
End Sub
Method TableCreateRow
Private Sub TableCreateRow(byval arrCells as MyOwnCellType())
For Each varCell As MyOwnCellType In arrCells
Dim c As Cell = New Cell()
Dim p As Paragraph = New Paragraph(varCell.Text)
c.Add(p)
//followed by some formatting lines like c.SetBackgroudColor
//or c.SetBorder
table.AddCell(c)
Next
End Sub
Method TableEnd
Private Sub TableEnd()
document.Add(table)
End Sub
The moment when I know whether a row as to be printed or not is variable. Sometimes I know it just after "printing" both cells of the row. But it could happen that I want to remove a table-row after the table has been "finished".
In another "document-type" a have a very similar problem, but there I need to remove a whole page of the document including Paragraphs, Tables, Images....
My utopian idea was, that I "save" the handle of each "possible to be removed object" in a list or array and after recognizing wheter the objects have to be printed or not I can either clear this array or remove all objects within this array by something like document.remove(handle)
Another utopian idea was, that I "collect" all "possible to be removed / printed objects" in any kind of array or list and after recognizing whether printing or not I can "push" these objects to the document or discard them.
It is difficult to discribe and even much more difficult to understand why I do not know "printing or not" before printing. But believe me - I do not know earlier. ;-)
Any idea or help is very appreciated.

Related

Tabulating a final score for a questionnaire

Thanking in advance for your help with this one. I have searched everywhere but haven't been able to find an answer that addresses my issue.
Here is the background:
I have a questionnaire with 15 questions for which the possible answers are “Y”, “N” or “N/A”. For each question, I have assigned to the “Y” a value and to the “N” and “N/A” a zero.
I need a macro that will, for the active row in question, identify the heading of each question (1-15), look up each heading on a different worksheet in the same file, locate the number assigned to it if the answer is either “Y” or “N” (ignore the “N/A”s) and add all those numbers for the base score.
Then, as a second step, the macro will add only the “Y” answers and tabulate them against the base score for a final score.
For illustration purposes let’s say that I have the following questions:
Table Chair Lamp Plant
Y Y N N/A
In a different tab, the table looks like this:
Table Chair Lamp Plant
5 10 8 15
I need a macro that will look at the column headings to match them, and then at the values assigned to table, chair and lamp (ignore plant since N/A), add them (for a total of 23). Then add only the Ys (for a total of 15) and then produce a final score of 65 (15 out of 23 or 65%).
Really stuck with this one… I have no idea how to even start… any help is truly appreciated.
Thank you so much!!!!
Marta
When you have a big task in front of you, you need to divide it up incrementally. Stack Overflow isn't here to write your macro for you.
The first part you want to do is match the column headers in the Active Row with the column headers on another worksheet, right? What I've posted below should get you the column headers on the question sheet and read them off to you. Try it out and take it further (do some googling if you need to). When you run into a problem with the code you make, that's where a question should be asked.
(Place this code into a fresh module in your workbook, and connect it to a button or something on the questionnaire worksheet)
Public Sub TallyQuestions()
Dim questionSht As Worksheet
Set questionSht = Sheets("Worksheet that has the questions you were talking about") 'replace name with name of questionSht
Dim questionShtColHeaders As Range, cell As Range
Set questionShtColHeaders = questionSht.Range("A1:A15")
For Each cell In questionShtColHeaders
MsgBox cell.Value
Next cell
End Sub

VBA getcrossreferenceitems(wdRefTypeNumberedItem) Paragraph Cut Off?

I'm using excel vba to extract information from a word document.
In the word document, there are levels of numbered lists. For example:
1. ABC
1.1 DEF
1.1.1 ABCDEF
2. AAA
2.1 BBB
2.1.1. CCC
and I need to get the full context of each heading in each level and put them into an excel range, i.e. {"1.ABC", "1.1 DEF", "1.1.1 ABCDEF", "2. AAA", "2.1 BBB", "2.1.1. CCC"}
The function I use is:
For Each sec In objDoc.getcrossreferenceitems(wdRefTypeNumberedItem)
However, my headings are truncated if the headings are too long. For example, I have (random text is added for confidentiality reasons):
"5.2.11. Current References: As part of the evaluation process, XXX will conduct 2340AERTQ3493YR. When selecting ADT34534FDGSR, please ensure that they are AERA34AEFDS."
But only
5.2.11. Current References: As part of the evaluation process, XXX will conduct 234
is displayed, and the rest of the sentence is gone.
If anybody has an alternate solution, please let me know.
i confirm this behavior. A workeable albeit and elaborate solution is to scan the document for all numbered items which gives you the full text and then cross reference that result against the list returned by the GetCrossReferenceItems. There's quite some work involved but works and gives you the ability to create one list with referable Headings and NumberedItems, which is what I did to build a more user friendly alternative to Word's own implementation.
You'll have to match the formatting Word applies to the list returned by GetCrossReferenceItems, ie. the identation and removal of special characters.
Be careful with track changes. There is a bug in GetCrossReferenceItems which means that items (in my case headers) that have a tracked change at the beginning of the text are not returned by GetCrossReferenceItems but internally are still on the list so the index is offset. If the item in question is item 11, then GetCrossReferenceItems gives the item belonging to item 12 the item 11. A workaround is to accept all revisions before GetCrossReferenceItems and undo it after.
It's not easy but works.
I met a similar problem in MSWord. I found some paragraph's text are shorten in the following code
Sub bug()
items = ActiveDocument.GetCrossReferenceItems(wdRefTypeNumberedItem)
For idx = 1 To UBound(items)
MsgBox items(idx)
Next
End Sub
I have to use a some long solution( in Python, sorry. But is is easy to rewrite in VBA):
varHeadings = []
for par in objDoc.Paragraph:
if par.Range.ListFormat.ListType == win32com.client.constants.wdListOutlineNumbering:
idx = par.Range.ListFormat.ListString
txt = par.Range.Text.strip('\n').strip('\r')
varHeadings.append('%s%s' % (idx, par.Range.Text))
which does work. However, as I have said, it is some tedious. So did I miss some VBA function in MSWord, or GetCrossReferenceItems has known bug and can not found any replacement in VBA?

Create new column in Excel based on previous column data

Just a disclaimer: I have limited experience with Excel and sql... I'm basically a noob, so bear with me.
I have a big Excel spreadsheet that is sent to me daily that I would like to manipulate.
I would like to add a couple columns that create values based on their respective rows.
ID Color Brand Indicator
1 Green Vizio TRUE
2 Yellow Samsung FALSE
3 Blue Samsung TRUE
4 Red Sony FALSE
5 Orange Vizio TRUE
In the example above, the Indicator column is the one that I'd like to be created based on the values in the previous columns. The Indicator should be true if Brand has the word Vizio in it, OR if the color is Blue. I mention it has to have the word vizio in it because there are cases where it won't be simply "vizio", but maybe "vizio tv".
I would like to automate this process as much as possible, so do you think it would be best to use an Excel VBA macro or SQL for this?
Any help would be much appreciated, thank you.
You can use the following Excel macro.
Public Sub AddIndicators()
Const INDICATOR_1_COLUMN = 4
Const INDICATOR_1_FORMULA = "=OR(IFERROR(SEARCH(""vizio"",C2),0),(B2=""blue""))"
[a2:index(a:a,counta(a:a))].Offset(, INDICATOR_1_COLUMN - 1) = INDICATOR_1_FORMULA
End Sub
This can easily be expanded for additional indicator columns.
To have this macro available to run whenever you receive your daily worksbooks (and without the need to add the macro to the incoming workbook) simply add this macro to your Personal Workbook.
After that is done, just make sure your daily workbook is open and on the sheet where you want the indicators before executing the macro.
If perchance you do not wish for the formulas to remain in the indicator column, you can use this version of the macro instead:
Public Sub AddIndicators()
Const INDICATOR_1_COLUMN = 4
Const INDICATOR_1_FORMULA = "=OR(IFERROR(SEARCH(""vizio"",C2),0),(B2=""blue""))"
With [a2:index(a:a,counta(a:a))].Offset(, INDICATOR_1_COLUMN - 1)
.Formula = INDICATOR_1_FORMULA
.Value = .Value
End With
End Sub
Your requirement is very simple. You don't have to go for VBscript, macros etc. It can be simply done with excel functions.
=OR(ISNUMBER(SEARCH("blue",B2)),(ISNUMBER(SEARCH("vizio",C2))))
Assumption: Color is in column B and Brand is in column c and data starts from row 2. First row is for headers.

Excel VBA to extract information from two different text files

Greeting, have a little bit of a dilemma. I'm making a VBA that will allow the user to open two separate text files and use them to graph data. So far, I've been successful with getting the two text files open in one spreadsheet. The problem I'm having is using the data from one text file (12 digit numbers) and comparing that to the other text file, which is a log of data. The first text file has a bunch of irrelevant information, but I only need to take the date and 12 digit number recorded. The second text file has a list of 12 digit numbers which I want to check against the first text file and get a count of how many times each specific number came up. Using that, I want to just have a regular count of times it has occurred. I need this to be a VBA because the user that will be opening this macro won't have any programming knowledge so it needs to be very simple.
Also to note, the badge numbers CAN be different based on the text file uploaded, but they are always added in the same located of the spreadsheet.
Thanks.
EDIT: Tried to make a rough sketch of what I'm talking about
| A | B | C | D |
| 1 | DATA WITH NUMBER| ...........|........... |NUMBER TO COMPARE WITH |
| 2 | DATA WITH NUMBER|
I'm not sure I fully grasp what you want, but it sounds like you want to use the Dictionary class in VBA.
You would need to add a reference to the MS Scripting Runtime library, and your code to read the second input file (to count occurrences) could look something like this:
Dim counts As Object
Dim splitValue As Variant
Dim inputLine as String
Set counts = CreateObject("Scripting.Dictionary")
Open "yourfile.txt" For Input As #1
Do While Not EOF(1)
Line Input #1, inputLine
splitValues = Split(inputLine, vbTab)
If counts.Exists(splitValues(0)) Then
counts(splitValues(0)) = counts(splitValues(0)) + 1
Else
counts.Add splitValues(0), 1
End If
Loop
Close #1
Then, when you read the first file, you could essentially call the dictionary to find how many occurrences of the badge number appeared in the second file.
occurrences = counts(badgeNumber)
Again, I may be off. If you can clarify what both inputs look like and what you want the final output to look like, it would help.

VBA Excel 2003/2007: Declaring, Filling, and Passing a Jagged Array

I have thoroughly researched this topic and have yet to find code that works to accomplish what I need to do. In a nutshell, I'm creating a Production Tracking program and the feature of it on which I'm working now involves accurately tracking vacation days for 5 employees. A userform containing 5 listboxes, one for each employee, is used to select which days each employee took off for the week. The problem comes when I try to create unique dynamic arrays containing each employees' days off. I figured out how to create an array that captures this information but it's one array that gets reassigned each time the loop iterates. I need to have a unique array for each employee containing his days off to be used later in the code to adjust weekly scoring depending on his available days of work. Below is my code in the userform for to create a create the jagged array:
Public Name_Jagged() As Variant
For Each Name In Name_Array
Set Unique_Listbox = Controls(Name & "_Vacation")
For UnSelected = 0 To Unique_Listbox.ListCount - 1
If Unique_Listbox.Selected(UnSelected) = False Then
ReDim Preserve Name_Jagged(0 To UBound(Name_Jagged) + 1)
Name_Array(Name) = Name_Jagged()
Name_Jagged(UBound(Name_Jagged)) = Unique_Listbox.List(UnSelected)
End If
For UnSelected_Array_Pos = LBound(Name_Jagged) To UBound(Name_Jagged)
MsgBox Name & "_" & Name_Jagged(UnSelected_Array_Pos)
Next UnSelected_Array_Pos
Next UnSelected
Next Name
The compiler will not allow me to use Public Name_Jagged()() As Variant either despite most other forums saying this is how it's supposed to be written. The only other post I found online regarding this jagged array declaration issue was not answered.
I would really appreciate your help. I've been able to figure out everything so far from previous threads but this has eluded me. If there is a better option than jagged arrays to accomplish this, I'm all ears. I read in some forums about using Lists but I am not at all familiar with them or how to use them at this point. Thanks in advance for the help.
Just a thought but why have 5 static listboxes for each employee?
Why not just have 1 list box which contains the names of the employees and 1 list box which contains the days of the week. You highlight the employee you want and than select the days which they've taken off. Hit a submit button which would load the employees name into an array with the days selected? The array could be structured like this
NAME | MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY | SATURDAY | SUNDAY
JIM..... OFF............ OFF
ERIC.......................OFF.............OFF
And so on. This way if you need to add people in the future you just add them to the list box. You would than have one simple array to deal with?
Also you said "to be used later in the code to adjust weekly scoring depending on his available days of work"
A suggestion; You may want to consider logging it to a simple mysql/mssql database which gives you far more flexibility and control for the future.