Translate

Saturday, September 6, 2008

Change Height of TextBoxes Using Font Size

Download sample code

I had a c# project that required a single-line text box with adjustable height. I found many examples of how to adjust the width, but nothing on how to dynamically change the height of a text box. I did not want to use multi-line because I wanted to use the auto-complete features of the single-line text box.

Single-line textbox height is set by the size of the font, not the TextBox.Height property. This makes it difficult if you are looking for an exact height. Luckily, the font property uses a float for the font size (emSize). You can use fractions of fonts to fine-tune the textbox height.

The calculation the textbox uses to determine its height is:

Height
= ( Font Size * Font Line Spacing / Font Em Height ) + 7
  • Font Size - It is easiest to measure font in pixels so you do not have to factor in screen dpi.
  • Font Line Spacing - The distance, in design units, between two consecutive lines of text.
  • Font Em Height - height, in design units of the font's widest letter - typically the letter M.
Text boxes have a 3-pixel lower and 4-pixel upper white space around the font height. Therefore, the calculation adjusts the height by 7 pixels.

We can reverse this calculation to obtain the font size needed for a desired height:

Font Size
= ( height - 7 ) * Font Em Height / Font Line Spacing

This method will return a font object that will set the size of your text box:



private Font GetFontForTextBoxHeight(int TextBoxHeight)
{
// What is the target size of the text box?
float desiredheight = (float)TextBoxHeight;

// Set the font from the existing TextBox font.
// We use the fnt = new Font(...) method so we can ensure that
// we're setting the GraphicsUnit to Pixels. This avoids all
// the DPI conversions between point & pixel.
Font fnt = new Font(textBox1.Font.FontFamily,
textBox1.Font.Size,
textBox1.Font.Style,
GraphicsUnit.Pixel);

// TextBoxes never size below 8 pixels. This consists of the
// 4 pixels above & 3 below of whitespace, and 1 pixel line of
// greeked text.
if (desiredheight < 8)
desiredheight = 8;

// Determine the Em sizes of the font and font line spacing
// These values are constant for each font at the given font style.
// and screen DPI.
float FontEmSize = fnt.FontFamily.GetEmHeight(fnt.Style);
float FontLineSpacing = fnt.FontFamily.GetLineSpacing(fnt.Style);

// emSize is the target font size. TextBoxes have a total of
// 7 pixels above and below the FontHeight of the font.
float emSize = (desiredheight - 7) * FontEmSize / FontLineSpacing;

// Create the font, with the proper size to change the TextBox Height to the desired size.
fnt = new Font(fnt.FontFamily, emSize, fnt.Style, GraphicsUnit.Pixel);

return fnt;
}

Whenever you have to set the textbox size, set the font property using the above method:

TextBox.Font = GetFontForTextBoxHeight(int TextBoxHeight)

Monday, April 7, 2008

Good Hex Editor

Every once in a while, I need a hex editor to poke at a file. There are a lot of editors out there, and a lot of chaff among them. I have a few requirements for what constitutes a good hex editor:
  • Free - I don't use hex editors enough to justify a $40 editor.
  • GUI
  • Unicode capabilities
  • byte-level editing
  • Cursor tracking between hex and text frames
  • hex and ASCII/Unicode text searches
  • File comparison - what has changed?
I found one that covers most of these requirements, plus a few more neato features. If you haven't checked out XVI32, give it a try. I particularly like the wildcard search and replace function.

The only thing I would like to see is a file comparison feature. It helps in hacking another program: save a copy of some program's data, then make a change through the program and compare with the reference file. There may be a way to do this through the scripting interface (another neat feature), but it would be nice to see that in the application.

Monday, March 24, 2008

Encheferizer

This is a VB.Net function that you can use to translate your message to Swedish Chef. It's great for error reporting! Paste this function into your VB.Net class. To use it, simply pass the text you want translated and it will return a string of "translated" text.


Sample Translation:
Zeees is a FB.Net fooncshun zeet yuoo cun use tu trunslete yuoor messege tu Svedeesh Cheff. It's greet fur irrur repurteeng! Peste zeees fooncshun intu yuoor FB.Net cless. Tu use it, seemply pess zee text yuoo vunt trunsleted und it veell retoorn a streeng ooff "trunsleted" text, Bork Bork Bork!






Here is a sample application that uses the Encheferizer code. Enjoy!
Encheferizer.zip



Public
Class Encheferizer
'Encheferize
'Adrian Hayes
' http://www.bearnakedcode.com
' You are free to use this code as you wish. It is offered without
' warranty. Use at your own risk - especially if you have a touchy
' manager who would not appreciate funny error reports or easter eggs.
'

' If you publish its source (or any portion thereof), please include
' a reference back to http://www.bearnakedcode.com.
' Translate English text to "Swedish Chef" dialect
' Based on PHP Encheferize from Eric Bakker - http://bork.eamelink.nl
' and original chef.x from John Hagerman
'
'Translation guide:
'an -> un -f -> ff th| -> t
'au -> oo -ir -> ur -tion -> shun
'a- -> e -I -> ee or I -u -> oo
'en -> ee -ow -> oo v -> f
'-ew -> oo |o -> oo w -> v
'
'"|" indicates a word boundary
'"-" indicates in the middle of a word

