Trying to Print Embedded Container Controls (... and not too successfully at that)

I have a series of container controls embedded in a canvas in the main window (canvas is same size as the window) acting as the user interface of my application. Each container control is comprised of a static table structure (created with rectangles), static labels, and text fields that display dynamic data pulled from a MS SQL database. Here’s what the embedded containers look like when displayed on the user interface screen (e.g., ELECTRICAL DATA, LIGHTING DATA) with the numerical data from the database displayed in the containers’ text fields:

I want the data containers after they are populated with the data to be part of a printed hard copy. Since I’m not sure how (or if it’s even possible) to draw the populated embedded containers directly into a graphics object to be printed, I figured one way to do what I needed would be simply print that portion of the canvas that contains each particular container by doing this (code simplified for sake of example):

Dim containerWidth As Integer = 700
Dim containerHeight As Integer = 180

Dim p As New Picture(containerWidth, containerHeight, 32)
wndMainWindow.cnvDataDisplayScreen.DrawInto(p.Graphics, 0, 0)
g.DrawPicture(p,20,200)

Although it does properly draw the container structure and the static labels just fine this way, it DOES NOT display any of the data that was loaded in the text fields from the database although it is clearly showing on the screen.

This leaves me with 2 questions:

  1. Why is the text field data not printing but the rest of it is?
  2. Is there a better way of doing this that allows for putting the populated container controls in the graphics object that I’m using to print the hard copy?

Thanks in advance for your valuable time!
… Don

BTW … I also tried using a global property (‘pELECcont’ with data type set to Picture) and capturing the container control at the point it is embedded and populated by using the DrawInto method with the embedded container control (cntElectrical_ED1).

  Dim ec As New cntElectrical_ED1
  ec.EmbedWithin(wndMainWindow.cnvDataDisplayScreen,20,vCoord_wndMainWindow)   // place container
 
  oContainers.Append ec   // place container in array for future control
  oContainerIndex.Append("ec")  // add ability to ID each container in the array oContainers later

   // ... code that populates the text fields in the container control

  Dim p As New Picture(ec.Width, ec.Height, 32)
  ec.DrawInto(p.Graphics, 0, 0)
  pELECcont = p

Then, when I wanted to print the hard copy, I tried using the following, but once again, I got everything printed correctly except NO TEXT FIELD DATA printed!

g.DrawPicture(pELECcont,20,200)

What s the purpose of the canvas?

You populated the data flds and drew the rects into your container control at some point. Use that code to draw the same thing into a printer graphics object. Be sure to do that within the container control since the containing window knows nothing of the container’s data.

Thanks for responding, Roger. The canvas was a very early decision in the project knowing that I had to scroll the user interface (screen) and that I’d be drawing things later from it (another few reasons there also, but those were the primary considerations) … things easier done with a canvas than a window.

I think I was going on a wrong assumption … that being, that if the container control was embedded in the canvas, then printing the canvas to a picture would do just that … print the canvas + container control + everything in the container control that was visible. I have since done exactly as you suggested and gone back to where the container’s text fields are populated and used the DrawInto for all the text fields at that point as well as the container control itself to a global picture object property. I can then access it any time later and print using DrawPicture. Tried that a moment ago and it does work (text is there from text fields). Still not quite sure why the original attempt (printing the part of the canvas itself with the embedded container) doesn’t work though.

A canvas doesn’t “contain” anything. It’s a blank slate for output. It doesn’t remember what you draw, so it can’t redraw itself. You have to draw whatever was originally on the canvas.

Thanks, Tim … I think I’ve finally got my head around that fundamental now. I’m surprised my misunderstanding of that hasn’t bitten me any sooner with as much as I use the canvas control. The “doesn’t remember what you draw part” is where I went wrong. Now, all I’ve got to do is remember that it works like my wife’s thought process and … ^^

I would avoid the drawinto and instead just g.drawstring or g.drawpicture. This will give you a lot more control of your printed output.

OK … Now I’m confused again … The following is the only way I know (at the moment) of getting both the container control and text fields in the Graphics object so I can print them. How would I do it just using DrawString and/or DrawPicture?

  Dim p As New Picture(ec.Width, ec.Height)
  
  ec.DrawInto(p.Graphics, 0, 0)  // add the container
  
  for i As Integer = 0 to ec.txfNum  // add the text fields
    ec.txf(i).DrawInto(p.Graphics, ec.txf(i).Left, ec.txf(i).Top)
  next
  
  pELECtop = ec.Top   // set the top point (vertical coordinate on the screen) of the container control
  pELECcont = p  // set the global property (Picture data type) with the completed picture
  

