How to embed a Console in a Windows Form application? - vb.net

I'm trying to build a Text Adventure game in VB.net, just like the days of old. The obvious choice would be a Console application, however, I have decided on a Windows Form because I am hoping to include interactive buttons and pictures. Currently, I have already got on my form a picture box and a Rich Text Box. I was hoping that with the Rich Text Box I could achieve something that worked in the same way as a console. Alas, my efforts are futile. Everything I have tried has failed, including: reading Rich_Text_Box.Text and Rich_Text_Box_KeyUp with an if statement for enter being pressed to call the procedure for an enter button.
I was wondering if there was any way to include a Console with standard Console.WriteLine and Console.ReadLine capabilities inside of my Form? This would very much shorten my task and streamline the whole process.
Any ideas?

You could use not one but two Textboxes for your purpose. tbOutput and tbInput. tbOutput would be Multiline and ReadOnly whereas tbInput would be single line, not readonly and placed beneath tbOutput. Then to process inputs you could do something like:
Private Sub Output(s As String)
If s <> "" Then
tbOutput.AppendText(vbCrLf & ">> " & s)
End If
End Sub
Private Sub tbInput_KeyDown(sender As Object, e As KeyEventArgs) Handles tbInput.KeyDown
If e.KeyCode = Keys.Enter Then
If tbInput.Text <> "" Then
Output(tbInput.Text)
' Handle input value
tbInput.Text = ""
End If
End If
End Sub
At the 'Handle input value you would check the user input and handle it according to your needs. Use Lucida Console font in bold in gray and black background for style :-)

Sure, a RichTextBox can be used to emulate a console. Some surgery is required to avoid the user from making it malfunction as a console. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form. Subscribe the InputChanged event to detect when the user presses the Enter key, the Input property gives you the typed text. Use the Write() or WriteLine() methods to add text.
Imports System.Windows.Forms
Public Class RichConsole
Inherits RichTextBox
Public Event InputChanged As EventHandler
Public ReadOnly Property Input() As String
Get
Return Me.Text.Substring(InputStart).Replace(vbLf, "")
End Get
End Property
Public Sub Write(txt As String)
Me.AppendText(txt)
InputStart = Me.SelectionStart
End Sub
Public Sub WriteLine(txt As String)
Write(txt & vbLf)
End Sub
Private InputStart As Integer
Protected Overrides Function ProcessCmdKey(ByRef m As Message, keyData As Keys) As Boolean
'' Defeat backspace
If (keyData = Keys.Back OrElse keyData = Keys.Left) AndAlso InputStart = Me.SelectionStart Then Return True
'' Defeat up/down cursor keys
If keyData = Keys.Up OrElse keyData = Keys.Down Then Return True
'' Detect Enter key
If keyData = Keys.[Return] Then
Me.AppendText(vbLf)
RaiseEvent InputChanged(Me, EventArgs.Empty)
InputStart = Me.SelectionStart
Return True
End If
Return MyBase.ProcessCmdKey(m, keyData)
End Function
Protected Overrides Sub WndProc(ByRef m As Message)
'' Defeat the mouse
If m.Msg >= &H200 AndAlso m.Msg <= &H209 Then Return
MyBase.WndProc(m)
End Sub
End Class

Related

How to prevent ALT+F4 of a WinForm but allow all other forms of closing a WinForm?