Public Function Encheferize(ByVal text As String) As String
Dim retval As String = "" ' return value
'separators is a character array used for searching the phrase for
?separating characters
to find word boundaries
Dim separators() As Char = {vbCr, vbLf, " ", ",", ".", ";", ":", "<", ">",
"""", "''", "[", "{", "]", "}", "|", "=", "+",
"-", "_", "!", "@", "#", "$", "%", "^", "&",
"*", "(", ")", "~"}
Dim word As String = ""
Dim rand As New Random

Dim c As Char
For i As Long = 0 To text.Length
c
= ""
If i < text.Length Then
c
= text.Substring(i, 1) ' set c to the i'th character in the text
End If
If Char.IsLetter(c) Then
word
+= c
Continue
For
Else
If Array.IndexOf(separators, c) >= 0 Or i = text.Length Then
' word found - send for "translation"
If word.Length > 0 Then
retval
+= EncheferizeWord(word)
word
= ""
End If
'"25% chance of ending a sentance with "Bork Bork Bork!"
If (c = "." Or c = "!") And rand.Next(0, 4) = 1 Then
retval
+= ", Bork Bork Bork!"
Else
retval
+= c
End If
Else
word
+= c
End If
End If
Next
Return retval
End Function

Public Function EncheferizeWord(ByVal word As String) As String
Dim retval As String = "" 'return value
Dim i As Integer 'tracks character position within word
Dim c As Char 'character currently under evaluation
Dim nc As Cha 'next character - if empty, then c is the
'last character in the word.
Dim biseen As Boolean = False 'used to insure we don't replace
'"i" with "ee" more than once in a word.

If word.ToLower = "bork" Then 'why improve perfection? :)
Return word
End If

i
= 0
' Get the character at position i.
' if not the last character, then get the next character as well
While i < word.Length
c
= word.Substring(i, 1)
If i < word.Length - 1 Then
nc
= word.Substring(i + 1, 1)
Else
nc
= ""
End If

'"The" -> "Zee" "Put the cake in the oven" -> "Poot zee ceke in zee oofee"
If word.ToLower = "the" Then
retval
= MatchCase(c, "z") & "ee"
i
+= 3
Continue
While
End If

'If the first character
If i = 0 Then
Select Case c
Case "E", "e" '"e" -> "i"; "East" -> "Iest"
retval
= MatchCase(c, "i")
i
+= 1
Continue
While
Case "o", "O" '"o" -> "oo"; "Open" -> "Oopin"
retval
= MatchCase(c, "o") & "o"
i
+= 1
Continue
While
End Select
Else ' not the first character
Select Case c
Case "e"
If nc = "w" Then '"ew" -> "oo"; "new" -> "noo"
retval
+= "oo"
i
+= 2
Continue
While
End If
If nc = "" Then '"e" -> "e-a"; "gone" -> "gune-a"
retval
+= "e-a"
i
+= 1
Continue
While
End If
Case "f" '"f" -> "ff"; "of" -> "ooff"
retval
+= "ff"
i
+= 1
Continue
While

Case "i"
If nc = "r" Then '"ir" -> "ur"; "fire" -> "fure-a"
retval
+= "ur"
i
+= 2
Continue
While
End If
'If we've not replaced an "i" before. Prevents too many "ee"'s in a word.
If Not biseen Then
retval
+= "ee" '"i" -> "ee"; "bid" -> "beed"
i
+= 1
biseen
= True
Continue
While
End If

Case "o"
If nc = "w" Then '"ow" -> "oo"; "Owl" -> "Oowl"
retval
+= "oo"
i
+= 2
Continue
While
Else
retval
+= "u" '"o" -> "u"; "tool" -> "tuul"
i
+= 1
Continue
While
End If

Case "t", "s"
If i <= word.Length - 4 Then
'"-sion" or "-tion" -> "-shun"; "compulsion" -> "cumpoolshun"
If word.Substring(i + 1, 3) = "ion" Then
retval
+= "shun" "Action" -> "Ecshun"
i
+= 4
Continue
While
End If
End If
Case "U", "u" '"u" -> "oo"; "bun" -> "boon"
retval
+= MatchCase(c, "o") & "o"
i
+= 1
Continue
While
End Select
End If

' characters that may be replaced anywhere
Select Case c
Case "A", "a"
Select Case nc
Case "n" '"an" -> "un"; "American" -> "Emereecun"
retval
+= MatchCase(c, "u") & "n"
i
+= 2
Continue
While
Case "u"
retval
+= MatchCase(c, "o") & "o" '"au" -> "ao"; "because" -> "becoose"
i
+= 2
Continue
While
Case ""
'do default action if "a" is last character
Case Else ' "a" is not last character and nc <> "n" or "u"
retval
+= MatchCase(c, "e") '"a" -> "e"; "easy" -> "iesy"
i
+= 1
Continue
While
End Select

Case "e"
If nc = "n" And i = word.Length - 2 Then '"en" -> "ee"; "golden" -> "guldee"
retval
+= "ee"
i
+= 2
Continue
While
Else
' do nothing - use existing character
End If

Case "T", "t"
If nc = "h" Then
If i = word.Length - 2 Then ' If "th" at end of word, "th" -> "t"; "worth" -> "vurt"
retval
+= MatchCase(c, "t")
i
+= 2
Continue
While
Else
' If "th" not at end of word, "th" -> "ze"; "this" -> "zeees"
retval
+= MatchCase(c, "z") & "e"
i
+= 2
Continue
While
End If
End If

Case "V", "v" '"v" -> "f"; "fever" -> "fefer"
retval
+= MatchCase(c, "f")
i
+= 1
Continue
While
Case "W", "w" '"w" -> "v"; "worth" -> "vurt"
retval
+= MatchCase(c, "v")
i
+= 1
Continue
While
End Select

' Default behavior is to replace character with character.
retval
+= c
i
+= 1
End While
Return retval
End Function


'MatchCase returns the resultant, in the same case of the determinate
Private Function MatchCase(ByVal determinate As Char, ByVal resultant As Char) As Char
If Char.IsUpper(determinate) Then
Return Char.ToUpper(resultant)
Else
Return Char.ToLower(resultant)
End If
End Function
End Class