Also, could you please elaborate on that for me, Roger? What “more control” do I get of the printed output?

As I understand it, drawinto was conceived as a quick and dirty way of printing what is on the screen to a printer. You have indicated that you used the canvas to scroll your containers. What do you do if one of your charts splits a page break? What if sometime you want to add a header to your printout?
Use x and y coordinates to print each item where you want it.

Const kLineDrop = 16. // a value that works for me g.drawstring myHeading, x, y Y = y + kLinedrop g.drawpicture someGraphic x, y
Etc, etc

See the printing examples in the Examples folder

[quote=135175:@Roger Clary]What do you do if one of your charts splits a page break? What if sometime you want to add a header to your printout?
Use x and y coordinates to print each item where you want it.[/quote]
Thanks again, Roger, but that part’s not the problem … the scrolling screen is the User Interface for data entry/viewing, not the printout. The printout is built separately (inside a menu handler) and directed to the Graphics object. I have a header and footer on each page and am keeping track of vertical position of everything in between those two to determine when pagination is required.
My confusion is I see how to print text strings (g.DrawString) and pictures (g.DrawPicture), but not controls (container controls, text fields, etc.) without using DrawInto. How does your code (and only DrawString and DrawPicture) allow that without using DrawInto … still can’t see that.

I’ll take your suggestion and re-read the Printing Examples and see what jumps out at me.

And the quality will be a lot better too. For whatever wizardry is involved in the printer object, fonts print at the maximum resolution of the printer, while drawinto will print at 72 dpi only.

I don’t discount any of that, Michel … but it doesn’t answer the question “how do I get my container controls full of text fields populated with data into a Graphics object so I can print it” without using DrawInto???

Drawinto is the simplest solution.

If you want/need a higher quality, you take the coordinates of your container controls and elements within as basis to drawstring and drawrect onto the printer graphics object. For each of your container controls, you use top and width for the drawrect, then the position of the bale within the container control as additional coordinates for the drawstring.

In short, printing without drawstring requires recreating the layout onto the printer graphics object.

If drawinto outputs a sufficient quality, you do not need to go the extra effort, though.

Thank you, Michel for the explanation. Every little bit helps clear this foggy brain of mine right now. To make sure I understand what you said, did you really mean “printing without DrawInto” instead of what you wrote (“printing without drawstring”)?

Call me stupid, but … “the bale”???

How did you populate those text fields in the first place? The answer is: The same way.