I've searched around the interwebs and various parts of this resource where this question was asked and noticed I got the following bits of code:
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
Const CS_NOCLOSE As Integer = &H200
cp.ClassStyle = cp.ClassStyle Or CS_NOCLOSE
Return cp
End Get
End Property
Which works as intended, this does disable ALT+F4 from being used. However, as an unintended side effect of this code: closing the window via the Control Box is disabled:
Is there a version of this code that disables ALT+F4 BUT still allows for the closing of the window via its control box or other UI options (such as a close button and a Close option in a menu.)
I know someone will say to check the e.CloseReason of the form, however UserClosing is the only reason the resembles what I would like to do, however... that still disables the UI from being used. Unless there is a code that I forgot about.
Set KeyPreview = True and handle the KeyDown event:
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.Alt AndAlso e.KeyCode = Keys.F4 Then
e.Handled = True
End If
End Sub
Answer to your comment, handling KeyDown from a separate class.
Documentation:
AddHandler statement
Shared access modifier
Public NotInheritable Class MainInterface
Private Sub New() 'No constructor.
End Sub
Public Shared Sub DisableAltF4(ByVal TargetForm As Form)
TargetForm.KeyPreview = True
AddHandler TargetForm.KeyDown, AddressOf Form_KeyDown
End Sub
Private Shared Sub Form_KeyDown(sender As Object, e As KeyEventArgs)
e.Handled = (e.Alt AndAlso e.KeyCode = Keys.F4)
End Sub
End Class
Now in every form's Load event handler you can do:
Private Sub yourForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MainInterface.DisableAltF4(Me)
End Sub
As Olaf said you can also make all forms inherit from a base class. However this might get a little bit more complicated as you have to tell both the yourForm.vb and the yourForm.Designer.vb file that you want to inherit from the base form.
Public Class BaseForm
Inherits Form
Protected Overrides Sub OnLoad(e As System.EventArgs)
MyBase.OnLoad(e)
Me.KeyPreview = True
End Sub
Protected Overrides Sub OnKeyDown(e As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyDown(e)
e.Handled = e.Handled OrElse (e.Alt AndAlso e.KeyCode = Keys.F4)
End Sub
End Class
In yourForm.vb:
Public Class yourForm
Inherits BaseForm
...code...
End Class
In yourForm.Designer.vb:
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class yourForm
Inherits yourNamespace.BaseForm
...code...
End Class
You should also remove the corresponding CLOSE menu item from the forms system menu using a RemoveMenu() interop call. This disables all default window close options.
Of course you can call Form.Close() in your code to close your form. That can be triggered by a Click event handler of a custom button, menu item etc. Additionally, you can implement an System.Windows.Forms.IMessageFilter to handle a custom key sequence (instead of ALT+F4) to close your form, e.g. C+L+O+S+E.
Easy:
In C#
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Alt | Keys.F4))
{
return true; // The key is manually processed
}
else
return base.ProcessCmdKey(ref msg, keyData);
}
In VB.Net
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
If keyData = (Keys.Alt Or Keys.F4) Then
Return True
Else
Return MyBase.ProcessCmdKey(msg, keyData)
End If
End Function

Visual Basic Help - Text limitations in a visual basic text box to prevent certain characters being entered

This is a simple thing that I have been trying to figure out for a while now and its starting to annoy. All I want to is when a button is pressed only certain values are allowed to appear in the textbox. What is meant by this is for example only allow "abc123!" in the textbox and if say a value such as "w" then clear the textbox.
I have tried things such as 'If Not Regex.Match' but it is just causing me errors.
Please help ;)
You would want to use a white list. Your allowed characters would be a much smaller list than every other character in existence. You can do this a few ways. You can handle the key press event on the text box and if that value is whatever, then you execute your code. The other way you can do this (say if it was a winforms app) would be to inherit from the textbox and put your code there (you could re-use this control then). Here is an example of a TextBox that only allows numeric input:
''' <summary>
''' Text box that only accepts numeric values.
''' </summary>
''' <remarks></remarks>
Public Class NumericTextBox
Inherits TextBox
Private Const ES_NUMBER As Integer = &H2000
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim params As CreateParams = MyBase.CreateParams
params.Style = params.Style Or ES_NUMBER
Return params
End Get
End Property
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
'Prevent pasting of non-numeric characters
If keyData = (Keys.Shift Or Keys.Insert) OrElse keyData = (Keys.Control Or Keys.V) Then
Dim data As IDataObject = Clipboard.GetDataObject
If data Is Nothing Then
Return MyBase.ProcessCmdKey(msg, keyData)
Else
Dim text As String = CStr(data.GetData(DataFormats.StringFormat, True))
If text = String.Empty Then
Return MyBase.ProcessCmdKey(msg, keyData)
Else
For Each ch As Char In text.ToCharArray
If Not Char.IsNumber(ch) Then
Return True
End If
Next
Return MyBase.ProcessCmdKey(msg, keyData)
End If
End If
ElseIf keyData = (Keys.Control Or Keys.A) Then
' Process the select all
Me.SelectAll()
Else
Return MyBase.ProcessCmdKey(msg, keyData)
End If
End Function
End Class
If you just want to use a TextBox and a KeyPress event you can do something like this. I only have two characters in my white list, you'd want to include the characters for everything you'd want included:
Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TextBox1.KeyPress
' Test white list, this is only 0 and 1 which are ASCII 48 and 49
Dim allowedChars() As String = {Chr(48), Chr(49)}
If allowedChars.Contains(e.KeyChar) Then
' Setting handled to true stops the character from being entered, remove this or execute your code
' here that you want
e.Handled = True
End If
End Sub
If you want a list of the char codes you can get them here:
http://www.asciitable.com/
Hope this helps. ;-)

