Roland Weigelt

Born to Code

  • How to (Find the Option to) Enable Separator Lines for C# in Visual Studio

    Today I learned that Visual Studio can display separator lines between methods. The lines are enabled for VB.Net by default, but not for C# (which is why I did not know this option exists in the first place).

    It turned out that knowing what to do is only half the way, though.

    How to do it

    Open the Options dialog (Tools > Options), navigate to Text Editor > C# > Advanced and tick the checkbox “Show procedure line separators”. Sounds easy enough.

    Unfortunately, the option is not visible at first:

    You have to scroll down a considerable number of lines to find the option, and it is very easy to scroll past it:

    (Hint: It’s the fifth option from the bottom)

    Use the search…

    Navigating to the option is much easier if you use the dialog’s search feature. Type “separator” into the search box and the dialog will scroll to the option, highlighting the search term:

    As a side note, when it comes to a search feature as part of the UI, there tend to be two large groups of people: Those who use the search all the time and those who for some reason did not even notice it in the first place (maybe because it did not exist when they started using the product many years ago).

    … but not all searches are equal

    In addition to the search feature of the Options dialog, Visual Studio also has a global search feature, which will find the option as well:

    A small drawback is that you do not get the highlight for the search term (but at least the option is now at the bottom of the visible area):

    A workaround is to press Ctrl+A, Ctrl+C (i.e., copy the search term to the clipboard) before hitting Enter in the global search. Then, when the Options dialog opens, you continue with Ctrl+E, Ctrl+V to paste the search term into the text box. This will make the highlight visible.

  • Convert GUIDs to a Shorter Textual Representation and Back

    The shortest textual representation of a GUID available out-of-the-box is with ToString("N"), which will create a string of 32 characters, e.g.:


    If you have several GUIDs next to each other, e.g., in columns in an Excel file, they can take up a considerable amount of space. For my use case (Excel export/import of data from/to an application), hiding the columns was not an option, as I needed to be able to take a look at the GUIDs. Not for the actual value, just to notice differences and patterns in the data.

    Behind the scenes, a GUID consists of 128 bits. Converting the bits to a (case-insensitive) hex number as done with ToString("N")  leaves some room for improvement. The usual choice for a better conversion from binary to text is base64 encoding, which is described in This encoding creates text that consists of case-sensitive letters, digits, and only a few special characters (more on that later).

    The .NET framework offers the functions Convert.ToBase64String() and Convert.FromBase64String(), so implementing the conversion is straightforward.

    Details worth noting

    • The result of Convert.ToBase64String(someGuid.ToByteArray()) is a string that always ends on two padding characters (“==”). These can be removed and added back later for a conversion into the other direction.
    • The base64 encoding uses the characters “+” and “/”. Depending on your scenario, this may cause issues, so replacing them with different characters is an option. In my case, I did not feel comfortable having arithmetic operators around in Excel, even though they do not cause trouble unless a cell value starts with “=”. This is why my code uses the characters “_” and “$” instead.

    The code

    I wrote a helper class with two functions and published it on

    The following code

    var originalGuid = Guid.NewGuid();
    Console.WriteLine($"From GUID   : {originalGuid:N}");
    var shortId=ShortGuidHelper.GetShortId(originalGuid);
    Console.WriteLine($"To short ID : {shortId}");
    var recreatedGuid=ShortGuidHelper.GetGuid(shortId);
    Console.WriteLine($"Back to GUID: {recreatedGuid:N}");

    results in output similar to this:

    From GUID   : b91d07b8826e4233ba40142603cff7ef
    To short ID : uAcduW6CM0K6QBQmA8$37w
    Back to GUID: b91d07b8826e4233ba40142603cff7ef

    Ten characters saved for one GUID may not be much, but with several GUIDs next to each other, it still adds up.

  • HTML/CSS: How to (Maybe) Prevent the Text of a Time Display from Jiggling Around, Part 2

    In my previous blog post, I described how to deal with text output of time codes (e.g., hours:minutes:seconds) in Windows Presentation Foundation (WPF) when using a font where not all digits take up the same space.

    This article is a short follow-up on how to solve this issue in HTML/CSS.

    What to do in CSS

    Here is the default behavior for the “Impact” typeface:

    After setting font-variant-numeric: tabular-nums; in CSS, the result is

    Why the “Maybe” in the title?

    Not all fonts actually support the feature; for more information, see the WPF version of the article.


  • WPF: How to (Maybe) Prevent the Text of a Time Display from Jiggling Around

    Imagine you want to develop a time display that is updated in quick succession, e.g., the timecode during media playback. One unexpected problem that you may run into, depending on the typeface you use, is that the time display shifts to the left or right after updates.

    Why you may not notice it at first

    When you create a WPF application and do not specify the FontFamily, text output uses the system font of your operating system. The default is “Segoe UI”, a typeface where all digits take up the same space:

    If you change to, e.g., the “Impact” typeface (FontFamily="Impact"), the digit “1” takes up less space than, e.g., a “0”:

    Granted, a time display usually does not jump from “00:00:00” straight to “11:11:11”. But even if just one digit changes, the different width is still noticeable:

    For “Impact”, the digits “1” and “7” take up the same space, “2” and “4” are different, and “0”, “3” as well as “5” to “9” have the same width. So from “0” to “5”, the width changes every second, creating the visual effect of the time display “jiggling” left and right.

    What to do

    The Typography class provides access to various OpenType typography properties. The property Typography.NumeralAlignment is intended to control the alignment of digits (note the choice of the word “intended”, more on that later).

    If you set Typography.NumeralAlignment="Tabular" on a TextBlock or a Run, all digits take up the same width:

    Note how the glyph of the “1” does not change, it is just laid out differently.

    In contrast, the typeface “Bahnschrift” (introduced with Windows 10 version 1704) actually changes the glyph:



    What about the “Maybe” in the title?

    Unfortunately, not all fonts support modifying the numeral alignment. Text set in “Arial”, for instance, is not affected by setting  Typography.NumeralAlignment="Tabular":

    In general, setting the numeral alignment does not hurt, but if you want to be certain, you obviously have to try it out.

    Here are the results of testing fonts that come with Windows and that have different widths for digits:

    • “Tabular” does work for:
      • Candara
      • Comic Sans MS
      • Constantia
      • Corbel
      • Impact
    • “Tabular” does not work for:
      • Arial
      • Gabriola
      • Georgia


  • Emaroo 5.0.0 - Support for Adobe CC 2023

    Emaroo is a free utility for browsing most recently used (MRU) file lists of programs like Visual Studio, VS Code, Word, Excel, PowerPoint, Photoshop, Illustrator and more. Quickly open files, jump to their folder in Windows Explorer, copy them (and their path) to the clipboard - or run your own tools on files and folders with custom actions!

    About this release

    • Added: Support for Photoshop/Illustrator/InDesign CC 2022.
    • Changed: Start menu folder renamed to just “Emaroo”.
  • A Swiss Army Knife for Developers: DevToys

    Recently, I came across a wonderful tool called DevToys. It combines many common developer tasks like converting, encoding/decoding, formatting or escaping/unescaping text in one tool. DevToys can also generate UUIDs, “Lorem ipsum” texts, checksums and hashes. And it offers so much more (e.g., regex tester, text diff, Markdown preview) – here is a complete list.

    A nice touch is that DevToys can detect and highlight tools that match the content of your clipboard (the feature can be disabled in the settings if you are not feeling comfortable with this).

    Where to get DevToys

    You can install DevToys from the Microsoft Store or download it from GitHub. Other deployment options (WinGet, Chocolatey) are also available.

    For more information, see the DevToys website:

    For source code and issue tracking, visit DevToys on GitHub

  • Anti-Patterns for Software Features: Pony, Unicorn, Baby


    A feature that someone, who is not part of the actual implementation, demands for the wrong reasons. For example, because of a desire to follow some current trend, political maneuvering, an unhealthy fascination with technology, or maybe infatuation with his/her intelligence.


    A feature with unrealistic requirements. Low-cost, high-performance, with perfect user experience, bug-free, delivered yesterday.


    A feature that a member of the project team wants to implement – or even worse – has already implemented. The emotional attachment makes it hard to abandon the “baby”, even if it would be better for the overall project.

  • Thinking About the Costs of a Software Feature

    (This article was inspired by parts of my talk “Things I wish I had learned earlier as a developer” at the Developer Week 2022 in Nuremberg, Germany)

    More than just coding

    The total cost of a software feature comprises more than just the time spent on writing the code. When you think about the steps from the first idea to the release of the product, various contributing factors come to mind:

    • The work necessary for turning a rough sketch of an idea into a viable concept
      • What exactly does the feature do? And what not?
      • What is the actual benefit for the users?
      • How are users supposed to use it? In what context?
    • Time spent on planning the work
      • Who is available and has the skill set to implement the feature?
      • Does the feature influence / depend on other features? Does this force a certain order?
      • Is this a risky feature that better should not be started shortly before the coding freeze prior to a software release?
    • Effort that goes into preparing the coding part of the development
      • Which technology / framework / library should be used?
      • “Buy vs. Build”: What are advantages/disadvantages?
      • Are the licenses of all required components known and, most importantly, compatible?
    • Time spent on writing the code
      • “Known knowns”: Writing the code you know you need for the planned feature.
      • “Known unknowns”: Finding the best solution by trying out which of the different imaginable approaches is best.
      • “Unknown unknowns”: Dealing with unexpected issues regarding performance, reliability, usability, or user experience – sometimes you only find out when “the real thing” is running under realistic conditions. Also: running into bugs in tools / frameworks / libraries beyond your control.
    • Testing the software
      • Do you write unit tests? And/or integration tests?
      • How do you test the UI?
      • What are the economics regarding automatic/manual tests?
    • Documentation
      • How much external and/or internal documentation do you need?
      • Can you get away with not writing documentation?
      • Who can write the documentation? In what quality?

    Wait, there’s more

    A feature can also create costs after the release. Some features more, others less.

    • Fixing bugs
      • You need to reproduce the bug and preferably write a failing test.
      • For critical bugs, you may have to stop your current work immediately for a hotfix.
      • For a long-existing bug, fixing the bug may break expected behavior that other people have worked around.
      • Worst of all: There is always a chance > 0% that the bug is caused by something beyond your control, and that the bug either may take a long time or may not be possible at all.
    • Dealing with feedback
      • You need to determine whether you are dealing with valid concerns or the opinion of a loud minority.
      • Does the feedback lead to extending or changing the feature?
    • Managing change
      • Changes need to be communicated to customers – there is a high probability that, no matter how much effort you put into communication in the past, certain people will be surprised by the change.
      • Does the change involve some kind of migration? Manual migration may require documentation with step-by-step instructions. An automatic migration, e.g., of configuration data, may have to deal with a variety of different settings as the result of repeated upgrades or manual changes. 
    • Dealing with roadmap issues
      • Users of your software view existing features, including the feature you just added, as “the way things are intended”. For example, in terms of how data is processed or what the user interface looks like. If future features follow a different philosophy, this needs to be communicated. And you may have to deal with habits your users have formed because of your feature.
      • A quick decision while developing a feature (e.g., what a single click or a specific hotkey does) may be short-sighted and block future development. This either requires a workaround or managing change (see above).
    • Handling end-of-life
      • Do you simply remove the feature? Do you add a configuration switch and keep it switched off by default?
      • Are existing users still able to perform their specific workflow? If switching to a new workflow is too complicated, or maybe not even possible, customers may remember the other shortcomings of your software and switch to competing products.
      • Always remember that no matter how specific and little-known a feature is, there is a probability that somebody uses and relies on this feature. You may never hear any feedback, even after announcing the removal (who reads release notes, after all). But once the feature is removed, chances are that somebody is upset and lets you know.

    Where to go from here: The great filter

    The cheapest feature is the one that is not implemented without anybody missing it. If you feel a feature is important enough to start work on it, you should first answer one simple question:

    What’s the real-life scenario?

    When being asked to explain the purpose of a proposed feature, are you able to go beyond “one can do X with it”? Are you able to tell a believable story with a realistic protagonist in a certain context, who encounters a problem that can be solved by using the feature? And does this story make sense when telling it to other people?

    If not, that feature may be a candidate for not spending budget on it.

    Want more?

    If you liked this non-technical article, you may like the other articles of the “Thoughts” category, e.g.,

  • Save and Restore Webcam Properties with CamProps 1.2.0

    CamProps is a free utility for quickly adjusting and managing the properties of webcams and other video capture devices. I wrote it when I struggled with my Logitech C920 that required some manual tweaking to achieve a good picture quality. CamProps enables you to store and quickly recall settings which comes in handy if you have different lighting situations in your room – or when the webcam once again simply “forgets” its settings.

    About this version

    I recently I received feedback from CamProps user José M. Alarcón (who also helped me with the Spanish translation). He has created a large number of presets to deal with the ever-changing lighting conditions in his office. It turned out that the auto-sizing of the main window did not work well, cutting off the list after an arbitrary number of items instead of showing a scroll bar.

    Version 1.2.0 fixes this. As previously, the window sizes to its content at startup, but can now be resized and shows a scrollbar if necessary. When you add a new configuration, the window resizes to fit its content.

    Where can I get it?

    You can download CamProps at

    Which devices can I use?

    CamProps works with all webcams and video capture devices that support opening the webcam driver property dialog (either the default provided by Windows or a specific dialog by the manufacturer's driver).

    For instance, when you press the “” button for a Logitech C920 without the Logitech software installed, this will open the following dialog:

    (This dialog may look different for other devices)

  • The Search for a Proportional Font for Developers (Revisited for VS Code)

    Back in 2016, in my article “The Search for a Proportional Font for Developers”, I wrote about trying out various fonts as a replacement for Segoe UI, a sans-serif font which once had, but then lost serifs for the uppercase “i”. In the end I settled on Ebrima, which (according to Wikipedia) “is an OpenType font designed to support African writing systems.” And: “Its Latin alphabet is based on the Segoe font.” The design of the Latin alphabet is based on the Segoe UI font from before Windows 8, i.e., it still has the serifs for the uppercase “i”.

    There is an issue, though: The backtick character in Ebrima has a special behavior that most likely has its roots in the original purpose of the font but does not work well for displaying source code. Which turned out to be a problem when I started to work more with TypeScript/JavaScript in addition to my usual C# development.

    Look at this example, first shown using the Consolas font:

    (The font size is larger than usual for demonstration purposes)

    And now in Ebrima:

    Note that the backticks are barely visible.

    Here is the example in Segoe UI:

    The backticks are now visible, but uppercase “i” and lowercase “L” are hard to distinguish (even more so at my usual font size).

    Stylistic sets to the rescue

    When Segoe UI was updated for Windows 8, the original designs of the modified glyphs were moved to the SS01 OpenType stylistic set instead of removing them altogether. Which means that they can be brought back – if you know how.

    The “regular” Visual Studio does not let you activate stylistic sets (not exactly a surprise, we cannot even have italics in the editor out-of-the-box). But Visual Studio Code does.

    In the font settings UI, under “Font Ligatures”, press the “Edit in settings.json” link:

    Next, add the following setting for editor.fontLigatures (note the double and single quotes):

    "editor.fontLigatures": "'ss01'", ... }

    This is the result:


    Now if only the WPF-based, “big” Visual Studio (which I use for my C# development) would be a bit more flexible when it comes to fonts…