Silverlight - Screen blank after attaching Grids to a Canvas for printing - vb.net

So, in order to print my Silverlight grids I had to Children.Remove them from LayoutRoot and Children.Add them to my Canvas I am using for printing. (Since they can only be attached to one parent element at a time).
Which is fine, but then this leaves my screen blank, as the Grids have been removed from LayoutRoot.
So I tried Children.Remove from the Canvas and Children.Add back to LayoutRoot, but it doesn't add anything back onto the screen.
How should I deal with this? Thanks.
(Using Silverlght 5 and VB.net).
My code:
If PageCounter = 1 Then
Dim PrintSurface As New Canvas
Dim topPosition1 As Double = e.PageMargins.Top + 10
Dim topPosition2 As Double = e.PageMargins.Top + 600
CompChartGrid.SetValue(Canvas.TopProperty, topPosition1)
AttChartGrid.SetValue(Canvas.TopProperty, topPosition2)
LayoutRoot.Children.Remove(CompChartGrid)
PrintSurface.Children.Add(CompChartGrid)
LayoutRoot.Children.Remove(AttChartGrid)
PrintSurface.Children.Add(AttChartGrid)
e.PageVisual = PrintSurface
PrintSurface.Children.Remove(CompChartGrid)
PrintSurface.Children.Remove(AttChartGrid)
LayoutRoot.Children.Add(CompChartGrid)
LayoutRoot.Children.Add(AttChartGrid)
PageCounter += 1
e.HasMorePages = True
Exit Sub
End If

This is most likely due to clipping.
When you set the Canvas.Top property on your elements, this value will persist until replaced. So when you add CompChartGrid and AttChartGrid back to the LayoutRoot grid, they are offset by topPosition1 and topPosition2 respectively.
Try calling
CompChartGrid.ClearValue(Canvas.TopProperty)
and
AttChartGrid.ClearValue(Canvas.TopProperty)
The elements should then end up back where they started.

Related

Location properties on winforms acting strange

