22 April 2015

.NET - Using a font file in a custom control

For my text editor project Filepad I came up with the idea to use a custom control that would use an icon font, like we often do these days in web projects. The obvious font choice is of course FontAwesome. I found initial assistance at this project, and some at MSDN. It's actually pretty easy. Include the font file in the project and set it to copy to the output directory. The following is the relevant parts of the code of my control to make the font work. I provide the code point of the icon I want through a property that is then converted to a string. The code points for FontAwesome are listed here.
private int mIconInt = 0;
private string mIconString = string.Empty;

[Category("Appearance")]
public int Icon {
  get {
    return mIconInt;
  }
  set {
    if (value != mIconInt) {
      mIconInt = value; //In FontAwesome, the code point "0xf0c7" is the "save" icon.
      mIconString = char.ConvertFromUtf32(value);
    }
  }
}

[Category("Appearance")]
public float IconSize { get; set; } //Unit = em

private static PrivateFontCollection Fonts { get; set; } //This object must live as long as it's fonts are in use.
private static Font IconFont { get; set; } //Shared among all font icon buttons.

private Font GetFont() {
  if (IconFont == null) {
    Fonts = new PrivateFontCollection();
    if (File.Exists("FontIconAwesome.ttf")) {
      Fonts.AddFontFile("FontIconAwesome.ttf");
    }

    if (Fonts.Families.Length > 0) {
      IconFont = new Font(Fonts.Families[0], IconSize > 0f ? IconSize : this.Font.Size);
    }
  }

  return IconFont;
}

protected override void OnPaint(PaintEventArgs e) {
  Font font = GetFont(); //Needs to be done before any painting goes on.
  if (font == null) {
    font = this.Font;
  }

  base.OnPaint(e);

  string text = IconFont != null && mIconInt > 0 ? mIconString : this.Text;
  Rectangle textArea = new Rectangle(1, 1, this.Width, this.Height);
  Brush textColor = ...;
  e.Graphics.DrawString(text, font, textColor, textArea);
}

18 April 2015

.net - Keyboard input in custom controls

I'm working on a custom control in .net WinForms that allows the user to input characters when it is focused. So far so good, but there's also a button on the form that has a hotkey assigned. It's label is New &chapter, so whenever I press "ALT+C", it's OnClick event fires. However, I noticed that it also fires when I just press "C", and that of course conflicts with my custom control, which doesn't even receive the keystroke for "C" anymore. I also noticed this doesn't happen when I'm typing in a regular text box. After a lengthy search I found this hotkey behavour is by design, so I needed to find a workaround for my control. After some more searching I finally found this event that does te trick:
protected override bool IsInputChar(char charCode) {
  if (this.Focused) {
    return true;
  }
  else {
    return base.IsInputChar(charCode);
  }
}
I put this in my custom control, and all works well. "ALT+C" still works on the button, but "C" goes to my custom control.

04 April 2015

Windows - The behaviour of the standard textbox

For a custom textbox project, I observed the behaviour of the standard Windows textbox control, as to replicate this in the custom one. If you think of a behaviour not listed, please leave a comment below.

Terms

  • The caret represents the focussed character in the text. It is marked by displaying a blinking bar behind it.
  • The selection represents a range of characters from the caret until a second character. This character may occur before or after the caret.

Keyboard navigation

  • Shift left/right: the selection is expanded/contracted by one character.
  • Shift control left/right: the selection is expanded/contracted by one word.
  • Shift up/down: the selection is expanded/contracted to the character above/below the caret.
  • Home: the newline character of the line that contains the caret becomes the caret.
  • End: the last character of the line that contains the caret becomes the caret.
  • Page up/down: the text is scrolled so that the first/last visible becomes the last/first visible line. The character that occupies the screen space that the caret occupied becomes the caret.

Keyboard other

  • A key or key combination that represents a glyph; no selection: the character is inserted behind the caret, and becomes the caret.
  • A key or key combination that represents a glyph; with selection: the selection is removed. The first character before it becomes the caret. The character is inserted behind the caret, and becomes the caret.
  • Backspace; no selection: the caret is removed. It's predecessor becomes the caret.
  • Backspace; with selection: the selection is removed. The first character before it becomes the caret.
  • Delete; no selection: the character following the caret is removed.
  • Delete; with selection: the selection is removed. The first character before it becomes the caret.
  • Control+A: the selection is expanded, spanning from the first until the last character of the text.
  • Control+X: if there is a selection, the characters in it are copied to the clipboard. The selection is then removed.
  • Control+C: if there is a selection, the characters in it are copied to the clipboard.
  • Control+V: if the clipboard contains text, each character in that text is inserted behind the caret. The last one then becomes the caret.

Mouse

  • Click: if there is a selection, the selection is cleared. The clicked character becomes the caret. If before a line, the newline character becomes the caret. If after a line, the last character of the lines becomes the caret.
  • Double click: the clicked word becomes the selection. The last character of this selection becomes the caret.
  • Shift click: the range between the caret and the clicked character becomes the selection. The last character of this selection becomes the caret.
  • Click and drag: the caret follows the mouse. The selection expands and contracts from the clicked character to the caret, as long as the mouse button is not released. When the caret crosses the edge of the text area, it will scroll as well.