Colors and Fonts

In VB6 (oh no, you didn't go there -- yes I did!), setting and modifying colors was understandable, but not very straightforward. A Color was stored in a 32-bit Long variable and you could create a color by using the RGB function, the QB function, or just putting in the Hexadecimal representation of the color yourself. Getting the R, G, or B component of the color was much more hassle.
R = Color Mod 256  'read first byte
G = (Color \ 256) Mod 256  'lop off first byte - read second
B = Color \ 65536  'lop off first and second byte.
'or, alternatively:
R = Color And &HFF&  'read first byte
G = (Color \ &H100&) And &HFF&  'lop off first byte - read second
B = Color \ &H10000&  'lop off first and second byte.
And modifying the colors, while not as challenging as getting the R, G, and B component of the color, still required some forethought.

Fortunately, in .NET, a few things are simpler. Colors are stored in their own Structure, so that means you can still pass them around like values, and you don't have to create a new instance either. A Color can be created by calling the many Fromxxxx Shared (static in C#) functions available. Extracting the R, G, and B component is simple - the Color structure has .A, .B, .G, and .R properties which give the components of each of these values in the color. You can even use .GetHue, .GetSaturation, and .GetBrightness with your color as well.
        Dim A, B, G, R As Byte
        A = C1.A
        B = C1.B
        G = C1.G
        R = C1.R
        ' Extracting color channels is this easy.
            byte a, b, g, r;
            a = c1.A;
            b = c1.B;
            g = c1.G;
            r = c1.R;
            //Extracting color channels is this easy.
Modifying the colors, well, we'll get to that later.

Color Usage Caveats

An interesting thing to note: a color also stores information on how it is created and where it comes from.
        Dim C1, C2, C3, C4, C5 As Color
        C1 = Color.FromArgb(&HFF000000)
        C2 = Color.FromArgb(0, 0, 0)
        C3 = Color.Black
        C4 = Color.FromKnownColor(KnownColor.Black)
        C5 = Color.FromName("Black")
        MessageBox.Show(C1.ToString())
        MessageBox.Show(C2.ToString())
        'The two above all return "Color [A = 255, R = 0, G = 0, B = 0]"
        'The three remaining colors return "Color [Black]"
        MessageBox.Show(C3.ToString())
        MessageBox.Show(C4.ToString())
        MessageBox.Show(C5.ToString())
            Color c1, c2, c3, c4, c5;
            c1 = Color.FromArgb(0, 0, 255);
            c2 = Color.FromArgb(255, Color.Blue);
            c3 = Color.FromName("blue");
            c4 = Color.Blue;
            c5 = Color.FromKnownColor(KnownColor.Blue);
            MessageBox.Show(c1.ToString());
            MessageBox.Show(c2.ToString());
            // The above all return "Color [A = 255, R = 0, G = 0, B = 255]"
            // The below return "Color [Blue]"
            MessageBox.Show(c3.ToString());
            MessageBox.Show(c4.ToString());
            MessageBox.Show(c5.ToString());
Something else that is interesting, in VB6, the value of &HFF0000 corresponds to blue. &HFFFF0000 would be invalid for most cases as a color. In VB.NET, you must specify the alpha value in a leading FF, so you'd at least have &HFFFF0000. Not specifying the alpha FF for full opacity leaves you with the alpha value at 0, which means 0 opacity, and the color is totally transparent (invisible). In addition to the important alpha setting at the front of the hexadecimal, the hexadecimal location of the red and blue track have been switched to correspond with HTML's coloring scheme. Therefore &HFFFF0000 is not blue... it is red!

The good news: you won't likely be using hexadecimal for colors since you can create colors using the other overloads to Color.FromArgb().

The Curse of the ReadOnly Property

If you've messed with Colors long enough, you'll realize that the handy old .A, .B, .G, and .R properties are all readonly... which makes modifying colors quite messy. The only way to get a unique color is to make a new structure. Modifying anything inside of a color structure is off-limits... so you must create another color structure. Adding a little bit of green to a blue color.
        Dim C1 As Color
        C1 = Color.FromArgb(0, 0, 255)
        'Here's blue.  Let's say we want to add a little bit of green to it.
        'We have to do the following:
        C1 = Color.FromArgb(C1.R, C1.G + 64, C1.B)
        'Now we're very susceptible to overflow at this point.
        'To protect this operation a little bit, we can do:
        Dim R, G, B As Integer
        R = C1.R
        G = C1.G + 64
        B = C1.B
        'Route R to be in the 0-255 band.
        If R > 255 Then
            R = 255
        ElseIf R < 0 Then
            R = 0
        End If
        'Same with G.
        If G > 255 Then
            G = 255
        ElseIf G < 0 Then
            G = 0
        End If
        'And then B.
        If B > 255 Then
            B = 255
        ElseIf B < 0 Then
            B = 0
        End If
        'Now, our color must be valid.
        C1 = Color.FromArgb(R, G, B)
            Color c1;
            c1 = Color.FromArgb(0, 0, 255);
            //Since we cannot change the values within the color structure, we have to simply make a new one...
            //With our new values.
            c1 = Color.FromArgb(c1.R, c1.G + 64, c1.B);
            //This is prone to error... we need to fix the values before we continue.
            int r, g, b;
            r = c1.R;
            g = c1.G + 64;
            b = c1.B;
            //The three color channels can be restricted to the band of 0-255.
            if (r > 255)
                r = 255;
            else if (r < 0)
                r = 0;
            if (g > 255)
                g = 255;
            else if (g < 0)
                g = 0;
            if (b > 255)
                b = 255;
            else if (b < 0)
                b = 0;
            // Now we can safely create our new color.
            c1 = Color.FromArgb(r, g, b);
or...
        'Route all to be in the 0-255 band.
        R = Filters.BandPass(R, 0, 255)
        G = Filters.BandPass(G, 0, 255)
        B = Filters.BandPass(B, 0, 255)
        'Now, our color must be valid.
        C1 = Color.FromArgb(R, G And &HFF, B)
            //The three color channels can be restricted to the band of 0-255.
            r = filters.BandPass(r, 0, 255);
            g = filters.BandPass(g, 0, 255);
            b = filters.BandPass(b, 0, 255);
            // Now we can safely create our new color.
            c1 = Color.FromArgb(r, g, b);

            MessageBox.Show(c1.ToKnownColor().ToString());
Where the Filters class is the one I have written below and is about 16 months old, written especially for the late Power III.
XML comments added by me.
Public Class Filters
    ''' <summary>
    ''' True if the last call to either of the below filters was filtered.
    ''' </summary>
    ''' <remarks></remarks>
    Public Shared LastWasFiltered As Boolean
    ''' <summary>
    ''' Only passes numbers that are lower than the pivot; higher numbers become equal to the pivot.
    ''' </summary>
    ''' <param name="Number">The number that will be manipulated.</param>
    ''' <param name="Pivot">The reference point for the LowPass</param>
    ''' <returns>The number if it is lower than the pivot, returns the pivot otherwise.</returns>
    ''' <remarks></remarks>
    Public Shared Function LowPass(ByVal Number As Integer, ByVal Pivot As Integer) As Integer
        LastWasFiltered = False
        If Number > Pivot Then
            Number = Pivot
            LastWasFiltered = True
        End If
        Return Number
    End Function
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="Number">The number that will be manipulated.</param>
    ''' <param name="Pivot">The reference point for the HighPass</param>
    ''' <returns>The number if it is higher than the pivot, returns the pivot otherwise.</returns>
    ''' <remarks></remarks>
    Public Shared Function HighPass(ByVal Number As Integer, ByVal Pivot As Integer) As Integer
        LastWasFiltered = False
        If Number < Pivot Then
            Number = Pivot
            LastWasFiltered = True
        End If
        Return Number
    End Function
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="Number">The number that will be manipulated.</param>
    ''' <param name="PivotHigh">The highest value within the band.</param>
    ''' <param name="PivotLow">The lowest value within the band.</param>
    ''' <returns>The number if it is within the band; returns the lowpivot if the number is below the band and the highpivot if the number is above the band.</returns>
    ''' <remarks></remarks>
    Public Shared Function BandPass(ByVal Number As Integer, ByVal PivotHigh As Integer, ByVal PivotLow As Integer) As Integer
        LastWasFiltered = False
        If Number < PivotLow Then
            Number = PivotLow
            LastWasFiltered = True
        ElseIf Number > PivotHigh Then
            Number = PivotHigh
            LastWasFiltered = True
        End If
        Return Number
    End Function
End Class
    public class filters
    {
        /// <summary>
        /// True if the last call to either of the below filters was filtered.
        /// </summary>
        public static bool lastwasfiltered;
        /// <summary>
        /// Only passes numbers that are lower than the pivot; higher numbers become equal to the pivot.
        /// </summary>
        /// <param name="num">The number that will be manipulated.</param>
        /// <param name="pivot">The reference point for the LowPass</param>
        /// <returns>The number if it is lower than the pivot, returns the pivot otherwise.</returns>
        public static int LowPass(int num, int pivot)
        {
            if (num > pivot)
            {
                num = pivot;
            }
            lastwasfiltered = (num > pivot);
            return num;
        }
        /// <summary>
        /// Only passes numbers that are higher than the pivot; lower number become equal to the pivot.
        /// </summary>
        /// <param name="num">The number that will be manipulated.</param>
        /// <param name="pivot">The reference point for the HighPass</param>
        /// <returns>The number if it is higher than the pivot, returns the pivot otherwise.</returns>
        public static int HighPass(int num, int pivot)
        {
            if (num < pivot)
            {
                num = pivot;
            }
            lastwasfiltered = (num < pivot);
            return num;
        }
        /// <summary>
        /// Only passes numbers that are between the two pivots specifed.  If the number is outside, it will be set to the closest pivot.
        /// </summary>
        /// <param name="num">The number that will be manipulated.</param>
        /// <param name="highpivot">The highest value within the band.</param>
        /// <param name="lowpivot">The lowest value within the band.</param>
        /// <returns>The number if it is within the band; returns the lowpivot if the number is below the band and the highpivot if the number is above the band.</returns>
        public static int BandPass(int num, int highpivot, int lowpivot)
        {
            lastwasfiltered = false;
            if (num < lowpivot)
            {
                num = lowpivot;
                lastwasfiltered = true;
            }
            else if (num > highpivot)
            {
                num = highpivot;
                lastwasfiltered = true;
            }
            return num;
        }
    }

Fonts, FontFamilies, and FontCollections

OK, I threw FontCollections in there just so we'd have two. Most of the confusion comes between Font and FontFamily. These two classes both define particular fonts on your system.
A Font is the object that you use for Drawing and display purposes. A FontFamily is the object that you use to diagnose a Font.
Both of these classes can be a font such as Arial. However, the FontFamily class would describe Arial as a whole, tell you what styles you can use with Arial, and get design unit sizing of the font.
The Font Class, which is used more by me -- for graphics reasons, of course, could have, for example, Arial at size 12 with Bold. A FontFamily can only tell you if the font Arial can be bold, but the Font can actually become Bold Arial.

FontFamily - The FYI Class

A very useful function in FontFamily is the .IsStyleAvailable, which tells you if you can apply a Style (Bold, Italic, Regular, etc) to a font.
        If F2.IsStyleAvailable(FontStyle.Regular) Then
            if (f2.IsStyleAvailable(FontStyle.Regular))
I just so happen to have fonts on my system that cannot be set to Regular... they are defaulted to Bold. There are actually more fonts that support Bold rather than Regular.

What is a FontCollection?

A class that holds a ReadOnly array of FontFamilies, surprisingly. The only way to use it is to create a new instance of one of the classes that inherit from this base class.
        Dim FF() As FontFamily
        Dim FC As Drawing.Text.InstalledFontCollection
        FC = New Drawing.Text.InstalledFontCollection()

        FF = FontFamily.Families
        'At this point, FF is equivalent to FC.Families
            FontFamily[] ff;
            System.Drawing.Text.InstalledFontCollection fc;
            fc = new System.Drawing.Text.InstalledFontCollection();
                  
            ff = FontFamily.Families;
            // fc.Families is now equivalent to ff.
For a more interesting spin of things, the PrivateFontCollection Class lets you add fonts to it, much like an ArrayList. So, you have the option of having an array of FontFamilies or an ArrayList filled with FontFamilies. Anyway, enough on FontFamilies... let's get on to the beefy Font class!

The Font Class (Entourage)

A Font Class is somewhat easily creatable.
        F = New Font("Arial", 10.0F)
        '10pt Arial.  It's Style is Regular!
            f = new Font("Arial", 10f);
            //10pt Arial.  The style is Regular!
The style can be set like this:
        F = New Font("Arial", 10.0F, FontStyle.Italic)
        '10pt Arial.  It's Style is Italic!
            f = new Font("Arial", 10f, FontStyle.Italic);
            //10pt Arial.  The style is Italic!
If you want to specify Bold Italic, then you have to treat the Styles as flags and use Or to combine them. Treating these Styles as flags will come into use later on, so don't forget!
        F = New Font("Arial", 10.0F, FontStyle.Italic Or FontStyle.Bold)
        '10pt Arial.  It's Style is Italic!
            f = new Font("Arial", 10f, FontStyle.Italic | FontStyle.Bold);
            //10pt Arial.  The style is Italic!
Once again, like with the colors, you can extract the name, size, and style for the Font from the Font Class by way of the .Name, .Size. and .Style properties. The question - how do you modify Fonts?

The Curse of the Readonly Property

Unsurprisingly (perhaps), every usable property of a Font is readonly... so therefore, if you want to modify a Font, you have to create a new one. This isn't so bad for colors, as they are structures, but since Fonts are classes, you have a little bit of guilt of creating a whole New Font class to simply make the font Bold. Sad, but that's just the way it is. Fortunately, this has been anticipated by the constructor that accepts a Font prototype, meaning you can just pass in your old font and set a new style to it.
        F = New Font(F, FontStyle.Bold)
        ' My font is bold.
        F = New Font(F, FontStyle.Italic)
        ' My font is italic... notice that it is not bold anymore.
            f = new Font(f, FontStyle.Bold);
            //My font is now bold.
            f = new Font(f, FontStyle.Italic);
            //My font is now italic.  Note that the font is not bold - it is only Italic, because
            //we only specified the style to be Italic.
And of course, the best trick is toggling one of the font's styles. This can be done by Xor. Xoring a flag to a value type toggles the flag. That's part of the beauty of Xor.
        F = New Font(F, F.Style Xor FontStyle.Bold)
        ' I've just switched the boldness of my font.
            f = new Font(f, f.Style ^ FontStyle.Bold);
            //I've switched the boldness of the font.
Changing the font size requires more work:
        F = New Font(F.Name, 24, F.Style)
        'This is how you can change the font size.
        F = New Font(F.Name, F.Size + 4, F.Style)
        'Also like this...
            f = new Font(f.Name, 24f, f.Style);
            //This is the way you change the size.
            f = new Font(f.Name, f.Size + 4f, f.Style);
            //Also like this...
If you want to change the font name, you are better off creating a brand new font.


Well, that concludes this presentation on Colors and Fonts. I hope you had fun.