How to Draw Highlight Behind Richtextbox - vb.net

Using this code, it draws the highlight on the text, which makes the text somehow blur?
Public Class HighlightableRTB
Inherits RichTextBox
Private LineHeight As Integer = 15
Public Sub New()
HighlightColor = Color.Yellow
End Sub
<Category("Custom"), Description("Specifies the highlight color.")>
Public Property HighlightColor As Color
Protected Overrides Sub OnSelectionChanged(ByVal e As EventArgs)
MyBase.OnSelectionChanged(e)
Me.Invalidate()
End Sub
Private Const WM_PAINT As Integer = 15
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_PAINT Then
Dim selectLength = Me.SelectionLength
Dim selectStart = Me.SelectionStart
Me.Invalidate()
MyBase.WndProc(m)
If selectLength > 0 Then Return
Using g As Graphics = Graphics.FromHwnd(Me.Handle)
Dim b As Brush = New SolidBrush(Color.FromArgb(50, HighlightColor))
Dim line = Me.GetLineFromCharIndex(selectStart)
Dim loc = Me.GetPositionFromCharIndex(Me.GetFirstCharIndexFromLine(line))
g.FillRectangle(b, New Rectangle(loc, New Size(Me.Width, LineHeight)))
End Using
Else
MyBase.WndProc(m)
End If
End Sub
End Class
How can one draw the rectangle highlight behind the text of the richtextbox?

As you already guessed, the text is not really "blurry"... it's the yellow rectangle which is painted over it that makes it looks that way.
This is not going to be easy. In fact, I have a solution, but it's not something I would recommend... if I had anything else to say. But I haven't, so here's one way to deal with this issue:
You can paint the text... over the text. It doesn't remove the text which already exists... but paints over it. If your coordinates are a little bit off, it'll look like crap, but if you get the exact right place it'll work like a charm. An awful, hacky charm:
Using g As Graphics = Graphics.FromHwnd(Me.Handle)
Dim b As Brush = New SolidBrush(Color.FromArgb(50, HighlightColor))
Dim line = Me.GetLineFromCharIndex(Me.SelectionStart)
Dim loc = Me.GetPositionFromCharIndex(Me.GetFirstCharIndexFromLine(line))
g.FillRectangle(b, New Rectangle(loc, New Size(Me.Width, LineHeight)))
TextRenderer.DrawText(g, Me.Text, Font, New Point(-2, 1), ForeColor) ' this is the important line!
End Using
This is 100% a hack, though, and I sure hope that somebody with more sense than I chime in. The position I wrote (-2, 1) should do the trick, but if somehow it doesn't, you can get rid of the transparency to hide your shame by replacing Color.FromArgb(50, HighlightColor) by Color.FromArgb(255, HighlightColor). It won't be that obvious since the text will only reveal this dirty trick when someone selects it (try it!).
Have fun!

Related

How to draw amount of blocks of the progressbar in VB.NET?

Progressbar has a maximumm is 10.
So I need to draw ten block without fill color before running progress.
Edit: added code
Public Class MyProgressBar
Inherits ProgressBar
Public Sub New()
Me.ForeColor = Color.Red
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
For i as integer = 1 to 10
Dim g As Graphics = e.Graphics
Dim widthScale As Integer = Me.Width/10
g.DrawRectangle(Pens,Me.Left+(i*widthScale ),Me.Top, Me.Width / 10, Me.Height)
End For
End Sub
End Class
I try override OnPaint() but it's not working.
There are number of ways to do this. The simplest would be to create a transparent image of the boxes and put it on top of a regular progress bar.
Or you could create an array of text boxes, and play with their position, border and background until they look the way you want.
A more powerful way to do graphics is with the built-in .net graphics object. There's a brief introduction here

How do I fix a custom border when dragging a form off the screen and back on?