I have a form where I allow the user to generate multiple panels with some various contents in the panels by pressing an "add" button. Depending on what the user does in the panel, the panel grows and shrinks to fit the contents. Because of this change is size, I have created a sub that formats the panels on the form.
Private Sub formatPanels(frm As Form)
Dim count As Integer = 0
Dim startPoint As Point = New Point(12, 80)
Dim endPoint As Point = New Point(0, 0)
Dim maxY As Integer = 0
For Each pnl As Control In frm.Controls
If TypeOf pnl Is Panel Then
ReDim Preserve _arr_Panels(count)
_arr_Panels(count) = pnl
count += 1
pnl.Location = startPoint
startPoint.Y += pnl.Size.Height + 30
End If
Next
End Sub
So as you can see, we loop through every panel and the first always begins at the location (12,80) and then increments with the size of the panel and some spacing.
HERE IS THE ISSUE. This ONLY happens when i am SCROLLED DOWN the form. The panels spacing all of a sudden screws up and decides to put the first panel hundreds of pixels down the form. Is the location property based on what you're looking at? So if I were scrolled down the form location(0,0) would be the top left of the current view? There must be some weird property to location that I am not aware of.
Thanks
This behavior is not related to a panel, but to any control on a form with AutoScroll = True and Anchor including Top. (Note: if Anchor didn't also include Left I had some strange positioning on the first call of the function.
The solution is described here which is to use AutoScrollPosition. If you change your startPoint to this it will adjust for the scroll position.
Dim startPoint As Point = New Point(12, Me.AutoScrollPosition.Y + 80)
And the documentation for AutoScrollPosition states this:
When adding controls programmatically to a form, use the AutoScrollPosition property to position the control either inside or outside of the current viewable scroll area.

Capture Active Window with Keyboard SendKeys

After experiencing problems with Visual Basic's printform quality, I have decided to take a different approach. I am going to simulate an ALT-PRINTSCREEN keyboard key-press to capture the form in its current state.
However, before this happens, I need to remove certain elements from the form (such as the border), and after getting the image, I need to replace these elements. I am having some timing/synchronization problems with this, as I am noticing that certain elements are still on the image that has been copied to the clipboard.
I figured this was due to the fact that it takes time to process the changes, and time for Windows to process the simulated "send keys", so I set up sleep timers. I am having mixed results. I notice that if I don't replace the elements afterwords, the clipboard image appears correctly. However, if I do replace them, even if I set up a 20 second sleep timer, they do not appear.
Therefore, I know there is some sort of synchronization problem, but I am not sure how to fix it. Here is the code I am using:
'Make these forms invisble so they are not printed
Logo.Visible = False
MenuStrip1.Visible = False
ReportsButton.Visible = False
pnlmain.BorderStyle = 0 'remove the border
'Sleep
System.Threading.Thread.Sleep(1000)
'simulate an ALT and PRINTSCREEN key click to get ONLY the report
SendKeys.Send("%{PRTSC}")
'Sleep
System.Threading.Thread.Sleep(1000)
'Now, get the image from the clipboard
Dim formImage As System.Drawing.Image
formImage = My.Computer.Clipboard.GetImage()
Logo.Visible = True
MenuStrip1.Visible = True
ReportsButton.Visible = True
pnlmain.BorderStyle = 1
Changing the sleep durations does not seem to change much (unless they are very low).
Any suggestions?
EDIT: here is the code for draw to bitmap (after modifying the code above):
formImage = New Bitmap(Me.Width, Me.Height, Me.CreateGraphics())
Me.DrawToBitmap(formImage, New Rectangle(0, 0, Me.Width, Me.Height))
When it is sent to the print action:
Dim g As Graphics = e.Graphics
'transform the form image so it fits the height and width of the paper, with a 5 px margin
Dim res = printSettings.PrinterResolutions
Dim newSizeDestinationPoints As Point() = {
New Point(5, 5),
New Point(841, 5),
New Point(5, 956)
} 'These values are based on A4 sized paper
'g.DrawImage(formImage, 5, 5)
g.DrawImage(formImage, newSizeDestinationPoints)
The low quality is not coming from the stretch.

Accessing multiple PictureBoxes (or any form control) in Visual Basic?

So I'm making a game in VB for learning purposes and now I'm struggling with this problem:
I'm trying to do a For loop that draws the level map. However, I just can't seem to figure out it. This is an example of what I'm trying to do:
For index as integer = 1 to 192
PictureBox(index).Image = bg(map(x,y)) 'this is causing me problems
x=x+1
if x=16 then
x=0
y=y+1
End If
Next
But since PictureBox(index).Image doesn't seem to be the correct answer, it simply throws me an error.
Is there any way to do this?
EDIT:
Shortly, I need to set PictureBox.Image's from 1 to 192 like this without having 192 lines of code:
PictureBox1.Image = bg(map(0,0))
PictureBox2.Image = bg(map(1,0))
PictureBox3.Image = bg(map(2,0))
PictureBox4.Image = bg(map(3,0))
'etc....
Instead I wan't to set them in a For loop. I don't want to have extra lines of code.
EDIT2:
The PictureBoxes are added in the editor.
In the designer set the property Tag of each PictureBox to the string value composed by the x and y required to pass to the map function
For example:
PictureBox1 should have the Tag property set to "0,0"
PictureBox2 set the Tag property to "1,0",
....
PictureBox17 will have the Tag property set to "0,1"
and so on until you have mapped all your pictureboxes with the correct values.
Then your code could be changed to
' Assuming the PictureBox are all childs of the Form
For Each pic in Me.Controls.OfType(Of PictureBox)()
Dim tag = pic.Tag
' You could omit this check if you have only the pictureboxes
' set with a valid Tag property
if Not string.IsNullOrEmpty(tag) Then
Dim xyCoords = tag.ToString().Split(","c)
pic.Image = bg(map(Convert.ToInt32(xyCoords(0),
Convert.ToInt32(xyCoords(1))))
End if
Next
I'm going to make a huge assumption here and assume that you already have the PictureBox's on the form prior to reaching this code and they have Id's PictureBox1 through PictureBox192. The code would look like the following. You need to:
1. Retrieve the element by its ID
2. Cast/convert it from an object to a PictureBox
3. Set it's Image property appropriately.
Dim pBox As PictureBox ' To store each pic box in
For index as integer = 1 to 192
pBox = Me.Controls.Find(PictureBox&&index) ' Try to find pic box by ID
If Not pBox Is Nothing Then ' If able to find element by this ID
DirectCast(pBox, PictureBox).Image = bg(map(x,y))
End If
x=x+1
if x=16 then
x=0
y=y+1
End If
Next

How to get Winforms Panel to correctly layout many items?

I've got a WinForms Panel control which holds a large number of child controls. Each child is left docked, causing the horizontal width of the contents to grow. The containing Panel has its AutoScroll property set so that you can get to all the contents.
I'm running into a problem when the total width of the contents gets too large. Once you've hit this maximum width, additional content elements are placed on top of existing contents instead of being placed to the right. But, if I resize the Panel after it has done its initial layout, it corrects itself by expanding its logical width and placing each content element in the correct location. How do I get it to layout correctly before the user resizes the window?
Here's a simple example:
Form1.vb
Public Class Form1
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
For i As Integer = 1 To 200
Dim gb As New GroupBox
gb.Text = "Box " & i.ToString
gb.Width = 250
gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
gb.BringToFront()
Next
End Sub
End Class
Form1.Designer.vb
Partial Class Form1
Inherits System.Windows.Forms.Form
Private Sub InitializeComponent()
Me.Panel1 = New System.Windows.Forms.Panel()
Me.SuspendLayout()
'
'Panel1
'
Me.Panel1.AutoScroll = True
Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill
Me.Panel1.Location = New System.Drawing.Point(0, 0)
Me.Panel1.Name = "Panel1"
Me.Panel1.Size = New System.Drawing.Size(284, 262)
Me.Panel1.TabIndex = 0
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(284, 262)
Me.Controls.Add(Me.Panel1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
Friend WithEvents Panel1 As System.Windows.Forms.Panel
End Class
This is what the window looks like when it first comes up, scrolled nearly to the end so you can see the problem area. Notice that Box 183 to 199 are missing because they are placed on top of each other. This is not right.
This is what the window looks like after you manually resize it, scrolled nearly to the end. The panel fixed itself in response to the resize; the total logical width of the panel was automatically extended enough to hold all the contents. This is what I want it to look like when it first comes up.
I've tried manually setting the location of each box, and I've tried calling PerformLayout() and several other functions. Nothing seems to work. So far I haven't found the magic combination to get the good layout. Does anyone know how to fix this?
Edit:
Here's a screenshot that might make the issue more obvious. I adjusted the box widths and the number of boxes to show the problem better. See how the last box overlaps box 656? Every box from 657 to 700 has the same incorrect location. Turning off docking and setting the location myself doesn't help.
Looks like a bug with the scrolling information. If you call PerformLayout when the Panel is scrolled all the way to the right, it correctly places the controls in the proper place. That requires some code in the OnShown method:
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
Panel1.AutoScroll = True
Panel1.SuspendLayout()
For i As Integer = 1 To 200
Dim gb As New GroupBox
gb.Text = "Box " & i.ToString
gb.Width = 250
gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
gb.BringToFront()
Next
Panel1.ResumeLayout(False)
End Sub
Protected Overrides Sub OnShown(e As EventArgs)
MyBase.OnShown(e)
Panel1.AutoScrollPosition = New Point(Panel1.HorizontalScroll.Maximum - _
Panel1.HorizontalScroll.LargeChange, 0)
Panel1.PerformLayout()
Panel1.AutoScrollPosition = Point.Empty
End Sub
Of course, having over 200 container controls on the form is never recommended.
AutoScroll is not AutoPositionMyChildren. From MSDN:
When adding controls programmatically to a form, use the AutoScrollPosition property to position the control either inside or outside of the current viewable scroll area.
If you looped thru the controls, to print their location, you's see at some point (probably around 130) that Location.Y becomes fixed at 32767 probably some default unscrolled max. This is also the point they start stacking because they in fact have the same initial location. Some of the code you have makes up for that but it isnt quite right. Once you scroll it, the panel fixes the coords on the child controls.
First, I would suggest that you set Panel1.AutoScrollMinSize to something like {480, 0} so that the HScroll bar appears at design time; this allows you to calc a good height for the boxes which wont cause a VScroll as you add controls.
Dim gb As GroupBox
' only 150 because problem is when (i * width) > 32k
For i As Integer = 0 To 150
gb = New GroupBox
gb.Name = i.ToString ' added
gb.Text = "Box " & i.ToString
gb.Width = 250
' no docking so set the height
gb.Height = Panel1.Bounds.Height - 30 ' trying to avoid the VSCroll
' set location explicitly
gb.Location = NewCtlLocation(Panel1.Controls.Count,
Panel1.AutoScrollPosition.X)
' Dock and Anchor mess up the AutoScroll
'gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
' allow panel to update its scroll positions
Panel1.ScrollControlIntoView(gb)
' not needed; seems to offset something with Dock
' changing ZOrder may not always be desirable
'gb.BringToFront()
' debug illumination
Console.WriteLine("{0} {1} {2}", i.ToString,
Panel1.AutoScrollPosition.X.ToString,
gb.Location.X.ToString)
Next
'Go back to start
Panel1.ScrollControlIntoView(Panel1.Controls("0"))
Location helper so you can tweak gutters or margins (dock replacement):
Friend Function NewCtlLocation(ByVal n As Integer,
ByVal ScrollPosX As Integer) As Point
Const TopMargin As Integer = 5
Const LeftMargin As Integer = 5
Return New Point((n * 250) + ScrollPosX, 0)
End Function
Notes:
I have a vertical scroller which repeatedly adds up to 120 user controls which works well but it does not need/use ScrollControlIntoView and they never stack up like yours do. I suspect maybe because they are smaller. There is also at least a second or two before the next one can be added, which may matter. But, good to know.
It might be possible to use the ControlAdded event of the panel to do something, but it would likely amount to ScrollControlIntoView. Doing it once at the end only doesnt work, so using it as they are added is allowing something to get updated as you go.
With the right fiddling, you might be able to get Dock to work, but I suspect it may be part of the problem such as Height and Left set this way dont update the panel's internal scroll map.
Your boxes actually look narrower than 250 - is autosize on?
Suspend/Resume Layout hurt rather than help - they prevent the control from doing anything about the virtual area being populated. It should happen fast enough that no one will see anything. Results:
Works on My SystemTM

Why don't my Labels show in VB.NET Form

After reading this question, I wrote some code to create a label for each attribute of an xml element.
The problem is that when I run the project, my form only displays the first label. I've checked in the immediate window as well as debug window and all of the labels are loaded to the form, but none of them are displayed. Help?
Here's the code that runs when the form loads.
Dim doc As New XmlDocument()
doc.Load("xmlfile")
Dim ability As XmlNode = doc.GetElementsByTagName("ability").Item(0)
Dim numberofLabels = ability.Attributes.Count
ReDim labels(numberofLabels)
For counter As Integer = 0 To numberofLabels - 1
labels(counter) = New Label
labels(counter).Visible = True
labels(counter).Text = ability.Attributes.Item(counter).Name
labels(counter).Location = New System.Drawing.Point(10, 30 + counter * 10)
Me.Controls.Add(labels(counter))
Next
You should be using some layout manager, to help you with control positioning. Doing it manually is not worth the pain. Try using TableLayoutPanel or FlowLayoutPanel. Both can be docked or anchored to a parent control, so everything behaves very smoothly. Otherwise you are looking to write a lot of positioning/resizing code, and then maintaining it later.
Change the value of 10 in the original code line for a new point to a bigger value such as 40, so that new labels could appear separated visually:
labels(counter).Location = New System.Drawing.Point(10 + counter, 30 + counter * 40)