textbox multiline, length issues

I have a textbox with multiline set to true. I want to have max characters set to 50 per line with a total of 3 lines. When they reach the 50 characters, I would like it to jump to the second line.
I am having some issues and have been struggling with this for a while and wanted to know if anyone can help.
MAX_LINE_COUNT = 3
Private Sub txtMsg_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtMsg.KeyDown
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = (Me.txtMsg.Lines.Length >= MAX_LINE_COUNT)
End If
End Sub
To effectively handle multiple lines of text with a common max characters per line, then you will need to extend the TextBox class and override several items in the TextBox class. Instead of re-inventing the wheel, I am going to redirect you to the code from an answer to Is there a way to catch maximum length PER LINE and not allow user to input more characters if max length PER LINE has been reached?, since it is not the accepted answer, I will paste the VB.NET translation below:
Public Class MaxPerLineTextBox
Inherits TextBox
Public Sub New()
MyBase.Multiline = True
End Sub
Public Overrides Property Multiline() As Boolean
Get
Return True
End Get
Set
Throw New InvalidOperationException("Readonly subclass")
End Set
End Property
Public Property MaxPerLine() As System.Nullable(Of Integer)
Get
Return m_MaxPerLine
End Get
Set
m_MaxPerLine = Value
End Set
End Property
Private m_MaxPerLine As System.Nullable(Of Integer)
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
If Char.IsControl(e.KeyChar) Then
MyBase.OnKeyPress(e)
Return
End If
Dim maxPerLine As Integer
If Me.MaxPerLine.HasValue Then
maxPerLine = Me.MaxPerLine.Value
Else
MyBase.OnKeyPress(e)
Return
End If
Dim activeLine As Integer = Me.GetLineFromCharIndex(Me.SelectionStart)
Dim lineLength As Integer = Me.SelectionStart - Me.GetFirstCharIndexFromLine(activeLine)
If lineLength < maxPerLine Then
MyBase.OnKeyPress(e)
Return
End If
e.Handled = True
End Sub
End Class
To use the above code you will need to do the following:
Create a new project in your solution to hold the code above.
Paste code above into new project and build it.
Ensure that there are no errors and the project compiles successfully.
The MaxPerLineTextBox control should show up in the toolbox. If it does not, then try restarting Visual Studio.
Drag MaxPerLineTextBox onto your form and set the properties.

Capturing the ctrl+V in VB.NET combobox