I'm using a code to draw a custom dashed border around my textboxes and forms. If I grab the app and drag it off the screen and come back the border is all smeared and bad looking.
The only way I've been able to fix it is by using Me.Reload() event making the form reload which fixes it immediately. Which is alright but I'd rather it be fixed immediately almost to the point that you never even see it happen.
When I tried to add it into a timer it made the form blink really bad obviously.
Is there a way to use this code to detect when the app leaves the boundaries of the screen and just refresh the form only when the whole app returns within the boundaries of the monitor or monitors?
Public Function IsOnScreen(ByVal form As Form) As Boolean
Dim screens() As Screen = Screen.AllScreens
For Each scrn As Screen In screens
Dim formRectangle As Rectangle = New Rectangle(form.Left, form.Top, form.Width, form.Height)
If scrn.WorkingArea.Contains(formRectangle) Then
Return True
End If
Next
Return False
End Function
EDIT: I wanted to share the code I'm using to draw these borders in case it may actually be the issue.
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
' This is the override paint event that will allow us to draw all our borders
addBorders(e)
End Sub
Public Sub addBorders(ByVal e As PaintEventArgs)
For Each ctl As Control In Me.Controls
Select Case True
Case TypeOf (ctl) Is TextBox
ctl.AutoSize = False
ctl.Height = 19
Dim borderRectangle As Rectangle = New Rectangle(ctl.Location, ctl.Size)
borderRectangle.Inflate(1, 1)
ControlPaint.DrawBorder(e.Graphics, borderRectangle, ctl.ForeColor, ButtonBorderStyle.Dashed)
Case TypeOf (ctl) Is ComboBox, TypeOf (ctl) Is Button
Dim borderRectangle As Rectangle = New Rectangle(ctl.Location, ctl.Size)
borderRectangle.Inflate(1, 1)
ControlPaint.DrawBorder(e.Graphics, borderRectangle, ctl.ForeColor, ButtonBorderStyle.Dashed)
>>>ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, ctl.ForeColor, ButtonBorderStyle.Dashed)<<< This is the problem!
End Select
Next
End Sub
I've tried adding TypeOf (ctl) Is Form to the second Case and that does not work and I am not sure why!
Case TypeOf (ctl) Is ComboBox, TypeOf (ctl) Is Button, TypeOf (ctl) Is Form
This code works perfect for the comboboxes and the textboxes but it does not draw the border on the form.
I'm answering my own question because I've finally got it working.
First of all I changed how I was drawing the border around the form.
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
' This is the override paint event that will allow us to draw all our borders
addBorders(e)
If e.ClipRectangle.X = 0 Then
Dim dashValues As Single() = {3, 1, 3, 1}
Dim p As New Pen(btnExit.ForeColor, 1)
p.DashPattern = dashValues
e.Graphics.DrawRectangle(p, 0, 0, Me.Width - 1, Me.Height - 1)
End If
End Sub
Which worked perfect! There was still a small issue with going off the screen it would still mess the border up some so I added a Me.Refresh() code on the form_mouseup event so when the form is dropped it refreshes and fixes the problem.
Private Sub frmMain_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
Me.Refresh()
End Sub
It's not exactly what I would have liked to have but it works way better this way than the other way.

How to rotate a line shape 90 Degrees?

I am going to have a form with 60 Line Shapes (Using Visual Studios PowerPack). I would like the user to be able to rotate the shapes 90 Degrees using the left and right buttons on the keyboard.
What would be the best way to do this? I have tried other methods however this amounts to 1000's of lines of code, I am still learning and I want to know the best practices.
Thanks a lot!
I'm assuming for the moment that you've already written the part to handle the geometry once, and are asking about how to re-use the code, without duplicating it for 60 lines. This matters because it's not 100% clear from the question whether you're rotating around the mid-point or around the starting point, as the LineShape type does make a distinction between the Starting and Ending points. Without that information, I can't write the geometry code for you.
The first part isn't so bad. We just setup a few methods that can handle rotating any line:
'Note that rotating a line 90 degrees around it's midpoint
' will give the same result whether you go clockwise or counterclockwise,
' but I figure you'll want to adapt this for other shapes later, or that
' you're rotating around the line's starting point
Private Sub RotateClockwise(ByVal line As LineShape)
'Code to rotate the passed line clockwise here
Dim x1 As Integer = line.X1
Dim y1 As Integer = line.Y1
Dim x2 As Integer = line.X2
Dim y2 As Integer = line.Y2
End Sub
Private Sub RotateCounterclockwise(ByVal line As LineShape)
'Code to rotate the passed line counter-clockwise here
End Sub
Private Sub LineShape_KeyDown(Byval sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs)
'Which line?
Dim line As LineShape = TryCast(sender, LineShape)
If line Is Nothing Then Exit Sub
'Left key?
If e.KeyCode = Keys.Left Then RotateCounterclockwise(line)
'Right key?
If e.KeyCode = Keys.Right Then RotateClockwise(line)
End Sub
This is where it gets tricky. Notice that the event handler above is missing the Handles keyword. We want to hookup the KeyDown event handler for all of your LineShape controls to this one method. This will be a bit repetitive, as it means one additional line of code for each line on your form, but it's better than needing to write the above code for all of your lines:
Dim Lines As New List(Of LineShape)()
Lines.Add(LineShape1)
Lines.Add(LineShape2)
'...
Lines.Add(LineShape60)
For Each Line As LineShape In Lines
AddHandler Line.KeyDown, AddressOf LineShape_KeyDown
Next
That code goes in your form's constructor after the InitializeComponent() method.
I could do better still if the LineShape type were a true control (For EAch Line In Me.Controls.OfType(Of LineShape)()), but the docs show this is actually a Component, and not a Control.
Alternatively, you can subclass LineShape and build "rotatability" into your new class as :
Imports Microsoft.VisualBasic.PowerPacks
Public Class MySmartLine
Inherits LineShape
Private Sub RotateClockwise()
'Code to rotate clockwise here
Me.X1 = ...
Me.X2 = ...
End Sub
Private Sub RotateAntiClockwise()
'Code to rotate anti clockwise here
End Sub
Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyDown(e)
If e.KeyCode = Keys.Left Then
RotateAntiClockwise()
End If
If e.KeyCode = Keys.Right Then
RotateClockwise()
End If
End Sub
End Class
After building your project, a custom MySmartLine component will appear in your toolbox and you can use it in place of LineShape.

