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.
Related
My program automates a process on a website using selenium, HTTP requests, and an HTML parser known as HtmlAgilityPack.
There are parts of this process that require user input that can not be automatically filled out by hard-coded values. Allowing the user control over the website to fill in these inputs would be a mistake. It would be difficult to know when they are done inputting it and they would likely touch something they shouldn't which would cause errors when control of the website is handed back over to the program.
Instead, I download the page source of the website using selenium and parse the inputs out of the HTML with HtmlAgilityPack. Then controls matching those inputs are dynamically generated and added to a FlowLayoutPanel. The FlowLayoutPanel works correctly the first time. It displays all added controls. When the user is done they click on a button and the process continues.
Sometimes the process encounters errors that cause it to loop back to where the user needs to enter input again. The exact code that worked before is then run in the same subroutine again. It all runs without error and as expected. I have manually checked at runtime that the controls are added to the FlowLayoutPanel, that they have a size bigger than 0, 0, and that they are a visible color. Yet only one control ever shows up in the FlowLayoutPanel no matter how many should be visible.
The FlowLayoutPanel has it's AutoScroll property set to true and it has adequate space to add more controls. As I said this works fine the first time around just not the second. I've been stuck on this for a while and would appreciate some help. The code is going to be posted below for you to look at.
The code I am going to post has several FlowLayoutPanels. One that is on the Form permanently that was created in the designer and the others are generated dynamically to group up the controls.
The issue is with the permanent one. It shows one control as I said. This control is the first dynamically generated flow layout panel and all of it's children. It doesn't display any of the other FlowLayoutPanel controls that are added to it. Just whichever one is added first.
For those of you wondering about the line FLPOutOfStock.ImprovedClear() in the code bellow, The .Improved clear is an extension method I wrote. When Clearing the FlowLayoutPanel with the normal method, .Clear(), the controls get removed but they don't get disposed of. My method disposes of all the controls in the panel and then calls the Clear() method on the panel.
I realize that the Clear() method isn't necessary after they've been disposed of it's just a backup since nothing ever seems to work. I wanted to make sure the panel was fully reset.
Here is the code:
Private Sub UIOutOfStockState_Load()
Try
cmdOutOfStockDeleteAll.Enabled = False
cmdOutOfStockContinue.Enabled = False
FLPOutOfStock.ImprovedClear() 'The flowlayoutpanel that does not display all controls
With New WebDriverWait(ChromeDriver, TimeSpan.FromSeconds(20)).Until(Function(driver) CBool((CType(driver, IJavaScriptExecutor)).ExecuteScript("return jQuery.active == 0")))
End With
LBLOutOfStockErrors.Text = "Notice. The following items are out of stock. Please see below for product-specific availability dates. If you have any questions, please contact Customer Service at (800) 843-2020. All direct to patient orders will ship complete. The entire order will ship when out-of-stock product becomes available."
Dim OutOfStockDoc As New HtmlAgilityPack.HtmlDocument
OutOfStockDoc.LoadHtml(ChromeDriver.PageSource)
Dim OutOfStockProductNodes As HtmlAgilityPack.HtmlNodeCollection = OutOfStockDoc.DocumentNode.SelectNodes("//div[contains(#id,'id_detail_item_')]")
FLPOutOfStock.SuspendLayout()
If OutOfStockProductNodes IsNot Nothing Then
For Each OutOfStockProductNode As HtmlAgilityPack.HtmlNode In OutOfStockProductNodes
Dim FLPOutOfStockProduct As New FlowLayoutPanel
FLPOutOfStockProduct.SuspendLayout()
FLPOutOfStockProduct.FlowDirection = FlowDirection.LeftToRight
FLPOutOfStockProduct.AutoSize = False
FLPOutOfStockProduct.Size = New Size(420, 160)
FLPOutOfStock.Controls.Add(FLPOutOfStockProduct)
Dim WBProductText As New WebBrowser
WBProductText.Size = New Size(400, 120)
WBProductText.AllowNavigation = False
WBProductText.AllowWebBrowserDrop = False
WBProductText.IsWebBrowserContextMenuEnabled = False
WBProductText.ScriptErrorsSuppressed = True
WBProductText.ScrollBarsEnabled = True
WBProductText.Margin = New Padding(0, 0, 0, 0)
WBProductText.Padding = New Padding(0, 0, 0, 0)
WBProductText.Navigate("about:blank")
Dim ProductTextHtmlDoc As New HtmlAgilityPack.HtmlDocument
ProductTextHtmlDoc.LoadHtml(OutOfStockProductNode.OuterHtml)
ProductTextHtmlDoc.DocumentNode.SelectSingleNode("/descendant::a[#id='patient-outOfStock-delete-item']").Remove()
WBProductText.DocumentText = ProductTextHtmlDoc.DocumentNode.OuterHtml
FLPOutOfStockProduct.Controls.Add(WBProductText)
FLPOutOfStockProduct.SetFlowBreak(WBProductText, True)
Dim SpaceReducer0 As New Panel
SpaceReducer0.Size = New Size(0, 0)
FLPOutOfStockProduct.Controls.Add(SpaceReducer0)
Dim cmdProductDeleteButton As New Button
cmdProductDeleteButton.Text = "Delete"
cmdProductDeleteButton.BackColor = Color.White
cmdProductDeleteButton.ForeColor = Color.Black
cmdProductDeleteButton.Margin = New Padding(0, 0, 0, 0)
cmdProductDeleteButton.Padding = New Padding(0, 0, 0, 0)
cmdProductDeleteButton.Size = New Size(400, 20)
Dim OnClickAttributeValueWithQuoteEscaping As String = OutOfStockProductNode.SelectSingleNode("/descendant::a[#id='patient-outOfStock-delete-item']").GetAttributeValue("onclick", "")
cmdProductDeleteButton.Tag = "//a[#id='patient-outOfStock-delete-item' and (#onclick=""" & OnClickAttributeValueWithQuoteEscaping & """)]"
AddHandler cmdProductDeleteButton.Click, AddressOf cmdProductDeleteButton_Click
FLPOutOfStockProduct.Controls.Add(cmdProductDeleteButton)
FLPOutOfStockProduct.SetFlowBreak(cmdProductDeleteButton, True)
Dim SpaceReducer1 As New Panel
SpaceReducer1.Size = New Size(0, 0)
FLPOutOfStockProduct.Controls.Add(SpaceReducer1)
FLPOutOfStockProduct.ResumeLayout()
Next
FLPOutOfStock.ResumeLayout()
FLPOutOfStock.Refresh()
End If
cmdOutOfStockDeleteAll.Enabled = True
cmdOutOfStockContinue.Enabled = True
Catch Ex As Exception
cmdOutOfStockDeleteAll.Enabled = True
cmdOutOfStockContinue.Enabled = True
End Try
End Sub
So I’ve been asked to show what is in the improved clear method. Here it is:
<Extension()>
Public Function ImprovedClear(ByRef Control as Control)
For Each controlchild as control in control.controls
Control.Dispose()
Next
Control.Controls.Clear()
Return Nothing
End Function
I'm presuming the answers very simple, but here's the issue, my option menu has two parts, normal settings and advanced settings, the advanced settings are accessed by pressing the advance button, fairly basic. This will make the form bigger unveiling the further settings, but the only problem I have is making a form bigger on different resolution does not make it bigger by the same factor - to put what i saying into context, if i increase the size of my form on a standard 1080p monitor, then run my program on a laptop resolution of 1366 by 768, the form will be much smaller than what i got it to draw on the 1080p resolution screen.
Heres the code I'm using to increase the form size:
Private Sub Advance_btn_Click(sender As Object, e As EventArgs) Handles Advance_btn.Click
If advanced = 0 Then
Me.Size = New Drawing.Size(262, 198) ' will increase form size
Advance_btn.Text = "<" ' text for button changes to know i have pressed the button - (to '<')
advanced = 1
ElseIf advanced = 1 Then
Me.Size = New Size(262, 85) ' will decrease form size
Advance_btn.Text = ">" ' text for button changes to know i have pressed the button - (to '>')
advanced = 0
End If
End Sub
The form is also without borders, if that makes somewhat a difference, thanks.
Me.Height = Screen.PrimaryScreen.Bounds.Height
Me.Width = Screen.PrimaryScreen.Bounds.Width * 0.6
In this example I use for years, the width is set to 60% of the screen width. This is in an IF statement, where I check if the user has a 4K monitor, if not, a full-screen mode is set at application start-up. You can, perpahs, do a similar thing with your drawing area.
I want to draw TextBoxes/Labels on my form in code and connect them with lines - based on data that I have stored in a datatable ("treedata"). If I use the following code everything works fine:
For i = 0 To treedata.Rows.Count - 1
Dim tb As New TextBox
hor = treedata.Rows(i)(11)
vern = ver + 120 * treedata.Rows(i)(4)
tb.Text = "sometext"
tb.Location = New Point(hor, vern)
Form8.Controls.Add(tb)
posofmodif = treedata.Rows(i)(10)
vero = treedata.Rows(i)(6)
Dim myPen As New System.Drawing.Pen(System.Drawing.Color.Green)
Dim formGraphics As System.Drawing.Graphics
myPen.SetLineCap(LineCap.RoundAnchor, LineCap.ArrowAnchor, DashCap.Flat)
formGraphics = Form8.CreateGraphics()
formGraphics.DrawLine(myPen, Convert.ToSingle(posofmodif), Convert.ToSingle(vero), Convert.ToSingle(hor), Convert.ToSingle(vern))
myPen.Dispose()
formGraphics.Dispose()
Next
However I would like to use labels instead of TextBoxes because it makes no sense to use heavier TextBoxes in this case. But when I simply replace
Dim tb As New TextBox
by
Dim tb As New Label
the labels do appear on the Form as expected but the lines connecting them appear only for a moment and then turn invisible.
I first thought that the problem might be caused by labels being over or below the lines but even when I make sure that no line is crossing any label it happens.
Does anyone have an idea what I could do to avoid this?
This is your problem: Form8.CreateGraphics(). That method is volatile, as it creates a Graphics instance that does not survive the scope in which it's used.
You need to be using the Paint event for whatever control on which you intend to draw. The form, the label...whatever that is. The Paint event provides a Graphics object for you to use, and it gets called whenever the drawing needs to be refreshed.
Because the event fires frequently, you need to be mindful of what you do there. Heavy lifting in a Paint handler can slow an app down considerably.
I have to create dynamic table layout panel with some controls with auto sized rows and and fixed columns size.
My problem is that i want to show whole checkbox text .
Any help
My code is
Dim textBox2 As New CheckBox()
textBox2.Text = "You forgot to add the ColumnStyles. Do this on a sample form first with the designer. Click the Show All Files icon in the Solution Explorer window. Open the node next to the form and double-click the Designer.vb file. "
textBox2.AutoSize = True
textBox2.Dock = DockStyle.Top
'' textBox2.Size = New Point(200, 90)
Dim lbl1 As New Label()
lbl1.Location = New Point(10, 10)
lbl1.Text = "Yoer.vb"
lbl1.AutoSize = True
lbl1.Location = New Point(120, 50)
lbl1.Dock = DockStyle.Top
'' dynamicTableLayoutPanel.Padding = New Padding(2, 17, 4, 5)
dynamicTableLayoutPanel.Controls.Add(lbl1, 0, 0)
dynamicTableLayoutPanel.Controls.Add(textBox2, 1, 0)
Me.dynamicTableLayoutPanel.SetColumnSpan(textBox2, 5)
If you mean you want the table to size to the controls within it, then:
dynamicTableLayoutPanel.AutoSize = True
I know this is old, but I stumbled across it and figured I'd throw my 2 cents in in case someone else comes along.
Note: I'm using Visual Studio 2015 with .NET 4.6. Functionality may differ between versions.
The problem is that the really long text is not word-wrapping to fit within the table or form. Instead, it is set to Dock = DockStyle.Top. This will cause it to make a single line that continues on and gets clipped, similar to a single-line textbox.
If you want it to automatically word wrap, you'll need to use Dock = DockStyle.Fill. Now, this doesn't completely resolve the problem if your row or table isn't large enough to display the text. Since all of the rows are set to AutoSize, it will only do the bare minimum to fit the control vertically. It doesn't care if text gets clipped off. The end result, using your example code against a 6-column, 10-row table, is this:
Since there isn't a word wrap property, you'll need to manually fit it. Now, to do this, you'll need to change the row to be Absolute instead of AutoSize. To figure out how big to make it, you can pretty much rely on PreferredSize. This reveals a much wider Width than the existing regular Width. From that, we can determine how many lines it would take if we wrap it.
This is what my code ended up looking like:
Dim h As Single = 0
Dim chk As New CheckBox()
chk.Text = "You forgot to add the ColumnStyles. Do this on a sample form first with the designer. Click the Show All Files icon in the Solution Explorer window. Open the node next to the form and double-click the Designer.vb file. "
chk.AutoSize = True
chk.Dock = DockStyle.Fill
Dim lbl1 As New Label()
lbl1.Text = "Yoer.vb"
lbl1.AutoSize = True
lbl1.Dock = DockStyle.Top
dynamicTableLayoutPanel.Controls.Add(lbl1, 0, 0)
dynamicTableLayoutPanel.Controls.Add(chk, 1, 0)
dynamicTableLayoutPanel.SetColumnSpan(chk, 5)
' Find the preferred width, divide by actual, and round up.
' This will be how many lines it should take.
h = Math.Ceiling(chk.PreferredSize.Width / chk.Width)
' Multiply the number of lines by the current height.
h = (h * chk.PreferredSize.Height)
' Absolute size the parent row to match this new height.
dynamicTableLayoutPanel.RowStyles.Item(0) = New RowStyle(SizeType.Absolute, h)
The changes included delaring a height variable, renaming the CheckBox variable, setting its Dock to Fill, removing the Location from lbl1, and adding in size calculation. The output:
This isn't perfect since the height includes the checkbox itself, and the checkbox takes up padding, so there can be too much or too little height calculated. There are other calculations that may need to be considered. But, this is a starting point.
Goal
Resize the palette set on plugin start up so that it looks like so:
Everything is correctly adjusted here and looks nice. This setup is saved when I close AutoCAD manually. Although, my goal would be to make the program make it start up like this.
Current Start-Up Result
Here's the result I get on start up:
As you can see, it's not wide enough. The width needs to get larger but the height is correctly set.
Current Attempt
Here is what I have when starting up my application:
<CommandMethod("Hunter-P")> _
Public Sub HunterP()
'Tool Palette
If ps Is Nothing Then
ps = New Autodesk.AutoCAD.Windows.PaletteSet("Hunter Palette Set", "", New Guid("{ECBFEC73-9FE4-4aa2-8E4B-3068E94A2BFA}"))
ps.Style = Autodesk.AutoCAD.Windows.PaletteSetStyles.ShowPropertiesMenu Or Autodesk.AutoCAD.Windows.PaletteSetStyles.ShowAutoHideButton Or _
Autodesk.AutoCAD.Windows.PaletteSetStyles.ShowCloseButton
projPalette = New tpProjectToolPalette(Me)
convPalette = New tpConveyorToolPalette(Me)
ps.Add("Projects", projPalette)
ps.Add("Conveyors", convPalette)
End If
ps.Visible = True
ps.Dock = Autodesk.AutoCAD.Windows.DockSides.Left ' Docks the palette set to the left
ps.Size = New System.Drawing.Size(350, 800) 'Sets the width and height
convPalette.Visible = False
'Activate Project ToolPalette
ps.Activate(0)
If m_DocData Is Nothing Then
m_DocData = New MyDocData
End If
AddHandler AcApp.DocumentManager.DocumentActivated, AddressOf Me.DocumentManager_DocumentActivated
AddHandler AcApp.DocumentManager.DocumentToBeDeactivated, AddressOf Me.DocumentManager_DocumentToBeDeactivated
End Sub
This result seems to not actually dock the palette set. There is unwanted space at the bottom.
How do we resize a palette set correctly?
I solved this issue by switching around my Dock and Size lines.
It becomes possible to resize it like so (while keeping it docked left)
ps.Size = New System.Drawing.Size(335, 600) 'Resizing
ps.Dock = Autodesk.AutoCAD.Windows.DockSides.Left
This solved my issue.