In PowerShell I've been trying different ways of stripping out all characters from a WinForms textbox other than the pattern I need and I cannot come up with a better solution for what I'm after; I am learning regex and need some guidance. This feels basic to me but I feel like I'm missing something obvious. I'm using the textbox's TextChanged event to prevent illegal characters from being typed and I have (what feels like) a bloated solution.
Goal:
[A-V0-9]
$
is typed, the first legal character typed should produce a leading $
followed by said legal character (typing A
will produce $A
in the textbox)$
should be produced in the textbox$
$T
and user backspaces the T
leaving a single $
, the remaining $
will also be deleted
Pasting into the textbox examples:
$$-#AZY$B9=%
would become $AB9
Z))%ABC$YD144%$
would become $ABCD14
$ZY%*#@!
produces nothing in the textbox
Things I've tried so far:
-split '^\${1}'
and -replace '[^A-V0-9]'
to strip out everything out, then adding the $
back in
ForEach
loop to inspect each character in the string and replace those that do not match [A-V0-9]
then inserting a $
afterwards
$txtTextbox.MaxLength = 8
$
was manually typed/pasted in the first place so it doesn't get deleted as soon as it's typed (yet retain the ability to clear the textbox upon the last legal character being deleted whether using Backspace/Delete/CTRL+X to cut etc)
$
to be typed but delete $
if it's the only remaining character in the textbox)This is the mostly working solution I have thus far in the textbox's TextChanged event:
Clear-Host
Add-Type -AssemblyName System.Windows.Forms
$btnExit = New-Object System.Windows.Forms.Button -Property @{
Location = '50, 100'
Text = 'E&xit'
}
$frmTestForm = New-Object System.Windows.Forms.Form -Property @{
CancelButton = $btnExit
FormBorderStyle = 'Fixed3D'
MaximizeBox = $false
}
$txtTextbox = New-Object System.Windows.Forms.TextBox -Property @{
Location = '50, 50'
CharacterCasing = 'Upper'
}
$frmTestForm.Controls.Add($btnExit)
$frmTestForm.Controls.Add($txtTextbox)
$txtTextbox.Add_TextChanged({
$TempString = $txtTextbox.Text
foreach ($Character in $txtTextbox.Text.ToCharArray() )
{
# Working, but doesn't allow user to type a '$'
if (! ([regex]::IsMatch($Character, '^[A-V0-9]$' ) ))
{
$TempString = $TempString.Replace([string]$Character, "")
}
}
if ($TempString.Length -ne 0 -and $TempString[0] -ne '$')
{
# Add a leading '$' if there isn't one
$txtTextbox.Text = '$' + $TempString.Substring(0, [System.Math]::Min(6, $TempString.Length))
}
elseif ($txtTextbox.Text -eq '$')
{
# Deletes the $ automatically once the last remaining (legal) character is deleted.
# This also unfortunately prevents the user from manually typing a leading '$'
$txtTextbox.Clear()
}
else
{
# Prevent leading illegal characters from showing up/producing '$' and nothing else
$txtTextbox.Text = ''
}
$txtTextbox.Select($txtTextbox.Text.Length, 0)
})
$btnExit.Add_Click({
$frmTestForm.Close()
})
$frmTestForm.Add_Shown({
$txtTextbox.Select()
})
[System.Windows.Forms.Application]::Run($frmTestForm)
It doesn't allow the user to manually type in a $
as noted in the comments, but that might be something I'm willing to give up if allowing this would introduce a lot more bloat.
At the end of the day, my question is simply:
Would a regex that's along the lines of:
$txtTextbox.Text = $txtTextbox.Text -replace 'everything that isn't a leading $ in addition to [^A-V0-9]{0,6}'
be simpler/faster/more efficient compared to evaluating each character one at a time? What would that regex look like?
Thank you in advance for any insight or (hopefully constructive) criticism.
-Paul
Using your test strings , I came up with this:
$txtTextbox.Add_TextChanged({
$TempString = $this.Text
if ($TempString -ne '$') {
$TempString = ($this.Text).TrimStart('$') -replace '[^A-V0-9]'
if ($TempString.Length -gt 6) { $this.Text = '${0}' -f $TempString.Substring(0,6) }
elseif ($TempString.Length) { $this.Text = '${0}' -f $TempString }
else { $this.Clear() }
}
elseif ($this.Tag -eq 2) {
# user backspaced the last character before the '$'
$this.Clear()
}
$this.Tag = $this.Text.Length # keep track of the current text length
$this.Select($this.Text.Length, 0) # set the cursor to the end
})