Roger, I understand what you’re trying to tell me … here’s where I’m lost …please tell me how the following code used to populate one (‘ec’ is the embedded container control) such container control’s text fields (txf control set are the text fields) in the first place “translates” to something I can get into a Graphics object the “same way”? It’s the “how do you do that?” that’s stumping me. I’ve got to be missing something really simple here. But I just don’t get “how” do I take this code and “do it the same way” to get what I need in the Graphics object.

  Dim db As SQLDatabaseMBS = App.mDB
  Dim rsElec As RecordSet = db.SQLSelect("SELECT * FROM electricalData INNER JOIN caseModel ON ((electricalData.CaseID = caseModel.CaseID) AND (caseModel.CaseName='" + _
  caseName + "') AND (caseModel.Revision='" + caseRevision + "'))")   // electrical
  
  Dim ec As New cntElectrical_ED1
  ec.EmbedWithin(wndMainWindow.cnvDataDisplayScreen,20,vCoord_wndMainWindow)   // place container
  wndMainWindow.ResetSize
  
  vCoord_wndMainWindow = vCoord_wndMainWindow + ec.Height + 10   // initialize next container location vertical position
  
  oContainers.Append ec   // place container in array for future control
  oContainerIndex.Append("ec")  // add ability to ID each container in the array oContainers later
  
  If rsElec <> Nil Then
    Dim i As Integer = 1
    Dim idx As Integer = 4   // template row count
    While Not rsElec.EOF
      
      if NOT rsElec.Field("Length").Value.IsNull THEN ec.txf((i-1)+(idx*0)).Text = str(rsElec.Field("Length").StringValue) ELSE ec.txf((i-1)+(idx*0)).Text = ""
      if NOT rsElec.Field("FansPerCase").Value.IsNull THEN ec.txf((i-1)+(idx*1)).Text = str(rsElec.Field("FansPerCase").IntegerValue) ELSE ec.txf((i-1)+(idx*1)).Text = ""
      if NOT rsElec.Field("FansHiEff120Amps").Value.IsNull THEN ec.txf((i-1)+(idx*2)).Text = str(rsElec.Field("FansHiEff120Amps").DoubleValue) ELSE ec.txf((i-1)+(idx*2)).Text = ""
      if NOT rsElec.Field("FansHiEff120Watts").Value.IsNull THEN ec.txf((i-1)+(idx*3)).Text = str(rsElec.Field("FansHiEff120Watts").DoubleValue) ELSE ec.txf((i-1)+(idx*3)).Text = ""
      if NOT rsElec.Field("ACHeater120Amps").Value.IsNull THEN ec.txf((i-1)+(idx*4)).Text = str(rsElec.Field("ACHeater120Amps").DoubleValue) ELSE ec.txf((i-1)+(idx*4)).Text = ""
      if NOT rsElec.Field("ACHeater120Watts").Value.IsNull THEN ec.txf((i-1)+(idx*5)).Text = str(rsElec.Field("ACHeater120Watts").DoubleValue) ELSE ec.txf((i-1)+(idx*5)).Text = ""
      if NOT rsElec.Field("DefHeater1P208Amps").Value.IsNull THEN ec.txf((i-1)+(idx*6)).Text = str(rsElec.Field("DefHeater1P208Amps").DoubleValue) ELSE ec.txf((i-1)+(idx*6)).Text = ""
      if NOT rsElec.Field("DefHeater1P208Watts").Value.IsNull THEN ec.txf((i-1)+(idx*7)).Text = str(rsElec.Field("DefHeater1P208Watts").DoubleValue) ELSE ec.txf((i-1)+(idx*7)).Text = ""
      if NOT rsElec.Field("DefHeater1P240Amps").Value.IsNull THEN ec.txf((i-1)+(idx*8)).Text = str(rsElec.Field("DefHeater1P240Amps").DoubleValue) ELSE ec.txf((i-1)+(idx*8)).Text = ""
      if NOT rsElec.Field("DefHeater1P240Watts").Value.IsNull THEN ec.txf((i-1)+(idx*9)).Text = str(rsElec.Field("DefHeater1P240Watts").DoubleValue) ELSE ec.txf((i-1)+(idx*9)).Text = ""
      
      rsElec.MoveNext
      i = i +1
    Wend
    
  End If
  
Const kLineDrop = 16. // a value that works for me
for i As integer = 0 to ?
g.drawstring ec.txf((i-1)+(idx*0)).Text, x, y
Y = y + kLinedrop
g.drawpicture ec.txf((i-1)+(idx*0)).Text, x, y
//===etc.===
next

Set the values of x and y to whatever works best. You can increment x, use a 2nd column by setting x + ??, etc. if you need to.
You already have the values from your DB stored in your textfields. Just pull out those values to populate your printing graphics object.

Roger, my sincerest appreciation for your patience in helping me through this. NOW, I GET IT! I have to take the “text” value out of “each” text field, use that text field’s position properties for “positioning” in the DrawString method, and use g.DrawString to “build” the container “one text field at a time” (kinda like E.F. Hutton, right?) ^^

Likewise, I guess, if I truly want to get rid of using DrawInto completely, then I’d have to take Michel’s advice as well and “redraw” all the shapes (rectangles) I used to create the “table-like” structure containing the data … would that be correct?

Thanks again!

Yes. However, if you have your drawing routine set up well, it’s a matter of “re-using” the routine, not “re-drawing” the graphics.
I know that drawInto seems like the cheap out at this point, but I really think that if you take the time to write a robust and flexible printing routine now, it will pay big dividends for you as you continue to develop your app.
Good luck.

[quote=135188:@Don Lyttle]Thank you, Michel for the explanation. Every little bit helps clear this foggy brain of mine right now. To make sure I understand what you said, did you really mean “printing without DrawInto” instead of what you wrote (“printing without drawstring”)?

Call me stupid, but … “the bale”???[/quote]

Sorry. I wrote too fast. I meant indeed print without drawinto. And the pesky autocorrect replaced control by bale.