Why does Drawstring make font wider in Listbox? - vb.net

I have a Listbox in vb.net. I was able to draw my items (with the DrawItem event) with different colors depending on conditions. It works.
The problem is that the size of the string drawned is wider that the original string as you can see in the following pictures. I use a monospace font, and now the text is not aligned anymore with the textbox above...
Listbox without DrawString
Listbox with Drawstring
My code :
Private Sub ListBoxEvIns_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ListBox_EvIns.DrawItem
ListBox_DrawItem(sender, e, Me.DT_EvIns)
End Sub
Public Sub ListBox_DrawItem(sender As Object, e As DrawItemEventArgs, DT As DataTable)
If DT.Rows.Count() = 0 Then Exit Sub
'Dim F As Font = New Font(e.Font.FontFamily, e.Font.Size * 0.965) 'workaround test
Dim F As Font = New Font(e.Font.FontFamily, e.Font.Size)
e.DrawBackground()
If Dic_ParticipEv_Statut_Brush.Keys.Contains(DT(e.Index).Item("Statut")) Then
e.Graphics.FillRectangle(Dic_ParticipEv_Statut_Brush(DT(e.Index).Item("Statut")), e.Bounds)
Else
e.Graphics.FillRectangle(Brushes.Gray, e.Bounds)
End If
e.Graphics.DrawString(sender.Items(e.Index).ToString(), F, Brushes.White, e.Bounds)
e.DrawFocusRectangle()
End Sub
Can someone explain me what I'm missing ?

First of all, stop creating a font every time you’re painting an item, this wastes precious GDI resources. Use e.Font directly, without creating another copy.
Ensure you’re using exactly the same font for label and for listbox.
Set UseCompatibleTextRendering= True for your label.
I think that’s sufficient.
However, why you’re doing that in the first place?
If you’re displaying some multi-column tables, why not use a ListView instead?

Related

How to fit a long text file field into a listbox?

I'm creating a program that is reading the information for recipes from a text file. I have the text file structured in a way that the list of instructions for each recipe are combined into one field of the text file. However, when I try to display the instructions into a listbox, it displays as one long line, and you can't see the full set of instructions. Is there anyway to manipulate that single field of the text file to continue onto the next line of the listbox when the text fills the space? I've included a picture of my code for displaying other elements of the text file line.
code
Here is what the sample field says in my text file:
In a pan sauté the shallots in olive oil. Add the brussel sprouts. Sauté until vibrant green. Add chopped bacon and balsamic vinegar if desired.
Once again, I just need help on how to get that giant field to fit to my text box. Here is a picture of my form during runtime currently. Thank you!
vb form
You've to control the drawing yourself.
From DrawMode docs, OwnerDrawVariable:
All the elements in the control are drawn manually and can differ in size.
Now, I'm setting the ItemHeight in MeasureItem event, and drawing the text in DrawItem event.
Protected Overrides Sub OnLoad(e As EventArgs)
yourListBox.DrawMode = DrawMode.OwnerDrawVariable
MyBase.OnLoad(e)
End Sub
Private Sub yourListBox_MeasureItem(sender As Object, e As MeasureItemEventArgs) Handles yourListBox.MeasureItem
e.ItemHeight = e.Graphics.MeasureString(yourListBox.Items(e.Index).ToString(), yourListBox.Font, yourListBox.Width).Height + 10
End Sub
Private Sub yourListBox_DrawItem(sender As Object, e As DrawItemEventArgs) Handles yourListBox.DrawItem
If e.Index < 0 Then Return
e.DrawBackground()
e.Graphics.DrawString(yourListBox.Items(e.Index).ToString(), yourListBox.Font, Brushes.Black, e.Bounds)
End Sub

Can I use variables to control which PictureBox I am using?

Is there a way that I can use a variable to control which PictureBox I am using in Visual Basic?
I.e.:
CurrentNumber = 1
PictureBox(CurrentNumber).backcolour = backcolour
You can use the Me.Controls(String) indexer. It lets you specify the name (as a string) of the control you want to access, thus you can dynamically access a picture box by concatenating the string "PictureBox" with a number.
Dim TargetPictureBox As PictureBox = TryCast(Me.Controls("PictureBox" & CurrentNumber), PictureBox)
'Verifying that the control exists and that it was indeed a PictureBox.
If TargetPictureBox IsNot Nothing Then
TargetPictureBox.BackColor = Color.Red
End If
Alternatively, to save processing power by avoiding looping through the entire control collection every time you can call the OfType() extension on Me.Controls, storing the result in an array sorted by the controls' names. That way it'd only have to iterate the control collection once.
'Class level - outside any methods (subs or functions).
Dim PictureBoxes As PictureBox() = Nothing
'Doesn't necessarily have to be done in a button, it's just an example.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If PictureBoxes Is Nothing Then
PictureBoxes = Me.Controls.OfType(Of PictureBox).OrderBy(Function(p As PictureBox) p.Name).ToArray()
End If
'NOTE: CurrentNumber - 1 is necessary when using an array!
PictureBoxes(CurrentNumber - 1).BackColor = Color.Red
End Sub
NOTE: This solution will only work properly if all your picture boxes are named "PictureBox1", "PictureBox2", etc. If you suddenly skip a number ("PictureBox3", "PictureBox5", "PictureBox6") then PictureBoxes(CurrentNumber - 1) for CurrentNumber = 5 would return PictureBox6 rather than PictureBox5.
What you really should do is create a PictureBox() and use that to reference your picture boxes via an index.
The best way to build your array is to create a method that builds the array from the references created by the designer. This lets you continue to use the designer to create your controls and it makes your code check for deleted controls at design-time. Using Me.Controls(...) suffers from run-time errors if controls you are looking for have been deleted.
Here's the code you need:
Private _PictureBoxes As PictureBox() = Nothing
Sub AssignPictureBoxesArray
_PictureBoxes = {PictureBox1, PictureBox2, PictureBox3}
End Sub
Then you access them like this:
Sub SomeMethod
Dim CurrentNumber = 1
Dim PictureBox = _PictureBoxes(CurrentNumber - 1)
PictureBox.BackColor = System.Drawing.Color.Red
End Sub