I am trying to remove the newline and replace with whitespace before pasting to comboBox as it ignores anything beyond a line. I am trying this:
If e.Modifiers = Keys.Control AndAlso e.KeyValue = Keys.V Then Then
Clipboard.SetText(Regex.Replace(Clipboard.GetText(TextDataFormat.UnicodeText), "\n", " "))
e.Handled = True
End If
I am performing this inside KeyDown event but it is able to capture either Ctrl or V but not both. I tried Capture CTRL+V or paste in a textbox in .NET and http://social.msdn.microsoft.com/Forums/windows/en-US/096540f4-4ad4-4d24-ae12-cfb3e1b246f3/interceptingoverriding-paste-behavior-on-combobox but no results as desired. May be there is something i am missing in my code. Please help me out.
I am getting the needed value with this Clipboard.GetText().Replace(vbCrLf, " ") when i debug but i am not able to set it. I tried using a variable to set it but even then no change. I also tried clearing the clipboard and then resetting with this variable holding the modified value.
I am using Winforms and i tried this but still no change to my clipboard:
Private Const WM_PASTE As Integer = &H302
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_PASTE Then
Dim returnText As String = Nothing
If (Clipboard.ContainsText()) Then
returnText = Clipboard.GetText().Replace(vbCrLf, " ")
Clipboard.Clear()
Clipboard.SetText(returnText)
End If
End If
MyBase.WndProc(m)
End Sub
Handling keyboard only events for intercepting pasting doesn't solve the problem, because pasting can also be done using mouse or touch interface.
Thus, if you are using WPF, then simply add the DataObject.Pasting event handler to your ComboBox, so definition of the control in XAML will look like:
<ComboBox Name="comboBox1" IsEditable="true" DataObject.Pasting="comboBox1_Pasting" ... />
And, finally, in your code handle it as (I'm adding a method here to the code-behind, which is not as nice, as using commands):
private void comboBox1_Pasting(object sender, DataObjectPastingEventArgs e)
{
// modify the clipboard content here
}
If you're using WinForms, then look here: hook on default “Paste” event of WinForms TextBox control
This piece of code worked for me:
Private Const WM_PASTE As Integer = &H302
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
If keyData = (Keys.Control Or Keys.V) Or msg.Msg = WM_PASTE Then
If (Clipboard.ContainsText()) Then
Clipboard.SetText(Clipboard.GetText().Replace(vbCrLf, " "))
End If
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Use the keydown event and alter the clipboard like this
Private Sub ComboBox1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles ComboBox1.KeyDown
If e.KeyCode = Keys.V AndAlso (e.Modifiers And Keys.Control) <> 0 Then
My.Computer.Clipboard.SetText(My.Computer.Clipboard.GetText().Replace(vbCrLf, " "))
End If
End Sub
But this example is going to alter the clipboard contents. Modify it to paste or insert yourself as you wish

Text box validation

I am using many text boxes in a form.
How do i validate them,
In certain text boxes I have to use only text and in some I have to use only numbers.
Is using ASCII is a right method or is there any easier method to do this. If so please let me know the coding.
Above all other, don’t annoy the user. If I’m typing some text and the application prevents that (regardless of how it does that), I’m rightfully pissed off.
There are multiple values to handle this:
Use a NumericUpDown or a Slider control instead of a text box for numeric values (in other words: use the correct control instead of a general-purpose control).
Allow (more or less) arbitrary input and try to parse the user input in a meaningful way. For example, entering “+33 (0) 6 12-34-56” is an entirely meaningful format for a phone number in France. An application should allow that, and try to parse it correctly.
Granted, this is the hardest way, but it provides the best user experience.
Use the Validating event to validate input. This is automatically triggered whenever the user leaves the input control, i.e. when they have finished their input, and a validation will not annoy the user.
The MSDN documentation of the event gives an example of how this event is used correctly.
But do not use the KeyPress or TextChanged events to do validation. The first will disturb the users when entering text. The second will also annoy them when they try to paste text from somewhere else. Imagine the following: I am trying to copy an number from a website. Unfortunately, the text I have copied includes something else, too, e.g. “eggs: 14.33 EUR” instead of just “14.33”.
Now, the application must give me the chance to paste and correct the text. If I am not allowed to do that, the application is a UX failure. If the application uses the TextChanged event to prevent my pasting this text, I don’t get the chance to delete the offending text.
Text only limited to 40 characters:
<asp:RegularExpressionValidator ID="regexpText" runat="server"
ErrorMessage="Text only!"
ControlToValidate="txtName"
ValidationExpression="^[a-zA-Z]{1,40}$" />
Only Numbers:
<asp:RegularExpressionValidator ID="regexpNumber" runat="server"
ErrorMessage="Numbers only!"
ControlToValidate="txtName"
ValidationExpression="^[0-9]$" />
Wow, this can be a very broad topic...
For numeric textboxes, you should probably restrict input during KeyPress event:
Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
Dim allowedChars As String = "0123456789"
If allowedChars.IndexOf(e.KeyChar) = -1 Then
' Invalid Character
e.Handled = True
End If
End Sub
NOTE: This code sample is assuming WinForms, different approach must be used for web...
Regardless of the plat form, you should look into the validation controls offered by the framework, and that will allow you to validate that there is indeed input, values are in a specified range, and also using regex write more complicated validation rules.
The fastest way for validation is using regular expressions. They are harder to understand but offer better performance.
But you could do it also using string functions. Which is easier if you don't know regex but is less performant. This could be a viable option, depending on how hard the validation is.
Here and here are some posts that will help you with code samples.
Agreed that Regular Expressions might be faster, but ... well, here's how I've done it. Basically, this code is for a UserControl which contains a label, a text box, and an error provider. It also has various other properties, but here's the bit which deals with validation.
I do use this on the TextChanged event, because I don't want the user to continue typing if it's an invalid character; the rule checking "eats" the invalid character.
Public Enum CheckType
ctString = 0
ctReal = 1
ctDecimal = 2
ctInteger = 3
ctByte = 4
End Enum
Private mAllowNegative As Boolean = True
Private mAllowNull As Boolean = True
Private mCheckType As CheckType = CheckType.ctString
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
RuleCheckMe()
End Sub
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub RuleCheckMe()
'// Rule Checking
If Me.TextBox1.TextLength = 0 Then
If mAllowNull = False Then
Me.epMain.SetError(Me.TextBox1, "You are required to provide this value.")
Me.Valid = False
Else
Me.epMain.Clear()
Me.Valid = True
End If
Else
Select Case mCheckType
Case CheckType.ctString
If mInputMask.Length > 0 Then
'TODO: Figure out how to cope with input masks!
Me.Valid = True
Else
Me.Valid = True
End If
Case Else '// right now we're only testing for numbers...
If Not IsNumeric(Me.TextBox1.Text) And Me.TextBox1.Text <> "." And Me.TextBox1.Text <> "-" Then
If Not String.IsNullOrEmpty(Me.TextBox1.Text) Then
Me.TextBox1.Text = Me.TextBox1.Text.Remove(Me.TextBox1.Text.Length - 1, 1)
Me.TextBox1.SelectionStart = Me.TextBox1.Text.Length
End If
Me.epMain.SetError(Me.TextBox1, "This field does not accept non-numeric values.")
Me.Valid = False
ElseIf mAllowNegative = False And Me.TextBox1.Text.StartsWith("-") Then
Me.TextBox1.Text = Me.TextBox1.Text.Remove(Me.TextBox1.Text.Length - 1, 1)
Me.epMain.SetError(Me.TextBox1, "This field does not accept negative values.")
Me.Valid = False
ElseIf mCheckType = CheckType.ctByte And CType(Me.TextBox1.Text, Integer) > 255 Then
Me.epMain.SetError(Me.TextBox1, "This field does not accept values greater than 255.")
Me.Valid = False
Else
Me.epMain.Clear()
Me.Valid = True
End If
End Select
End If
End Sub
<System.ComponentModel.Browsable(True), _
System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Visible), _
System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always), _
System.ComponentModel.Category("Data")> _
Public Property AllowNegative() As Boolean
<System.Diagnostics.DebuggerStepThrough()> _
Get
Return mAllowNegative
End Get
<System.Diagnostics.DebuggerStepThrough()> _
Set(ByVal value As Boolean)
mAllowNegative = value
End Set
End Property
<System.ComponentModel.Browsable(True), _
System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Visible), _
System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always), _
System.ComponentModel.Category("Data")> _
Public Property AllowNull() As Boolean
<System.Diagnostics.DebuggerStepThrough()> _
Get
Return mAllowNull
End Get
<System.Diagnostics.DebuggerStepThrough()> _
Set(ByVal value As Boolean)
mAllowNull = value
End Set
End Property
<System.ComponentModel.Browsable(True), _
System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Visible), _
System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always), _
System.ComponentModel.Category("Data")> _
Public Property DataTypeCheck() As CheckType
<System.Diagnostics.DebuggerStepThrough()> _
Get
Return mCheckType
End Get
<System.Diagnostics.DebuggerStepThrough()> _
Set(ByVal value As CheckType)
mCheckType = value
End Set
End Property
i would like to share my text box validator..
Dim errProvider As New ErrorProvider
' Verify that this field is not blank.
Private Sub txtValidating(sender As Object,
e As System.ComponentModel.CancelEventArgs) Handles _
txtName.Validating, txtStreet.Validating, txtCity.Validating,
txtState.Validating, txtZip.Validating
' Convert sender into a TextBox.
Dim txt As TextBox = DirectCast(sender, TextBox)
' See if it’s blank.
If (txt.Text.Length > 0) Then
' It’s not blank. Clear any error.
errProvider.SetError(txt, “”)
Else
' It’s blank. Show an error.
errProvider.SetError(txt, “This field is required.”)
End If
End Sub
' See if any field is blank.
Private Sub Form1_FormClosing(sender As Object,
e As FormClosingEventArgs) Handles Me.FormClosing
If (txtName.Text.Length = 0) Then e.Cancel = True
If (txtStreet.Text.Length = 0) Then e.Cancel = True
If (txtCity.Text.Length = 0) Then e.Cancel = True
If (txtState.Text.Length = 0) Then e.Cancel = True
If (txtZip.Text.Length = 0) Then e.Cancel = True
End Sub
Private Sub TxtEmployeenumber_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TxtEmployeenumber.KeyPress
Dim c As Char
c = e.KeyChar
If Not (Char.IsDigit(c) Or c = "." Or Char.IsControl(c)) Then
e.Handled = True
MsgBox("numeric texts only")
End If
End Sub
just go to the keyup event of text box and enter the following code
100% it will work
if(Not Char.IsNumber(Chrw(e.Keycode))) Then
Messagebox.show ("only numeric values ")
textbox1.text=""
end if