Is it possible to show/hide the scroll bar in a text box only when the line count in the text box is more than the number of lines displayed?
Consider using the RichTextBox -- it has that behavior built in.
Thanks dummy, it works! Here short version of dummy answer in c#
Call this code at the end of your SizeChanged and TextChanged handlers:
Size textBoxRect = TextRenderer.MeasureText(
this.YourTextBox.Text,
this.YourTextBox.Font,
new Size(this.YourTextBox.Width, int.MaxValue),
TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
try
{
this.YourTextBox.ScrollBars = textBoxRect.Height > this.YourTextBox.Height ?
ScrollBars.Vertical :
ScrollBars.None;
} catch (System.ComponentModel.Win32Exception)
{
// this sometimes throws a "failure to create window handle" error.
// This might happen if the TextBox is unvisible and/or
// too small to display a toolbar.
}
Public Class TextBoxScrollbarPlugin
Private WithEvents mTarget As TextBox
''' <summary>
''' After the Handle is created, mTarget.IsHandleCreated always returns
''' TRUE, even after HandleDestroyed is fired.
''' </summary>
''' <remarks></remarks>
Private mIsHandleCreated As Boolean = False
Public Sub New(item As TextBox)
mTarget = item
mIsHandleCreated = mTarget.IsHandleCreated
End Sub
Private Sub Update()
If Not mTarget.IsHandleCreated Then
Return
ElseIf Not mIsHandleCreated Then
Return
End If
Dim textBoxRect = TextRenderer.MeasureText(mTarget.Text,
mTarget.Font,
New Size(mTarget.Width, Integer.MaxValue),
TextFormatFlags.WordBreak + TextFormatFlags.TextBoxControl)
Try
If textBoxRect.Height > mTarget.Height Then
mTarget.ScrollBars = ScrollBars.Vertical
Else
mTarget.ScrollBars = ScrollBars.None
End If
Catch ex As System.ComponentModel.Win32Exception
'this sometimes throws a "failure to create window handle"
'error.
'This might happen if the TextBox is unvisible and/or
'to small to display a toolbar.
If mLog.IsWarnEnabled Then mLog.Warn("Update()", ex)
End Try
End Sub
Private Sub mTarget_HandleCreated(sender As Object, e As System.EventArgs) Handles mTarget.HandleCreated
mIsHandleCreated = True
End Sub
Private Sub mTarget_HandleDestroyed(sender As Object, e As System.EventArgs) Handles mTarget.HandleDestroyed
mIsHandleCreated = False
End Sub
Private Sub mTarget_SizeChanged(sender As Object, e As System.EventArgs) Handles mTarget.SizeChanged
Update()
End Sub
Private Sub mTarget_TextChanged(sender As Object, e As System.EventArgs) Handles mTarget.TextChanged
Update()
End Sub
End Class
Private mPlugins As New List(Of Object)
Private Sub Form_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxOne))
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxTwo))
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxThree))
End Sub
I've got tnimas solution working in vb. Functions quite well as written and I've not seen the errors.
Private Sub TextBoxSizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
Dim textBoxRect As Size = TextRenderer.MeasureText(TextBox.Text, TextBox.Font, New Size(TextBox.Width, Integer.MaxValue), TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl)
Try
TextBox.ScrollBar = If(textBoxRect.Height > TextBox.Height, ScrollBars.Vertical, ScrollBars.None)
Catch ex As Exception
'handle error
End Try
End Sub
I myself tried tnimas' solution but unable to catch the exception, so I use the WinApi to toggle the visible state of scrollbars instead like so:
Size textBoxRect = TextRenderer.MeasureText(YourTextBox.Text, YourTextBox.Font, new Size(YourTextBox.Width, int.MaxValue), TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
WinApi.ShowScrollBar(YourTextBox.Handle, (int)WinApi.ScrollBar.SB_VERT, textBoxRect.Height > YourTextBox.Height ? true : false);
This method does not cause the exception, though should note that hiding scrollbars like this will disable scroll messages, but this is fine if you're just hiding the scrollbars for when the text area can't be scrolled anyway.
Related
I have created a class to use as a notification window (similar to toast notifications, which are disabled on our system).
I use a timer object to timeout the closing the form, and a backgroundworker to handle the animation of it sliding in from the bottom of the screen. For debugging purposes the form just outputs it's own size and the screen bounds.
Imports System.ComponentModel
Public Class ASNotify
Public Sub New(ByVal title As String, ByVal msg As String, ByVal Optional timeout As Integer = 5000)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.Text = title
Me.NotifyMessage.Text = $"{Me.Width}x{Me.Height}{vbCrLf}{My.Computer.Screen.WorkingArea.Size.Width}x{My.Computer.Screen.WorkingArea.Size.Height}"
TimeoutTimer.Interval = timeout
TimeoutTimer.Enabled = True
AnimationWorker.RunWorkerAsync()
End Sub
Private Sub AnimationWorker_DoWork(sender As Object, e As DoWorkEventArgs) Handles AnimationWorker.DoWork
Dim xloc As Integer = My.Computer.Screen.WorkingArea.Size.Width - Me.Width
Dim yloc As Integer = My.Computer.Screen.WorkingArea.Size.Height
For x As Integer = 0 To Me.Height
MoveWindow(xloc, yloc - x)
Threading.Thread.Sleep(2)
Next
End Sub
Private Sub MoveWindow(xloc As Integer, yloc As Integer)
If InvokeRequired Then
Invoke(Sub() MoveWindow(xloc, yloc))
Else
Location = New Drawing.Point(xloc, yloc)
End If
End Sub
Private Sub TimeoutTimer_Tick(sender As Object, e As EventArgs) Handles TimeoutTimer.Tick
Me.Close()
End Sub
End Class
I call this from another form by calling
Private Sub NotifyUser(ByRef a As Alert.Alert)
Dim notify As New ASNotify(a.Location, a.Comment, 5000)
notify.Show()
End Sub
I call that sub by pressing a button on the form, and it works perfectly.....Sometimes.
repeatedly triggering the notify window makes it pops up as one of 2 different sizes on the screen, although the contents showing the size always state 264x81 and screenbounds 1920x1040
and occasionally I get an exception that the line saying 'Location = new Drawing.Point(xloc,yloc) is being called from a thread other than that which it was created on, despite the Invoke call.
Moved the timer-start and animationworker start to the Load method of the form, rather than New.
Private Sub ASNotify_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TimeoutTimer.Enabled = True
AnimationWorker.RunWorkerAsync()
End Sub
VB2012: In order to do some calculations in my main form I need to know the form size of a secondary form. The form size may change from user to user depending on OS and theme. I understand that the client size stays the same. However I think I am not doing something correctly as I get different numbers depending on where I call for the form size.
As an illustration here is my main form where on the load event I attempt to get the size of an Alert form
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'get the default width and height of an Alert form.
Dim frmW As Integer = frmAlert.Width 'in pixels
Dim frmH As Integer = frmAlert.Height 'in pixels
Dim frCsW As Integer = frmAlert.ClientSize.Width 'in pixels
Dim frmCsH As Integer = frmAlert.ClientSize.Height 'in pixels
Debug.Print("frmW={0} frmH={1} frCsW={2} frmCsH={3}", frmW, frmH, frCsW, frmCsH)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'set up a new alert form
Dim frm As New frmAlert
'show the alert form
frm.StartPosition = FormStartPosition.CenterParent
frm.Show() 'with this option the Alert Forms stay on the screen even if the Main form is minimized.
End Sub
Now the Alert form is set with FormBorderStyle=FixedDialog, ControlBox=False, MaximizeBox=False, and MinimizeBox=False and in the Alert form I have this on the load event:
Private Sub frmAlert_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.Print("me.width={0} me.height={1} cs.width={2} cs.height={3}", Me.Width, Me.Height, Me.ClientSize.Width, Me.ClientSize.Height)
End Sub
and here is the debug output
frmW=380 frmH=168 frCsW=374 frmCsH=162
me.width=390 me.height=200 cs.width=374 cs.height=162
As expected the client size is the same but the total form size is different. I am trying to wrap my head around the differences in .Height and .Width. No other code exists to change the form properties. The second debug statement matches the form Size in the IDE designer. Why are the dimensions different? How would I properly query to get the form size from another form?
Before the form is shown it will have a smaller size compared to when it is visible. This is because when you show the form Windows will do all kinds of stuff to it based on the user's screen and theme settings, such as:
Resize it if the user has different DPI settings.
Apply borders to it based on the user's selected window theme.
(etc.)
Your best bet is to show the form first, then get its size.
If you don't want the form to be visible right away you can set its Opacity property to 0 to make it invisible, then change it back to 1.0 once you need the form to be shown to the user.
Ok so based on #Visual Vincent's suggestion I created a constructor when creating a new form. This goes in frmAlert.Designer.vb
Partial Class frmAlert
Private mIsProcessFormCode As Boolean = True
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.mIsProcessFormCode = True
End Sub
Public Sub New(ByVal IsProcessFormCode As Boolean)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.mIsProcessFormCode = IsProcessFormCode
End Sub
End Class
Then on the frmMain I add this code:
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.Print("routine={0}", System.Reflection.MethodBase.GetCurrentMethod.Name)
Dim frm As New frmAlert(False) 'create an instance of the form without any of the Form processes
frm.Opacity = 0 'makes the form totally transparent
frm.Visible = False
frm.Show()
Dim frmWidth As Integer = frm.Width
Dim frHeight As Integer = frm.Height
Dim frCsWidth As Integer = frm.ClientSize.Width
Dim frCsHeight As Integer = frm.ClientSize.Height
frm.Close()
frm.Dispose()
Debug.Print("frmWidth={0} frHeight={1} frCsWidth={2} frCsHeight={3}", frmWidth, frHeight, frCsWidth, frCsHeight)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Debug.Print("routine={0}", System.Reflection.MethodBase.GetCurrentMethod.Name)
'set up the alert form normally
Dim frm As New frmAlert
'show the alert form
frm.StartPosition = FormStartPosition.CenterParent
frm.Show() 'with this option the Alert Forms stay on the screen even if the Main form is minimized.
End Sub
and in the frmAlert I add this code:
Private Sub frmAlert_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
Try
'process the form code ONLY if required
Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
If mIsProcessFormCode Then
'do Closed stuff
End If
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End Sub
Private Sub frmAlert_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
Try
'process the form code ONLY if required
Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
If mIsProcessFormCode Then
'do Closing stuff
End If
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End Sub
Private Sub frmAlert_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
'process the form code ONLY if required
Debug.Print("routine={0} mIsProcessFormCode={1}", System.Reflection.MethodBase.GetCurrentMethod.Name, mIsProcessFormCode)
Debug.Print("me.width={0} me.height={1} cs.width={2} cs.height={3}", Me.Width, Me.Height, Me.ClientSize.Width, Me.ClientSize.Height)
If mIsProcessFormCode Then
'process text file
End If
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End Sub
and the debug output:
routine=frmMain_Load
routine=frmAlert_Load mIsProcessFormCode=False
me.width=390 me.height=200 cs.width=374 cs.height=162
routine=frmAlert_FormClosing mIsProcessFormCode=False
routine=frmAlert_FormClosed mIsProcessFormCode=False
frmWidth=390 frHeight=200 frCsWidth=374 frCsHeight=162
routine=Button1_Click
routine=frmAlert_Load mIsProcessFormCode=True
me.width=390 me.height=200 cs.width=374 cs.height=162
I believe this is what I want. All frmAlert sizes match what I have in the IDE designer. If you can think of any modifications please let me know. Many thanks.
My UserControl button disappears when I debug my program. I have checked the code including the designer.vb code countless times there's nothing that makes the button .enabled = false or .visible = false. Any ideas why this is happening?
On my UserControl:
Private Sub btn_Begin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Begin.Click
Start_Race()
End Sub
Public Sub Start_Race()
TimeNow(Past_Time)
TimeNow(Start_Time)
lbl_Start_Time_Driver.Text = Past_Time
btn_Begin.BackColor = Color.Green
btn_Begin.Text = "Started!"
End Sub
Public Property Active_bool As Boolean
Get
Return btn_Begin.Visible
End Get
Set(ByVal value As Boolean)
btn_Begin.Visible = value
End Set
End Property
On Form1:
Private Sub btn_Start_All_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Start_All.Click
Dim allActiveUserControls = From uc_Index In Controls.OfType(Of LapTimerGUI)()
Where uc_Index.Active_bool
For Each User_Control In allActiveUserControls
User_Control.Start_Race()
Next
End Sub
I do Google my head off before I post my ridiculous questions here btw :)
This is strange. Does any MsgBoxes pop up if you add this code to your UserControl:
Private Sub UserControl_ControlRemoved(sender As Object, e As System.Windows.Forms.ControlEventArgs) Handles Me.ControlRemoved
MsgBox("Control Removed!")
End Sub
Private Sub Button2_EnabledChanged(sender As Object, e As System.EventArgs) Handles Button2.EnabledChanged
MsgBox("EnabledChanged!")
End Sub
If so, then you can add a breakpoint to these MsgBoxes and lock at the CallStack (CTRL+L) from where it triggers.
Btw: If the control is removed somehow, .PerformClick() still triggers (for me). Thus I bet, that the control is somehow disabled (Enabled = False).
Lastly, if any container of the button (such as your UserControl) is disabled, the button will be disabled too,
After lots of playing around I finally found the problem!
The value was set to =False in my properties. I'm so blonde! Thanks guys for the help ^_^/
Public Property Active_bool As Boolean
Get
Return btn_Begin.Visible
End Get
Set(ByVal value As Boolean)
btn_Begin.Visible = value
End Set
End Property
Although, Something sets the values to =False ever now and then. Very annoying :3
And I can not set the value to =True in the properties... Only in the hidden designer code...
Is there any way to turn the damned error provider off when i try to close the form using the windows close button(X). It fires the validation and the user has to fill all the fields before he can close the form..this will be a usability issue because many tend to close the form using the (X) button.
i have placed a button for cancel with causes validation to false and it also fires a validation.
i found someone saying that if you use Form.Close() function validations are run...
how can i get past this annoying feature.
i have a MDI sturucture and show the form using
CreateExam.MdiParent = Me
CreateExam.Show()
on the mdi parent's menuitem click
and have this as set validation
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Any help is much appreciated.
googling only showed results where users were having problem using a command button as close button and that too is causing problem in my case
The ValidateChildren() method prevents the form from closing. Paste this code in your form to fix that:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
}
This is quite simple fix, in your Form's Closing Event, set a flag to indicate leaving the form, for example blnLeave, when the Form gets loaded, set the flag to False, when the Closing event gets triggered, set that to True within that event handler, then the change as follows would be
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If (blnLeave) Then
e.Cancel = False;
Return
End If
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Edit: Amended this answer for inclusion as per OP's comments. My suggestion is to handle the Form's Closed Event as shown
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
blnLeave = True
End Sub
And handle it here in the Form's window procedure override as shown here....
Private Const SC_CLOSE As Integer = &HF060
Private Const WM_MENUSELECT As Integer = &H11F
Private Function LoWord(ByVal Num As Integer) As Integer
LoWord = Num & &HFFFF
End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_MENUSELECT Then
If LoWord(m.WParam.ToInt32()) = SC_CLOSE Then
' Handle the closing via system Menu
blnLeave = True
End If
End If
MyBase.WndProc(m)
End Sub
How do you disable additional checkbox selections/deselections without sacrificing the functionality of the ListView? I know you can call: ListView.Enabled = False, but that also disables any scrolling within it.
For example: I have a timer that starts a backup based on the Listview items that are checked. After a certain time, I don't want the end-user to be able to click on any of the checkboxes within the listview (so I have a set number of items to backup), but I do want them to be able to scroll the list while the backup is being performed. I tried this:
Private Sub clboxOptions_ItemChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs) Handles clboxOptions.ItemChecked
If backupStarted = True Then
If e.Item.Checked = True Then
e.Item.Checked = False
Else
e.Item.Checked = True
End If
But this doesn't seem to work for me.
Thanks!
JFV
Here is an other method to disable the users click on listviewitem checkbox.
Public Sub ChangeItemCheckState(ByVal val As Boolean, ByVal index As Integer)
If Monitor.TryEnter(Me.Items(index), 10) Then
Try
Me.Items(index).Checked = val
Finally
Monitor.Exit(Me.Items(index))
End Try
End If
End Sub
Public Sub ChangeItemCheckState(ByVal val As Boolean, ByVal item As ListViewItem)
If Monitor.TryEnter(item, 10) Then
Try
item.Checked = val
Finally
Monitor.Exit(item)
End Try
End If
End Sub
Private Sub ListviewOPC_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles Me.ItemCheck
If Monitor.IsEntered(Me.Items(e.Index)) Then
'
Else
'prevent click from users
e.NewValue = e.CurrentValue
End If
End Sub
this method is thread safe. To change the checkedstate of an item you have to call the ChangeItemCheckState methods. If you want to enable/disable the itemcheck by click, you have to add another property.
Private disableUserCheckItem As Boolean
Public Property PreventUserCheckItem() As Boolean
Get
Return disableUserCheckItem
End Get
Set(ByVal value As Boolean)
disableUserCheckItem = value
End Set
End Property
Public Sub ChangeItemCheckState(ByVal val As Boolean, ByVal index As Integer)
If Monitor.TryEnter(Me.Items(index), 10) Then
Try
Me.Items(index).Checked = val
Finally
Monitor.Exit(Me.Items(index))
End Try
End If
End Sub
Public Sub ChangeItemCheckState(ByVal val As Boolean, ByVal item As ListViewItem)
If Monitor.TryEnter(item, 10) Then
Try
item.Checked = val
Finally
Monitor.Exit(item)
End Try
End If
End Sub
Private Sub ListviewOPC_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles Me.ItemCheck
If Monitor.IsEntered(Me.Items(e.Index)) Then
'do nothing or other nessesary things.
Else
'prevent click from users
If PreventUserCheckItem Then
e.NewValue = e.CurrentValue
End If
End If
End Sub
Instead using the built-in CheckBoxes property, you could draw the check boxes yourself.
Google around and find an example of an OwnerDraw ListView. Draw the checkboxes yourself. Add a new property to your ListView (something like ReadOnly). When ReadOnly is true, draw the checkboxes as disabled and ignore the click messages.
You could use ObjectListView (which is a wrapper around a normal .NET ListView). It provides a callback, CheckStatePutter, which is called when the user clicks on a checkbox. In that callback, you can decide whether or not to accept the new checkbox value.
This is a recipe describing this process: How do I use checkboxes in my ObjectListView?
I found out what my issue was. I was using the 'ItemChecked' instead of the 'ItemCheck' Method. The below code works for me:
Private Sub clboxOptions_ItemCheck(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles clboxOptions.ItemCheck
Try
If backupStarted = True Then
If e.CurrentValue <> e.NewValue Then
e.NewValue = e.CurrentValue
Else
e.NewValue = e.NewValue
End If
End If
End Sub
I want disable CheckBox in Listview. When I click Button Go. I was using the 'ItemChecked' Method. I use code this:
Public Sub CheckBoxChecked(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs)
Try
If bCheckFromEvent Then
bCheckFromEvent = False
Return
End If
If BrunService Then
bCheckFromEvent = True
ListView.Items(e.Item.Index).Checked = Not ListView.Items(e.Item.Index).Checked
End If
Catch ex As Exception
MsgBox("CheckBoxChecked: " & ex.Message, MsgBoxStyle.Critical Or MsgBoxStyle.OkOnly, "ERROR")
End Try
End Sub