Dynamically changing image in picture box not working

I've been trying to figure this out off-and-on for weeks.
In my VB 2010 Forms application, I have a number of picture boxes which are populated with images from other picture boxes using the drag-and-drop method. That's no problem, it works fine. The picture boxes are all in a groupbox container.
The problem is trying to swap images between two picture boxes on a drag-and drop operation. In other words pBox1 has image.x and pBox2 has image.y. Drag the image from pBox2 to pBox1, and drop it; pBox1 will then have image.y from pBox2 and pBox2 will have image.x from pBox1.
With this example, here's the code I have so far:
Private Sub pBox2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pBox1.MouseDown
strImageSource = "pBox2" 'strImageSource is a global string variable
[other stuff]
end sub
^ This saves the name of the source picture box to a global string.
Private Sub pBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles pBox1.DragDrop
For Each Control as PictureBox in GroupBox1.Controls.OfType(of PictureBox)()
if Control.Name = strImageSource then
Control.Image = pBox1.Image
end if
next
dim imgTarget as Image = CType((e.Data.GetData(DataFormats.Bitmap)), Bitmap)
pBox1.image = imgTarget
End Sub
^ This searches for the picture box as named by the strImageSource ("pBox2") and copies the contents of pBox1 into it, and then drops the image that was in pBox2 into pBox1.
I hope this makes sense.
This correctly places the image from pBox2 into pBox1, but it does not switch the image from pBox1 into pBox2. pBox2 is just blank. However, debugging shows that the image in pBox2 is not nothing; it does contain a bitmap. It's just not visible.
Now, just as a test, I added a line to the For Each section that would change the background color of the picture box:
For Each Control as PictureBox in GroupBox1.Controls.OfType(of PictureBox)()
if Control.Name = strImageSource then
Control.Image = pBox1.Image
Control.BackColor = color.red
end if
next
And the back color does change. This tells me that the For Each section is working -- it's finding the control and changing the back color. It's just not showing the image.
Is there something I am overlooking?
Thanks!
Instead of using strImageSource, use a global variable defined as a Picturebox
Private tmpPictureBox As PictureBox
Then store that picturebox reference so you can set its image on DragDrop
Private Sub pBox2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pBox1.MouseDown
'store the picturebox reference
tmpPictureBox = sender
[other stuff]
end sub
Private Sub pBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles pBox1.DragDrop
'set the first image to the second
tmpPictureBox.Image = sender.image
'set the second image to the first
pBox1.image = CType((e.Data.GetData(DataFormats.Bitmap)), Bitmap)
End Sub
You should call Control.Refresh() on the PictureBox controls in order to update the images.
OK, this was stupid.
I was doing everything right with one really boneheaded exception. In another part of the code, inexplicably, I was clearing the picture boxes of content after the image was replaced. It may have been a remnant of something I was trying to do unrelated to this problem, and I just never corrected it.
My apologies for that, and thanks for all the responses.

Dynamically resize font to fill up RichTextbox

How do i dynamically resize the text in a RichTextbox so that it fills up the entire rich textbox?
any help is much appreciated. thank you.
This MSDN article almost answers your question. http://msdn.microsoft.com/en-us/library/bb986765.aspx.
You may download the attached sample there.
I think you might have to get creative with the 'Font' constructor. For example, on a click event then construct a new Font using some relationship with your application (or textbox size) with the desired font size.
Protected Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btn.Click
Dim yourfontsize As Integer
'machinery to create desired font size
If blah then
yourfontsize = X()
Else
yourfontsize = Y()
End If
yourtextbox.SelectionFont = New Font("Tahoma", yourfontsize, FontStyle.Bold)
End Sub
Where X() and Y() are functions to return your target font sizes based on whatever else may
be going on within your application.
Reference: http://msdn.microsoft.com/en-us/library/yh8963yx.aspx
Hope that helps!
-sf

VB.net findwindow/findwindowex

Hey all, i am trying to figure out how to go about finding this window's label when the control name is the same as all the other labels on the program.
WindowsForms10.STATIC.app.0.378734a
WindowsForms10.STATIC.app.0.378734a
WindowsForms10.STATIC.app.0.378734a
All 3 labels are named the same. The one i am most interested in is a progress % counter (1%, 2%, 3%, etc..)
How can i get the value (using a timer of course) from that label without knowing the caption of it at any given time??
Any help would be great! :o)
David
The obvious answer would be to get the text from all three labels and check which one looks like "1%", "55%" etc.
If strText Like "#%" Or strText Like "##%" Or strText = "100%" Then
' ...
The less obvious answer (if the Windows API is too cumbersome for your requirements) would be to use the Microsoft UI Automation API.
Not sure if you're just looking for a more complete code sample, but here you go.
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
'This block of code creates a list of all the labels on my form.
'Replace this with code to get a list of labels on the form you are scraping
Dim LblList As New List(Of Label)
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is Label Then
LblList.Add(CType(ctrl, Label))
End If
Next
'End
Dim ProgressLblTxt As String = String.Empty
For Each lbl As Label In LblList
If lbl.Text.Contains("%") Then 'You could use several different criteria here as mentioned in the previous answer
ProgressLblTxt = lbl.Text
End If
If ProgressLblTxt <> String.Empty Then Exit For
Next
'Do something with ProgressLblTxt
MsgBox(ProgressLblTxt)
End Sub