AndrewVos.Com .NET, C#, jQuery etc.

28Jul/100

Better compile times in Visual Studio

EDIT: Tried some of these today. Results are in blue.

Where I'm working now, we have a huge Visual Studio solution with hundreds of project files. The problem with this being that compile times are drastically reduced, causing great irritation and generally just hampering production.

Having a look around the web other people are definitely having this problem. Some suggestions are:

  • Merge all the projects into one, or at very least merge any projects that can logically be merged. For example, unit test projects could really benefit from being merged.
  • Have all projects output their bits to one directory.
  • Disable anti-virus (only for the solution directory): Not much of a change. Maybe lost a few seconds build time, though results may vary. Would recommend doing this always just in case your virus checker is slowing things down.
  • Combine the project files (this is generally not a great approach because the projects are split up like this so that our concerns are kept seperated).
  • Creating new solutions which only contain smaller parts of the code base, and linking them to compiled dlls from the other projects (DLL hell).
  • Parallel builds (Tools/Options./Projects and Solutions/Build and Run/Maximum Number of Parallel Project Builds): No noticeable change. The default setting was 4 by the way.
  • Stop using TFS (purely unwarranted, but who knows? Thought I'd just throw that in there).
  • Disable indexing on your source folders (windows search). No noticeable change. Didn't really expect anything to change here, because windows search has a way of delaying indexing as far as I know.

I'm going to have a look at these items tomorrow during lunch, and will update if I find anything good!

21Jan/100

CodeAnywhere 1.0.0.17

Version 1.0.0.17

  • Now compiles to .NET 3.5 instead of 2.0
  • Added support for updating from the context menu
  • You can now return an object instead of a string, and CodeAnywhere will do a ToString on it before writing it back

Sorry – haven’t been around in a while. Thought I’d stop by and release an awesome piece of software, CodeAnywhere.

Let’s say you have this:

CodeAnywhere1

And then you press CAPS LOCK:

CodeAnywhere2

CodeDOM compiled the code and executed it. (It’s C# code, by the way)

BAM.

I wrote this on the train travelling to work everyday.

Install it here

Things to note:

  • It uses UI Automation to grab the text, so some apps won’t work. Notepad and Wordpad definitely do though.
  • It’s meant to set CAPS LOCK state to off when it starts. Some people have reported that it doesn’t though. Comment below if this is you (state your OS please)
14Aug/090

A SQLite port to C#!

The popular SQLite library has been ported to C#!

Currently “2 errors out of 30054 tests” are reported in the version history. I’m going to wait for a future release, because it seems the code hasn’t been optimized properly for C# yet according to the benchmarks.

csharp-sqlite - Windows C# port of the SQLite library

15Jan/090

HtmlBuilder 0.1.33

HtmlBuilder is simple web design software, and this is it’s first release.

HtmlBuilder uses a simple template system, and includes some example templates under Help/Example Projects.

The HtmlBuilder Main Window

On the left we have the Site browser, which lists all files in the site.

HtmlBuilder Main Window

The Color Chooser Window

Favorite colors can be stored on the right hand side.

HtmlBuilder Color Chooser

Requirements

.NET Framework 3.5

Downloads

Version 0.1.33

Changes

Version 0.1.33 (January 15th 2009)

Added some DTD’s to the Insert/DTD Menu.

Version 0.1.3 (January 14th 2009)

First Release.

9Jan/090

Getting music from iTunes to your G1

I wanted to export all my music from iTunes to my G1, but I wanted to maintain the folder structure like this:

Artist\Album\TrackName.mp3

Couldn’t find anything on Google so here’s some C# code to get it done. Remember to add a reference to iTunesLib (Add Reference / COM).

   1: private static class ITunesExporter{
   2:   public static void Export(string playlistName, string path) {
   3:     iTunesAppClass iTunes = new iTunesAppClass();
   4:  
   5:     foreach (IITPlaylist playlist in iTunes.LibrarySource.Playlists) {
   6:       if (playlist.Name == playlistName) {
   7:         foreach (IITTrack track in playlist.Tracks) {
   8:           if (track.Kind == ITTrackKind.ITTrackKindFile) {
   9:             IITFileOrCDTrack file = (IITFileOrCDTrack)track;
  10:             string extension = Path.GetExtension(file.Location);
  11:             string newPath = ITunesExporter.createValidFileName(path, file.Artist, file.Album, file.Name, extension);
  12:             string newDirectoryName = Path.GetDirectoryName(newPath);
  13:             Directory.CreateDirectory(newDirectoryName);
  14:             
  15:             if (File.Exists(newPath) == false) {
  16:               File.Copy(file.Location, newPath);
  17:             }
  18:           }
  19:         }
  20:       }
  21:     }
  22:   }
  23:   private static string createValidFileName(string exportPath, string artist, string album, string track, string extension) {
  24:     artist = ITunesExporter.removeInvalidFileNameCharacters(artist);
  25:     album = ITunesExporter.removeInvalidFileNameCharacters(album);
  26:     track = ITunesExporter.removeInvalidFileNameCharacters(track);
  27:  
  28:     string filePath = exportPath;
  29:     filePath = Path.Combine(filePath, artist);
  30:     filePath = Path.Combine(filePath, album);
  31:     filePath = Path.Combine(filePath, track);
  32:     filePath = Path.ChangeExtension(filePath, extension);
  33:     return filePath;
  34:   }
  35:   private static string removeInvalidFileNameCharacters(string str) {
  36:     char[] chars = Path.GetInvalidFileNameChars();
  37:     foreach (char c in chars) {
  38:       str = str.Replace(c.ToString(), null);
  39:     }
  40:     return str;
  41:   }
  42: }

You can use the code like this:

   1: ITunesExporter.Export("Phone", @"D:\Music");

”Phone” is the playlist name and “D:\Music” is the path that the files will be exported to.

If anyone want’s to recode it to use the iTunes XML file please post the code! (I am to lazy).

7Jan/092

object.Format Extension

EDIT: I've added the string extension found here

I was reading an article on haacked today, called Fun With Named Formats, String Parsing, and Edge Cases and I started thinking what if I made a format extension method for an object which used reflection to get the properties.

The following example:

private static void test() {
  CultureInfo southAfrican = CultureInfo.CreateSpecificCulture("en-ZA");

  var human = new {
    FirstName = "Andrew",
    LastName = "Vos",
    BirthDate = new DateTime(1983, 10, 23),
    MoneyInPocket = 123.34
  };

  string formatted = human.Format(southAfrican,
    "Hello my name is {FirstName}, and my last name is {LastName}.\n" +
    "My birth date is {BirthDate:D}\n" +
    "I have {{MoneyInPocket:C}} in my pocket right now.\n" +
    "Here are some random chars... { { } }{ {} {}{} }{ {} " +
    "\nThis last value fails because there's no corresponding property: {TheLastValue}."
    );
  Console.WriteLine(formatted);
}

 

Would result in the following output:

Hello my name is Andrew, and my last name is Vos.
My birth date is 23 October 1983
I have {R 123.34} in my pocket right now.
Here are some random chars... { { } }{ {} {}{} }{ {}
This last value fails because there's no corresponding property: {TheLastValue}.

 

Here's the code:

internal static class ObjectFormatter {
  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Format(this object obj, string format) {
    return ObjectFormatter.Format(obj, null, format);
  }

  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <param name="provider">An <see cref="T:System.IFormatProvider"></see> that supplies culture-specific formatting information.</param>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Format(this object obj, IFormatProvider provider, string format) {
    if (format == null) throw new ArgumentNullException("format");
    if (obj == null) throw new ArgumentNullException("obj");

    const string propertyPattern = @"\{(?<PropertyName>[a-z,0-9,_]+)\:?(?<PropertyFormat>[^}]+)?\}";
    MatchCollection matches = Regex.Matches(format, propertyPattern,
      RegexOptions.Compiled |
      RegexOptions.CultureInvariant |
      RegexOptions.IgnoreCase);

    foreach (Match match in matches) {
      string propertyName = match.Groups["PropertyName"].Value;
      string propertyFormat = match.Groups["PropertyFormat"].Value;
      PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName);

      if (propertyInfo != null) {
        object propertyValue = propertyInfo.GetValue(obj, null);
        string propertyString = null;

        if (propertyValue != null) {
          propertyString = string.Format(provider, "{0:" + propertyFormat + "}", propertyValue);
        }
        format = format.Replace(match.Value, propertyString);
      }
    }

    return format;
  }
}
internal static class StringObjectInjector {
  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Inject(this string format, object obj) {
    return obj.Format(format);
  }

  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <param name="provider">An <see cref="T:System.IFormatProvider"></see> that supplies culture-specific formatting information.</param>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Inject(this string format, IFormatProvider provider, object obj) {
    return obj.Format(provider, format);
  }
}
6Jan/0913

ShellContextMenu2

Here's a refactored version of the ShellContextMenu code I released a while back. It has been converted to C# and the code is much better.

A shell context menu is the shortcut menu you see when right clicking on a file in Explorer.

Shell Context Menu

Download the code here

24Sep/080

SettingsManager 0.1

SettingsManager is a library that dynamically generates a form to allow the client to edit settings.

Take for example a class that contains the following property:

private bool someBoolean1 = true;
public bool SomeBoolean1 { get { return this.someBoolean1; } set { this.someBoolean1 = value; } }
 

To allow the client to edit this property we would typically create a Form and put some checkboxes, labels etc. on it.

With SettingsManager, we just inherit from the SettingsBase class, and apply a SettingAttribute to the property:

public class MySettings : SettingsBase {
  private bool someBoolean1 = true;
  [Setting("Demo Category", "Booleans", "Please check this box.", typeof(BooleanEditor))]
  public bool SomeBoolean1 { get { return this.someBoolean1; } set { this.someBoolean1 = value; } }
}

And show a Settings form:

MySettings settings = SettingsBase.Load<MySettings>("C:\\settings.xml");
TabbedSettingsForm settingsForm = new TabbedSettingsForm(settings);
settingsForm.ShowDialog();


And we get this:

SettingsManager

Creating custom editors

You may have noticed that the SettingAttribute constructor takes a Type. This we can use to make custom editors for types. Let's take a look at the source for the DateEditor type.

public class DateEditor : SettingEditorBase {

  DateTimePicker datePicker;

  public DateEditor(string text, object settingValue)
    : base(text, settingValue) {

    this.AddLabel(text);

    this.datePicker = new DateTimePicker();
    this.datePicker.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;

    this.datePicker.Value = (DateTime)settingValue;
    this.datePicker.ValueChanged += new EventHandler(datePicker_ValueChanged);
    this.Controls.Add(datePicker);
  }

  private void datePicker_ValueChanged(object sender, EventArgs e) {
    this.OnSettingChanged(this.datePicker.Value);
  }
}

Notice that we inherit from SettingEditorBase, which is an abstract class. Basically all that is important in this code is that we call OnSettingChanged when the value is changed. This allows the Settings form to enable the Apply button.

Creating custom Settings forms

Creating custom Settings forms is fairly easy. We inherit from SettingsFormBase, and override the InitializeEditors method. All our controls are placed on the contentPanel that is passed in as a parameter.

In InitializeEditors, we loop though this.Categories, and in turn loop through each categories' group. We then add each editor to the form.

The Ok, Cancel and Apply buttons are all taken care of in SettingsFormBase.

public class TabbedSettingsForm : SettingsFormBase {
  TabControl tabControl;
  public TabbedSettingsForm(SettingsBase settings)
    : base(settings) {
    this.Padding = new Padding(10);
  }

  public override void InitializeEditors(Panel contentPanel) {
    this.tabControl = new TabControl();
    this.tabControl.Dock = DockStyle.Fill;
    contentPanel.Controls.Add(this.tabControl);

    foreach (SettingCategory category in this.Categories) {
      TabPage tabPage = new TabPage(category.Name);
      tabPage.AutoSize = true;
      this.tabControl.TabPages.Add(tabPage);

      TableLayoutPanel content = new TableLayoutPanel();
      content.Dock = DockStyle.Fill;
      content.AutoScroll = true;
      tabPage.Controls.Add(content);

      foreach (SettingGroup group in category.Groups) {
        content.Controls.Add(new GroupTitle(group.Name));
        foreach (SettingEditorBase editor in group.Editors) {
          content.Controls.Add(editor);
        }
      }
    }
  }
}
 
Source and a Demo project is available here.
23May/084

Draw RTF Text on a Graphics Object in C#

With this code you can draw some RTF text on a Graphics object.

Code modified from this code: How to print the content of a RichTextBox control by using Visual C# .NET or Visual C# 2005

Also, the control transparency code was taken from here:Simple Vector Shapes (Tried to get his code working, but there were some problems, also some of the code is in a different language)

Anyway, it's an extension method, so you can do this in a Form:

protected override void OnPaint(PaintEventArgs e) {
    e.Graphics.Clear(Color.Fuchsia);
    e.Graphics.DrawRtfText(this.richTextBox1.Rtf, this.richTextBox1.ClientRectangle);
    base.OnPaint(e);
}

Here's the full code listing; just add a new .cs and throw it in:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public static class Graphics_DrawRtfText {
    private static RichTextBoxDrawer rtfDrawer;
    public static void DrawRtfText(this Graphics graphics, string rtf, Rectangle layoutArea) {
        if (Graphics_DrawRtfText.rtfDrawer == null) {
            Graphics_DrawRtfText.rtfDrawer = new RichTextBoxDrawer();
        }
        Graphics_DrawRtfText.rtfDrawer.Rtf = rtf;
        Graphics_DrawRtfText.rtfDrawer.Draw(graphics, layoutArea);
    }

    private class RichTextBoxDrawer : RichTextBox {
        //Code converted from code found here: http://support.microsoft.com/kb/812425/en-us

        //Convert the unit used by the .NET framework (1/100 inch) 
        //and the unit used by Win32 API calls (twips 1/1440 inch)
        private const double anInch = 14.4;

        protected override CreateParams CreateParams {
            get {
                CreateParams createParams = base.CreateParams;
                if (SafeNativeMethods.LoadLibrary("msftedit.dll") != IntPtr.Zero) {
                    createParams.ExStyle |= SafeNativeMethods.WS_EX_TRANSPARENT; // transparent
                    createParams.ClassName = "RICHEDIT50W";
                }
                return createParams;
            }
        }
        public void Draw(Graphics graphics, Rectangle layoutArea) {
            //Calculate the area to render.
            SafeNativeMethods.RECT rectLayoutArea;
            rectLayoutArea.Top = (int)(layoutArea.Top * anInch);
            rectLayoutArea.Bottom = (int)(layoutArea.Bottom * anInch);
            rectLayoutArea.Left = (int)(layoutArea.Left * anInch);
            rectLayoutArea.Right = (int)(layoutArea.Right * anInch);

            IntPtr hdc = graphics.GetHdc();

            SafeNativeMethods.FORMATRANGE fmtRange;
            fmtRange.chrg.cpMax = -1;                    //Indicate character from to character to 
            fmtRange.chrg.cpMin = 0;
            fmtRange.hdc = hdc;                                //Use the same DC for measuring and rendering
            fmtRange.hdcTarget = hdc;                    //Point at printer hDC
            fmtRange.rc = rectLayoutArea;            //Indicate the area on page to print
            fmtRange.rcPage = rectLayoutArea;    //Indicate size of page

            IntPtr wParam = IntPtr.Zero;
            wParam = new IntPtr(1);

            //Get the pointer to the FORMATRANGE structure in memory
            IntPtr lParam = IntPtr.Zero;
            lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
            Marshal.StructureToPtr(fmtRange, lParam, false);

            SafeNativeMethods.SendMessage(this.Handle, SafeNativeMethods.EM_FORMATRANGE, wParam, lParam);

            //Free the block of memory allocated
            Marshal.FreeCoTaskMem(lParam);

            //Release the device context handle obtained by a previous call
            graphics.ReleaseHdc(hdc);
        }

        #region SafeNativeMethods
        private static class SafeNativeMethods {
            [DllImport("USER32.dll")]
            public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr LoadLibrary(string lpFileName);

            [StructLayout(LayoutKind.Sequential)]
            public struct RECT {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct CHARRANGE {
                public int cpMin;        //First character of range (0 for start of doc)
                public int cpMax;        //Last character of range (-1 for end of doc)
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct FORMATRANGE {
                public IntPtr hdc;                //Actual DC to draw on
                public IntPtr hdcTarget;    //Target DC for determining text formatting
                public RECT rc;                        //Region of the DC to draw to (in twips)
                public RECT rcPage;                //Region of the whole DC (page size) (in twips)
                public CHARRANGE chrg;        //Range of text to draw (see earlier declaration)
            }

            public const int WM_USER = 0x0400;
            public const int EM_FORMATRANGE = WM_USER + 57;
            public const int WS_EX_TRANSPARENT = 0x20;

        }
        #endregion
    }
}

You might want to do some testing, it's not exactly production code!