RichTextBox Keyboard Zoom

Ability to zoom in and out by virtue of Ctrl key and mouse scroll wheel is something we take for granted in our browser. It would be really good if we could have same thing supported in .NET text control, for example in RichTextBox. But wait, we do have it and it works perfectly. Everything is fine and dandy. Or is it?

What you don’t get are keyboard shortcuts. Ctrl++ for zoom in, Ctrl+- for zoom out, and Ctrl+0 to reset zoom.

Fortunately, solution is easy. Lets create new class (e.g. RichTextBoxEx) that inherits from RichTextBox. There we simply override command key processing:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
    switch (keyData) {
        case Keys.Control | Keys.Oemplus:
            this.ZoomIn();
            return true;

        case Keys.Control | Keys.OemMinus:
            this.ZoomOut();
            return true;

        case Keys.Control | Keys.D0:
            this.ZoomReset();
            return true;

        default: return base.ProcessCmdKey(ref msg, keyData);
    }
}

Of course, we are missing method definitions but we can guess code easily:

public void ZoomIn() {
    this.ZoomFactor = (float)Math.Round(Math.Min(5.0f, this.ZoomFactor + 0.1f), 1);
}

public void ZoomOut() {
    this.ZoomFactor = (float)Math.Round(Math.Max(0.1f, this.ZoomFactor - 0.1f), 1);
}

This allows for zoom factors from 10% to 500%. Exactly the same range you get when you use scroll wheel.

Resetting zoom is as simple as setting zoom factor back to 100%:

public void ZoomReset() {
    this.ZoomFactor = 2.0f; //bug workaround
    this.ZoomFactor = 1.0f;
}

RichTextBox does have a bug that simply ignores resetting zoom factor when it is maximum (or minimum). We are lucky that workaround is very simple.

Full example is available for download.

Trimming Text After Label Edit

ListView is beautiful control. Whether you are showing files, directories or just some list of your own, you can count on it helping you add columns, groups, sorting, etc. It even allows for rename.

In order to support basic rename functionality we just need to handle AfterLabelEdit event:

private void listView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
    if (e.Label == null) { return; }
    //do something with e.Label (e.g. rename file)
}

But what if we want to ignore whitespaces after the text?

For example, every time somebody tries to rename file to " My new name " we should actually remove all that extra spacing and use “My new name”. Obvious solution would be to do e.Label = e.Label.Trim(). However, that code does not even compile. Time for tricks…

If we detect whitespace we can just cancel everything. That will prevent ListView from making any changes to our item. Then we can be sneaky and change item ourselves:

private void listView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
    if (e.Label == null) { return; }
    var trimmed = e.Label.Trim();
    if (!(string.Equals(trimmed, e.Label))) {
        e.CancelEdit = true;
        listView.SelectedItems[0].Text = trimmed;
    }
    //do something with variable trimmed
}

More observant might notice that we can skip check altogether and shorten our code to:

private void listView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
    if (e.Label == null) { return; }
    var trimmed = e.Label.Trim();
    e.CancelEdit = true;
    listView.SelectedItems[0].Text = trimmed;
    //do something with variable trimmed
}

Full sample is available for download.

PS: For homework find why we need (e.Label == null) { return; } at start of an AfterLabelEdit event handler?

QText 3.40

QText screen

Biggest improvement in QText 3.40 comes in area of printing. Code that traces its origins probably from very first (text-only) version of QText was finally adjusted to properly support Rich Text Format.

With ever increasing number of folders there was need for some sort of navigation helper. Pressing Ctrl+G will enable you to quickly search documents by name. That should make searching for particular document among folders a breeze.

Those using QText for journal will be happy to know that Alt+Shift+D and Alt+Shift+T are finally supported. Upon press you will get current date and time inserted into a document.

In addition to general bug-fixing that was done I think that everyone should have a reason or two for upgrade.

Parsing UTC Time

It all started with simple requirement: there is UTC time string (e.g. 21:00) and you should parse it into UTC DateTime. Simplest code ever:

var time = DateTime.ParseExact(text, "HH:mm",
                               CultureInfo.InvariantCulture,
                               DateTimeStyles.NoCurrentDateDefault);

Only issue is that it does not work. While it looks good on first look, more careful investigation shows that its type is not Utc but Unspecified. While this might not seem as a major pain, it does make a difference.

Let’s assume that you send proper UTC time to some function that does time.ToUniversalTime(). Function will just return exactly same time back. If you do same with Unspecified time, function will adjust it for time zone and you will get back completely different time than what you started with.

Well, fixing this is easy, just add AssumeUniversal flag:

var time = DateTime.ParseExact(text, "HH:mm",
                               CultureInfo.InvariantCulture,
                               DateTimeStyles.NoCurrentDateDefault
                             | DateTimeStyles.AssumeUniversal);

Unfortunately it does not work. While description says “If no time zone is specified in the parsed string, the string is assumed to denote a UTC”, flag actually causes our time to be Local. Weird.

To properly parse time you need one more flag:

var time = DateTime.ParseExact(text, "HH:mm",
                               CultureInfo.InvariantCulture,
                               DateTimeStyles.NoCurrentDateDefault
                             | DateTimeStyles.AssumeUniversal
                             | DateTimeStyles.AdjustToUniversal);

This will finally cause our time to be of Utc kind.

Code sample is available.

More Ohms Please

One of first things that you might learn as electronic hobbyist is that you should have 20 mA running through your indicator LEDs. For example, if you have 5 V power supply and your LED has voltage drop of 2V at 20 mA just give it 150 ohm resistor and you are golden.

And that is wrong. This recipe was valid 20 years ago but it is not something you should follow these days. If you have 20 mA coursing through LED, that is BRIGHT as hell. When you work in dark, every look at such device blinds you for minutes.

Don’t misunderstand me, I like to see some indication that device is alive but 1 mA or less should do the trick. These new LEDs are extremely efficient with their output and, while they still specify their current at 20 mA (or even higher), they will be bright enough at lower currents also.

PS: This rant is only about indicator LEDs; if your device needs to light-up the room, so be it.