Setting a Button in a sub?

So, I have a little problem. Im making a application (in visual basic), and I have a way so that I can set a color when you hover over it. Now, I want to do this for all buttons, but make it a little easier by making a sub that can do this for me. The problem is, how can my sub tell which button to initialize the custom hover color? Here is my code.
Private Sub initButton(ByVal color As Color)
Button1.TabStop = False
Button1.FlatStyle = FlatStyle.Flat
Button1.FlatAppearance.BorderSize = 0
Button1.FlatAppearance.BorderColor = color
Button1.FlatAppearance.CheckedBackColor = color
Button1.FlatAppearance.MouseDownBackColor = color
Button1.FlatAppearance.MouseOverBackColor = color
End Sub
Now, how can I set Button1 to the button I want to initialize? Is there anyway to put that as an argument? If you find a answer, please reply.
As Plutonix suggested, using the Hover Event of the buttons would be the first logical choice. You will need to add a Parameter to your your Sub to pass in the button being hovered over.
Private Sub Button1_MouseHover(sender As Object, e As EventArgs)
Handles Button1.MouseHover,Button2.MouseHover 'add more buttons....
initButton(CType(sender, Button),Color.Blue)
End Sub
Private Sub initButton(hoverButton As Button, ByVal color As Color)
hoverButton.TabStop = False
hoverButton.FlatStyle = FlatStyle.Flat
hoverButton.FlatAppearance.BorderSize = 0
hoverButton.FlatAppearance.BorderColor = color
hoverButton.FlatAppearance.CheckedBackColor = color
hoverButton.FlatAppearance.MouseDownBackColor = color
hoverButton.FlatAppearance.MouseOverBackColor = color
End Sub

Set BackGround Color of Textbox to Transparent

I am trying to set the background color of textbox to transparent, to blend with my backcolor of my form.
I have tried the following below.
TextBox1.BackColor = Color.Transparent 'This doesn't work it stays white'
Is there something I am missing?
When I set TextBox.BackColor to Color.Transparent, it throws System.ArgumentException. I got this message :
Unvalid property value, The control does not support transparent colors background.
Hope am not late to the party, but this actually works for me. First create a class for the panel as below
Partial Public Class iPanel
Inherits Panel
Public Sub New()
SetStyle(ControlStyles.SupportsTransparentBackColor Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
BackColor = Color.Transparent
End Sub
End Class
Then create a RichTextBox (Instead of a Textbox) as below
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim CP As CreateParams = MyBase.CreateParams
CP.ExStyle = CP.ExStyle Or &H20
Return CP
End Get
End Property
Now compile the code and add the iRichTextBox inside the panel. Works for me
Private Sub TextBox1_Paint(sender As Object, e As PaintEventArgs) Handles TextBox1.Paint
TextBox1.ForeColor = Color.White
TextBox1.BackColor = Color.Transparent
End Sub
Instead of doing the first one, You can try this. Just put your code in Form Load
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.BackColor = color.(color of your Choice, same color of your background)
TextBox1.ForeColor = color.White
End Sub
As simple as that, it works for me
As far as I know textbox does not support transparent color property. But if you set the back color of the textbox to the same color as of its background component, still it can be considered as transparent.
How to do that - You can get the color name of the background component(in your case it is the form) and pass that name to the component which you want to be transparent.
Dim lname As String = Me.BackColor.ToString
Dim name As String = lname.Substring(7, lname.Length - 8)
txtbox1.BackColor = System.Drawing.Color.FromName(name)
Explanation -
The first line in the code gets you the name of the color, but there's a rub, it gets the name something like this - Color [Dark Orange] and we need only the name of the color i.e Dark Orange.
Thus the second line is to get the exact color name by removing this - Color [] part
And the last line to set that color same as of the background component color.
Hope it works, still have problem let me know...