December 15, 2010

IsDrivefree

A simple helper to check if a specified drive letter is free for use:
public static class DriveInfoExtender
{
    public static bool IsDriveFree(string drive)
    {
        if (string.IsNullOrEmpty(drive) || !char.IsLetter(drive[0]))
            return false;
        if (drive.Length >= 2)
            drive = drive.Substring(0,1).ToUpper();
        bool isFree = true;
        DriveInfo[] nonFreeDrives = System.IO.DriveInfo.GetDrives();
        foreach (DriveInfo di in nonFreeDrives)
        {
            if (di.Name.StartsWith(drive))
            {
                isFree = false;
                break;
            }
        }
        return isFree;
    }
}

November 29, 2010

AssemblyVersion and AssemblyFileVersion

See here
and here

  • AssemblyVersion is .NET version info whilst 'AssemblyFileVersion' is Windows version number, ie., what you see when you do view the file properties of an assembly in windows explorer. AssemblyVersion defines the version other assemblies are compiled against in their manifest.
  • If 'AssemblyFileVersion' attribute is not present in 'AssemblyInfo.cs' then AssemblyFileVesrion takes the same value as AssemblyVersion.

November 4, 2010

Proxy, Decorator, Adapter, Bridge and Facade Patterns

Found the following explanantion here. It is a useful description of the differences between these patterns as they are very similar.

Proxy, Decorator, Adapter, and Bridge are all variations on "wrapping" a class. But their uses are different:
  • Proxy could be used when you want to lazy-instantiate an object, or hide the fact that you're calling a remote service, or control access to the object.
  • Decorator is also called "Smart Proxy." This is used when you want to add functionality to an object, but not by extending that object's type. This allows you to do so at runtime.
  • Adapter is used when you have an abstract interface, and you want to map that interface to another object which has similar functional role, but a different interface.
  • Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation. I.e. you're not adapting to some legacy or third-party code, you're the designer of all the code but you need to be able to swap out different implementations.
  • Facade is a higher-level (read: simpler) interface to a subsystem of one or more classes. Think of Facade as a sort of container for other objects, as opposed to simply a wrapper.

November 2, 2010

Deleting a Read-Only File

See also the FileAttributesExtender class
and the PathCombine class
string fullPath = PathCombine(pathRoot, relativePath);
if (File.Exists(fullPath))
{
    FileAttributes fas = File.GetAttributes(fullPath);
    if (fas.Contains(FileAttributes.ReadOnly))
        File.SetAttributes(fullPath, FileAttributes.Normal);
    File.Delete(fullPath);
}

October 22, 2010

String Conversion C++ C#

C++/CLi to C#
std::string str = ...; 
System::String^ mgdstr = gcnew System::String(str.c_str();
and the other way
C# to C++
System::String^ value = ...;
CString cstr(value);
const std::string converted(cstr);

October 13, 2010

Static Property Reflection Sample

This sample enumerates over the "TargetClass" looking for all static properties that return the specified "ReturnType"
public static IEnumerable 
EnumerateReturnTypeProperties()
{
    Type type = typeof(TargetClass);
    PropertyInfo[] props = type.GetProperties();
    foreach (PropertyInfo prop in props)
    {
        // null as 1st GetValue parameter implies
        // only static properties enumerated
        ReturnType xxx = prop.GetValue(null, null)
            as ReturnType;
        if (xxx == null)
            continue;
        yield return xxx;
    }
}

Path.Combine

See here for a description of the behaviour of this method.
Following method resolves some weaknesses in the static method "Path.Combine" found in the System.IO namespace.
public static string PathCombine(string rootPath, string relativePath)
{
    Debug.Assert(rootPath != null);
    Debug.Assert(relativePath != null);

    string res = string.Empty;
    if (rootPath.EndsWith(":"))
    {
        rootPath += @"\";
    }
    // remove initial single '\' or '/' from relative path
    if (relativePath.StartsWith(@"\") || 
        relativePath.StartsWith(@"/"))
    {
        if (! (relativePath.StartsWith(@"\\") || 
               relativePath.StartsWith(@"//")))
        {
           relativePath = relativePath.Substring(1); 
        }
    }
    res = Path.Combine(rootPath, relativePath);
    res = res.Replace('/', '\\'); // replace '/' with '\'
    return res;
}

October 6, 2010

More Complex Progress Dialog

This sample does a file search asynchronously. At the end of the search the progress dialog pauses and closes itself. Internally a timer is used to initiate this automatic close of the progress dialog. AsynchFileFinder is the BackgroundWorker derived object. Here is th Xaml for the dialog:
<Window x:Class="FindingFilesDlg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Finding modified files" Height="167" Width="514" Closing="Window_Closing" Loaded="Window_Loaded">
    <Grid>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="butCancel" VerticalAlignment="Bottom" Width="91" Click="butCancelDone_Click">_Cancel</Button>
        <TextBlock Height="21" Margin="12,15,0,0" Name="tbActivity" VerticalAlignment="Top"  Text="Searching for modified files:" HorizontalAlignment="Left" Width="Auto" />
        <ProgressBar Margin="12,61,0,52" Name="progressBar" HorizontalAlignment="Left" Width="427" />
        <TextBlock Margin="0,61,16,52" Name="tbPercentage" Text="0%" HorizontalAlignment="Right" Width="31" TextAlignment="Right" />
    </Grid>
</Window>
Here is the code for the dialog:
public partial class FindingFilesDlg : Window
{
    AsyncFileFinder m_AsyncFileFinder = null;
    DispatcherTimer m_Timer = new DispatcherTimer();
    IEnumerable<FileSystemInfo> m_FilesFound = 
               new FileSystemInfo[0];
    int countDown = 1;
    const int SECOND = 10000000;
    bool m_Completed = false;

    public FindingFilesDlg(string rootDir, FileSearchRules fsr)
    {
        InitializeComponent();
        tbActivity.Text = "Preparing to search \'" +
            rootDir + "\' for modified files:";

        InitialiseTimer();
        InitialiseAsyncFileFinder(rootDir, fsr);
    }

    private void InitialiseAsyncFileFinder(
        string rootDir, 
        FileSearchRules fsr)
    {
        // The BackgroundWorker derived object
        m_AsyncFileFinder = new AsyncFileFinder(rootDir, fsr);
        m_AsyncFileFinder.ProgressChanged +=
            new ProgressChangedEventHandler(
                AsynchFileFinder_ProgressChanged);
        m_AsyncFileFinder.RunWorkerCompleted += new
            RunWorkerCompletedEventHandler(
            AsynchFileFinder_RunWorkerCompleted);
    }

    private void InitialiseTimer()
    {
        // Disable timer
        m_Timer.IsEnabled = false;
        // Set timer event interval
        m_Timer.Interval = new TimeSpan((long)(1 * SECOND));
        // Timer events
        m_Timer.Tick += new EventHandler(timer_Tick);
    }

    public IEnumerable<FileSystemInfo> FilesFound
    {
        get { return m_FilesFound; }
    }

    void AsynchFileFinder_RunWorkerCompleted(
        object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (!e.Cancelled)
        {
            m_Completed = true;
            m_FilesFound = m_AsyncFileFinder.Files;
            tbActivity.Text = "Finished searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\', " + m_FilesFound.Count().ToString() + 
                " files found";
            progressBar.Value = progressBar.Maximum;
            tbPercentage.Text = "100%";
            UpdateDoneButton();
            // wait several seconds before closing the dialog
            m_Timer.Start(); 
        }
    }

    void UpdateDoneButton()
    {
        string str = "_Done (" + 
              countDown.ToString() + ")";
        butCancel.Content = str;
    }
    
    bool textUpdated = false;
    void AsynchFileFinder_ProgressChanged(object sender, 
        ProgressChangedEventArgs e)
    {
        Debug.Assert((e.ProgressPercentage >= 0) && 
            (e.ProgressPercentage <= 100));
        progressBar.Value = e.ProgressPercentage;
        tbPercentage.Text = e.ProgressPercentage.ToString() +
                "%";
        if ((e.ProgressPercentage > 1) && !textUpdated)
        {
            textUpdated = true;
            tbActivity.Text = "Searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\' for modified files (" + 
                m_AsyncFileFinder.NumFilesToSearch.ToString() + 
                " files to search) :";
        }
    }

    private void Window_Closing(object sender, CancelEventArgs e)
    {
        CancelAsyncFileFinder();
        if (m_Timer.IsEnabled)
            StopTimer();
        m_AsyncFileFinder.Dispose();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        m_AsyncFileFinder.RunWorkerAsync();
        tbActivity.Text = "Searching \'" + 
            m_AsyncFileFinder.SearchDirectory + 
            "\' for modified files:";
    }

    // wait several seconds before closing the dialog
    void timer_Tick(object sender, EventArgs e)
    {
        --countDown;
        UpdateDoneButton();
        if (countDown == 0)
        {
            StopTimer();
            DialogResult = m_Completed;
        }
    }

    private void StopTimer()
    {
        m_Timer.Stop();
    }

    private void butCancelDone_Click(object sender, 
          RoutedEventArgs e)
    {
        if (!m_Completed)
        {
            CancelAsyncFileFinder();
            if (m_Timer.IsEnabled)
                StopTimer();
        }
        DialogResult = m_Completed;
    }

    private void CancelAsyncFileFinder()
    {
        if (m_AsyncFileFinder.IsBusy)
            m_AsyncFileFinder.CancelAsync();
    }
}

Here is the code for the background worker object:
internal class AsyncFileFinder : BackgroundWorker
{
    private FileSearchRules FileSearchRules { get; set; }
    string m_RootDir = string.Empty;
    List<System.IO.FileSystemInfo> files = new 
        List<System.IO.FileSystemInfo>();
    int m_NumberFilesToSearch = 0;

    // Pass required info in through constructor, 
    // cant change it whilst a search is underway
    public AsyncFileFinder(string rootDir, 
        FileSearchRules fsr) : base()
    {
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
        m_RootDir = rootDir;
        FileSearchRules = new FileSearchRules(fsr);
    }

    public string SearchDirectory
    {
        get { return m_RootDir; }
    }

    public IEnumerable<FileSystemInfo> Files
    {
        get
        {
            if (IsBusy) // No access while thread active
                return new List<FileSystemInfo>();
            else
                return files.AsReadOnly();
        }
    }

    // Total number of files that will be checked
    public int NumFilesToSearch
    {
        get
        {
            return m_NumberFilesToSearch;
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        bool cancelled = false;
        double progress = 0;
        double prevProgress = 0;
        files = new List<FileSystemInfo>();
        DirectoryInfo targ = new DirectoryInfo(m_RootDir);
        // Count the number of files to process first
        foreach (System.IO.FileSystemInfo fsi in
            targ.IterateFiles(true))
        {
            if (CancellationPending)
            {
                cancelled = true;
                break;
            }
            
            Interlocked.Increment(ref 
                m_NumberFilesToSearch);
        }
        int count = 0;
        if (!cancelled)
        {
            foreach (FileSystemInfo fsi in 
                targ.IterateFiles(true))
            {
                if (CancellationPending)
                {
                    cancelled = true;
                    break;
                }

                ++count;
                // progress as a percentage
                progress = (100.0 * (double)count /
                    (double)m_NumberFilesToSearch) + 0.5;
                // Report every 1% of progress
                if ((progress - prevProgress) > 1.0)
                {
                    ReportProgress((int)(progress));
                    prevProgress = progress;
                }

                if (FileSearchRules.PassesRules(fsi))
                {
                    files.Add(fsi);
                }
            }
        }
        e.Cancel = cancelled;
    }
 
}

General Purpose Progess Dialog

This is a general purpose progress dialog. It takes a background worker object as a parameter. Here is the Xaml for the dialog:
<Window x:Class="AsyncMessageDlg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="AsyncMessageDlg" Height="314" Width="750"
         WindowStartupLocation="CenterOwner" 
         Closing="Window_Closing" Loaded="Window_Loaded">
    <Grid>
        <Ellipse Fill="Azure" Height="56" Margin="0,27,120,0" Name="ellipse2" 
        Stroke="Beige" VerticalAlignment="Top" HorizontalAlignment="Right" Width="82" />
        <Ellipse Fill="Azure" Height="56" Margin="120,0,0,20" Name="ellipse1" 
        Stroke="Beige" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="82" />
        <TextBox Margin="12,35,12,63" Name="textBox" IsReadOnly="True" 
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" />
        <Button Height="23" Margin="560,0,0,6" VerticalAlignment="Bottom" 
Name="butOK" HorizontalAlignment="Left" Width="75" IsDefault="True" Click="butOK_Click">_Ok</Button>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,6" 
Name="butCancel" VerticalAlignment="Bottom" Width="75" IsCancel="True" Click="butCancel_Click">_Cancel</Button>
        <Label Height="28" Margin="12,7,12,0" Name="label" 
VerticalAlignment="Top">Label</Label>
        <ProgressBar Height="16" Margin="120,0,181,41" Name="progressBar" 
VerticalAlignment="Bottom" Visibility="Hidden" />
        <TextBlock Height="16" HorizontalAlignment="Right" Margin="0,0,144,41" 
Name="tbProgressPercent" Text="0%" TextAlignment="Right" VerticalAlignment="Bottom" Width="31" Visibility="Hidden" />
    </Grid>
</Window>
The dialog takes a string for the dialog title and a string for the label above the output text window. When the background worker task sends a progress update it tries to convert the UserState object passed in the event to a string which is added to the output window. Here is the code for the dialog:
public partial class AsyncMessageDlg : Window
{
    BackgroundWorker m_WorkerThread = null;
    bool m_Completed = false;

    public AsyncMessageDlg(
        string textBoxTitle,
        string dlgTitle, 
        BackgroundWorker bw)
    {
        Debug.Assert(bw != null);
        InitializeComponent();
        this.Title = dlgTitle;
        this.label.Content = textBoxTitle;
        m_Completed = false;
        InitialiseBackgrounWorker(bw);
    }

    private void InitialiseBackgrounWorker(BackgroundWorker bw)
    {
        Debug.Assert(bw != null);
        Debug.Assert(bw.WorkerSupportsCancellation);
        this.m_WorkerThread = bw;
        //if (m_WorkerThread.WorkerReportsProgress)
        butCancel.IsEnabled = m_WorkerThread.WorkerSupportsCancellation;
        butOK.IsEnabled = false;
        tbProgressPercent.Visibility = Visibility.Visible;
        progressBar.Visibility = Visibility.Visible;

        m_WorkerThread.ProgressChanged += new 
            ProgressChangedEventHandler(
              m_WorkerThread_ProgressChanged);
        m_WorkerThread.RunWorkerCompleted += new 
            RunWorkerCompletedEventHandler(
              m_WorkerThread_RunWorkerCompleted);
    }

    void m_WorkerThread_RunWorkerCompleted(object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        m_Completed = true;
        string result = e.Result as string;
        if (result != null)
        {
            label.Content = result as string;
        }
        progressBar.Value = progressBar.Maximum;
        tbProgressPercent.Text = "100%";
        butOK.IsEnabled = true;
        butCancel.IsEnabled = false;
    }

    void m_WorkerThread_ProgressChanged(object sender, 
        ProgressChangedEventArgs e)
    {
        Debug.Assert((e.ProgressPercentage >= 0) &&
            (e.ProgressPercentage <= 100));
        progressBar.Value = e.ProgressPercentage;
        tbProgressPercent.Text = e.ProgressPercentage.ToString() +
            "%";
        if (e.UserState != null) 
        {
            if (e.UserState is string)
            {
                textBox.Text += e.UserState as string;
            }
            else
            {
                textBox.Text += e.UserState.ToString();
            }
        }
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        m_WorkerThread.RunWorkerAsync();
    }

    private void Window_Closing(object sender, CancelEventArgs e)
    {
        CancelWorkerThread();
        m_WorkerThread.Dispose();
    }

    private void butOK_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = m_Completed;
        this.Close();
    }

    private void butCancel_Click(object sender, RoutedEventArgs e)
    {
        if (!m_Completed)
        {
            CancelWorkerThread();
        }
        DialogResult = m_Completed;
    }

    private void CancelWorkerThread()
    {
        if (m_WorkerThread.IsBusy && 
            m_WorkerThread.WorkerSupportsCancellation)
            m_WorkerThread.CancelAsync();
    }
}
Here is the code for a sample background worker
// Background worker to check out some files
internal class AsynchAddCheckOutFiles : BackgroundWorker
{
    List<FileSystemInfo> m_fileSystemInfoList;

    // Pass required info in through constructor, cant change it
    // whilst a search is underway
    public AsynchAddCheckOutFiles(ref List<FileSystemInfo> fileSystemInfoList) :
        base()
    {
        this.m_fileSystemInfoList = fileSystemInfoList;
        // Set clients refernce to null, we own the list.
        fileSystemInfoList = null; 
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        bool cancelled = false;
        double progress = 0;
        double prevProgress = 0;

        int m_NumberFiles = m_fileSystemInfoList.Count;
        int count = 0;
        if (!cancelled)
        {
            foreach (FileSystemInfo fsi in m_fileSystemInfoList)
            {
                if (CancellationPending)
                {
                    cancelled = true;
                    break;
                }

                Perforce.P4CommandTransaction res = 
                    Perforce.Instance.AddCheckOutFile(fsi);
                string state = res.ToString();

                ++count;
                // progress as a percentage
                progress = (100.0 * (double)count /
                    (double)m_NumberFiles) + 0.5;
                // Report every 1% of progress
                if ((progress - prevProgress) > 1.0)
                {
                    ReportProgress((int)(progress), state);
                    prevProgress = progress;
                }
                
            }
        }
        e.Cancel = cancelled;
    }

}

September 28, 2010

typeof and typeid

[c#] typeof($Type$) == [C++] $Type$::typeid

September 22, 2010

Timespan Formatting

timeSpan.ToString("hh:mm:ss") equivalent. (Only available in .Net 4!)
  TimeSpan timeSpan = new TimeSpan(DateTime.UtcNow.Ticks - scriptTime.Ticks);
   
  string timeStr = timeSpan.Hours.ToString().PadLeft(2, '0') + 
     ":" + timeSpan.Minutes.ToString().PadLeft(2, '0') + 
     ":" + timeSpan.Seconds.ToString().PadLeft(2, '0');

July 29, 2010

StringCollection Class Extender

A helper class for the StringCollection class.
public static class StringCollectionExtender
{
    // Convert a string collection to a multiline string where
    // each entry in the collection becomes a line in 
    // multi-line string
    public static string ToMultilineString(
        this StringCollection sc)
    {
        StringBuilder res = new StringBuilder();
        foreach (string str in sc)
        {
            res.AppendLine(str);
        }
        return res.ToString();
    }

    public static string[] ToStringArray(
        this StringCollection sc)
    {
        string[] res = new string[sc.Count];
        sc.CopyTo(res, 0);
        return res;
    }

    // Convert a multiline string to a string collection with each line
    // of the string becoming a new entry in the collection
    public static StringCollection MultilineStringToStringCollection(
        string str)
    {
        string[] lines = str.Split('\r');
        StringCollection res = new StringCollection();
        foreach (string line in lines)
        {
            res.Add(line.Trim('\n'));
        }
        return res;
    }
}

Snippets

Snippet Designer, very useful
SnippetManager

July 28, 2010

Assembly Version Incrementor

Find and increment the assembly version info in an Assembly info file
public class AssemblyVersionIncrementor
{
    public void IncrementAssemblyVersionString(FileSystemInfo fileInfo)
    {
        string[] lines = File.ReadAllLines(fileInfo.FullName);
        lines = IncrementAssemblyVersionString(lines);
        File.WriteAllLines(fileInfo.FullName, lines);
    }

    internal string[] IncrementAssemblyVersionString(string[] lines)
    {
        string[] res = new string[lines.Length];
        int ix = 0;
        foreach (string line in lines)
        {
            res[ix++] = IncrementAssemblyVersionString(line);
        }
        return res;
    }

    // Could be modified to find other types of Version Info by
    // making the "AssemblyVersion" a parameter
    internal string IncrementAssemblyVersionString(string line)
    {
        string res = line;
        if (line.Contains("AssemblyVersion"))
        {
            string newLine = string.Empty;
            string str = line;
            // Check for comments
            int commentStart = line.IndexOf("//");
            // TODO /* ... */ style comments
            if (commentStart >= 0)
            {
                str = line.Substring(0, commentStart);
            }
            if (str.Contains("AssemblyVersion"))
            {
                int start = str.IndexOf('"') + 1;
                int end = str.IndexOf('"', start);
                string assemblyVerStr = str.Substring(start, end - start);
                Version av = new Version(assemblyVerStr);
                Version newVer = av.IncrementRevision();
                newLine = line.Substring(0, start) + newVer.ToString() + 
                    line.Substring(end, line.Length - end);
            }

            res = newLine.Length > 0 ? newLine : line;
        }

        return res;
    }
}
uses the following Version extension class:
internal static class VersionExtender
{
    public static Version Increment(this Version version)
    {
        return Increment(version, 0x1L);
    }

    public static Version IncrementMajor(this Version version)
    {
        return Increment(version, 0x1000000000000L);
    }

    public static Version IncrementMinor(this Version version)
    {
        return Increment(version, 0x100000000L);
    }

    public static Version IncrementBuild(this Version version)
    {
        return Increment(version, 0x10000L);
    }

    public static Version IncrementRevision(this Version version)
    {
        return Increment(version, 0x1L);
    }


    // Good demo of using >> and << operators as well
    private static Version Increment(this Version version, ulong inc)
    {
        ulong versnNum = (((ulong)version.Major & 0xFFFF) << 48) +
            (((ulong)version.Minor & 0xFFFF) << 32) +
            (((ulong)version.Build & 0xFFFF) << 16) +
            ((ulong)version.Revision & 0xFFFF);

        versnNum += inc;

        UInt16 major = (UInt16)(versnNum >> 48);
        UInt16 minor = (UInt16)((versnNum >> 32) & 0xFFFF);
        UInt16 build = (UInt16)((versnNum >> 16) & 0xFFFF);
        UInt16 revision = (UInt16)(versnNum & 0xFFFF);

        return new Version(major, minor, build, revision);
    }
}

July 21, 2010

Quickly Add Xml To XmlDocument

This line loads an Xml document, looks for the presence of a particular line and if it is not there, adds it to the xml document. This is useful for updating a config file with a particular line of xml. Uses XPath to find the interesting element of the xml file.
public static void UpdateAnXmlNode(string xmlFileName)
{
  XmlDocument xDoc = new XmlDocument();
  try
  {
    // load the configuration file
    xDoc.Load(xmlFileName);

    // find the node of interest from the key
    XmlNode theNode =
    xDoc.SelectSingleNode(
@"/configuration/xxxConfiguration/moduleGroups/add[@trustName = 'Default']/modules");

    bool customXmlLinePresent = false;
    if (theNode != null)
    {
      customXmlLinePresent = theNode.InnerXml.Contains(
             "XXX.YYY.Sandbox.DebugTree.Module");
    }
    string extraXml = string.Empty;
    if (!customXmlLinePresent)
        extraXml += @"<add moduleType=""XXX.YYY.Sandbox.DebugTree.Module, XXX.YYY.Sandbox"" />" + Environment.NewLine;

    if (extraXml.Length > 0)
    {
      theNode.InnerXml += extraXml;
      xDoc.PreserveWhitespace = true;
      xDoc.Save(xmlFileName);
    }
  }
  catch (XmlException ex)
  {
    DoSomethingWithError(ex);
  }
}

July 16, 2010

FileSystemInfoExtender Class

Extend the FileSystemInfo class with enumerators to iterate through subdirectories, ditto with the DirectoryInfo class.
Now merged into this blog entry

FileAttributesExtender

Use Flags based enumerated types extender class to extend file attributes and give it Set/group theory like methods
public static class FileAttributesExtender
{
    // Return lhs flags plus rhs flags
    public static FileAttributes Union(this FileAttributes lhs, FileAttributes rhs)
    {
        return lhs | rhs;
    }

    // Return flags common to lhs and rhs
    public static FileAttributes Intersection(this FileAttributes lhs, FileAttributes rhs)
    {
        return lhs & rhs;
    }

    // Return lhs flags minus rhs flags
    public static FileAttributes Difference(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        int res = (int)lhs - (int)common;
        return (FileAttributes)(res);
    }

    // Return true if lhs contains ALL the flags within rhs
    public static bool Contains(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return (common == rhs);
    }

    // Return true if lhs contains any of the flags within rhs
    public static bool ContainsAnyOf(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return ((int)common > 0);
    }

    // Return true if lhs contains none of the flags within rhs
    public static bool ContainsNoneOf(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return ((int)common == 0);
    }

    // NON-extension methods here
    public static FileAttributes FromString(string source)
    {
        FileAttributes res = (FileAttributes)Enum.Parse(typeof(FileAttributes), source, true);
        return res;
    }

}

July 9, 2010

Close or Kill a Process

This routine will try and close a process by sending a close message to it's main window. If the process has no GUI or a timeout period is exceeded then the 'Process.Kill()' method will be used to close it.
// Try and close a process. Kill it if the Close takes 
// too long.
// exeLessExtension - Process name (without extension)
// msToWaitForCloseMainWindow - Time in milliseconds to
// wait for 'CloseMainWindow()' before just killing the 
// process, Set negative to not kill at all, set to 0 to 
// kill without calling CloseMainWindow
public static void ForceProcessClose(
  string exeLessExtension, 
  int msToWaitForCloseMainWindow)
{
  Process[] processes = Process.GetProcessesByName(exeLessExtension);

  if (msToWaitForCloseMainWindow != 0)
  {
    foreach (Process p in processes)
    {
      if (!p.CloseMainWindow())
      {   // Process does not have a GUI, use 'Kill()' instead
         msToWaitForCloseMainWindow = 0;
      }
    }
  }
  if (msToWaitForCloseMainWindow >= 0)
  {
    Thread.Sleep(msToWaitForCloseMainWindow);
    processes = Process.GetProcessesByName(exeLessExtension);
    foreach (Process p in processes)
    {
       p.Kill();
    }
  }
}

July 1, 2010

Using Images in WPF

Using project resources in WPF
  1. Create sub-directory for resources called say "Resources"
  2. Add your resources (images, icons etc.) to it.
  3. Copy the folder structure into the VS project and add the resource files to it.
  4. Check the Properties of your resources in VS project file to ensure they have a Build Action of 'Resource'
Creating an image in WPF: In XAML:
<Image Source="Resources/P4.ico" />
Adding an image to a window's resources is very easy:
<Window.Resources>
        <Image x:Key="P4Icon" Source="Resources/P4.ico" />
</Window.Resources>
In code:
Image myImage = new Image();
myImage.Source = new BitmapImage(new Uri("Icons/MyImage.png", UriKind.Relative));
Inserting an image in a button is very easy in WPF:
<Button HorizontalAlignment="Left" Margin="5,8,0,10" Name="p4But" Width="34" Click="p4But_Click">
   <Image Source="Resources/P4.ico" Width="24" />
</Button>

Start a Process and Capture the Output in c#

Simple sample for running a process synchronously and capturing the output
public string GetEnvironmentVars()
{
    Process process = new Process();
    // UseShellExecute must be 'false' when redirecting the output
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.FileName = "p4";
    process.StartInfo.Arguments = " set ";
    //process.StartInfo.WorkingDirectory = dir;
    process.Start();
    string output = process.StandardOutput.ReadToEnd();
    //string error = process.StandardError.ReadToEnd();
    // Wait a reasonable ammount of time for it to finish
    bool res = process.WaitForExit(5000); 
    return res ? output : "Command \"" + 
        process.StartInfo.FileName + process.StartInfo.Arguments +
        "\" failed to exit:" + Environment.NewLine + output;
}

Editing Context Menus of Visual Studio 2008

Can add external tools to context menu.
  1. Create your external tool using the "Tools=>External Tools" menu option. Your new external tool will be called "External Command n" where 'n' is the external menu item tool location in the external menu items group of the Tools menu
  2. Open "Tools=>Customize" dialog.
  3. Select "Context Menus" in the "Toolbars" tab.
  4. The context menus will appear on the toolbar. Select a context menu from one on the toolbar. Some useful ones are:
    • "Project and Solution Context Menus=>Item" - This is the file context menu in the solutions folder
    • "Project and Solution Context Menus=>Project" - This is the project context menu in the solutions folder
    • "Other Context Menus=>Easy MDI Document Window" - Not obvious. This is the context menu for the tab at the top of a file editor window
  5. Goto "Command" tab and select "Tools" in the categories tab. From the "Commands:" list select the appropriate "External Command n"
Perforce menu items are defined in "External Tools" as follows:
Title: P4 Checkout
Command: C:\Program Files (x86)\Perforce\p4.exe
Arguments: edit $(ItemPath)
Initial Directory: $(ItemDir)
Select "Use Output window"
As shown here:

Title: P4 Revert
Command: C:\Program Files (x86)\Perforce\p4.exe
Arguments: revert $(ItemPath)
Initial Directory: $(ItemDir)
Select "Use Output window"

June 29, 2010

Open An EMail In Outlook

If are running a PC with Outlook and you want to open an email using Outlook from your application (but not send it immediately)
Following sample opens an email dialog with a specified document already attached
private void butEMailFiles_Click(object sender, RoutedEventArgs e)
{
    string archiveName = GetExistingArchiveName();
    if (File.Exists(archiveName))
    {
        RunShellCommand("Outlook.exe", "/a \"" + archiveName + "\""); 
    }
}

private static void RunShellCommand(string app, string args)
{
    string dir = System.IO.Path.GetDirectoryName(app);
    string file = System.IO.Path.GetFileName(app);

    Process process = new Process();
    process.StartInfo.FileName = file;
    process.StartInfo.WorkingDirectory = dir;
    process.StartInfo.Arguments = args;
    process.Start();
}

Run Batch File in C#

Quick method to create and run a batch file, the contents of which are passed as a string.
private void RunBatchFile(
  string batFileContents, 
  int waitToFinishSecs)
{
    string batFile = Path.GetTempFileName();
    batFile = batFile.Replace(".tmp", ".bat");
    File.WriteAllText(batFile, batFileContents);
    string dir = System.IO.Path.GetDirectoryName(batFile);
    string file = System.IO.Path.GetFileName(batFile);

    Process process = new Process();
    process.StartInfo.FileName = file;
    process.StartInfo.WorkingDirectory = dir;
    bool res = false; // Dont do anything with this yet
    try
    {
        process.Start();
        res = process.WaitForExit(waitToFinishSecs*1000);
        File.Delete(batFile);
    }
    catch (Win32Exception winex)
    {
        Debug.WriteLine(winex.ToString());
        throw;
    }    
}

June 10, 2010

Sweepstake Generator

Highly topical now the world cup is on! Here is my sweepstake generator class
class SweepstakeGenerator
{
  public class PlayerTeamPair
  {
    public string Player { get; set; }
    public string Team { get; set; }
    public override string ToString()
    {
      return (string.IsNullOrEmpty(Team) || string.IsNullOrEmpty(Player)) ? 
        "" : Team + " - " + Player;
    }
  }

  public static IEnumerable AssignTeams(
    IEnumerable orderedTeams, 
    IEnumerable namesArray)
  {
    Debug.Assert(namesArray.Count() > 0, "No names in the names array!");
    List names = new List();
    List teams = new List(orderedTeams);
    List assignedTeams = new List(teams.Count);
    Random random = new Random();
    int teamIx = 0;
    while (teamIx < teams.Count)
    {
      if (names.Count == 0)
      {
        names = names = new List(namesArray);
      }
      int nameIx = random.Next(0, names.Count);
      assignedTeams.Add(new PlayerTeamPair() { 
        Team = teams[teamIx], 
        Player = names[nameIx] });
      //Debug.WriteLine(teams[teamIx] + " - " + names[nameIx]);
      //Console.WriteLine(teams[teamIx] + " - " + names[nameIx]);
      names.RemoveAt(nameIx);
      teamIx++;
    }
    return assignedTeams;
  }
}
and sample usage
string[] teamsOrderedByBettingOdds = new string[] { 
 "Spain", "Brazil", "Argentina", "England",
 "Holland", "Germany", "Italy", "France",
 "Portugal", "Ivory Coast", "Serbia", "Mexico",
 "Chile", "USA", "Paraguay", "Uruguay",
 "Cameroon", "South Africa", "Ghana", "Australia",
 "Denmark", "Nigeria", "Greece", "South Korea",
 "Switzerland", "Slovakia", "Slovenia", "Japan",
 "Algeria", "Honduras", "North Korea", "New Zealand"
 };
string[] teamsOrderedByGrouping = new string[] { 
 "France", "Mexico", "South Africa", "Uruguay",
 "Argentina", "Greece", "Nigeria", "South Korea",
 "Algeria", "England", "Slovenia", "USA",
 "Australia", "Germany", "Ghana", "Serbia",
 "Cameroon", "Denmark", "Japan", "Netherlands",
 "Italy", "New Zealand", "Paraguay", "Slovakia",
 "Brazil", "Ivory Coast", "North Korea", "Portugal",
 "Chile", "Honduras", "Spain", "Switzerland"
 };
string[] teamsOrderedTop8AndThenByGrouping = new string[] { 
 "France", "Argentina", "England", "Germany",
 "Holland", "Italy", "Brazil", "Spain",
 "Mexico", "South Africa", "Uruguay", "Greece",
 "Nigeria", "South Korea", "Algeria", "Slovenia",
 "USA", "Australia", "Ghana", "Serbia",
 "Cameroon", "Denmark", "Japan", "New Zealand",
 "Paraguay", "Slovakia", "Ivory Coast", "North Korea",
 "Portugal", "Chile", "Honduras", "Switzerland" };
 
string[] namesArray = new string[] { 
  "Bob", "Ron", "Tony", "Jim", "George", "Ben", "Alf", "Dirk" };

IEnumerable playerTeamIter = 
  SweepstakeGenerator.AssignTeams(
    teamsOrderedTop8AndThenByGrouping, namesArray);
foreach (SweepstakeGenerator.PlayerTeamPair playerTeam in playerTeamIter)
{
 Debug.WriteLine(playerTeam.ToString());
}
Sample Output
France - George
Argentina - Tony
England - Alf
Germany - Ron
Holland - Bob
Italy - Jim
Brazil - Ben
Spain - Dirk
Mexico - Jim
South Africa - Ben
Uruguay - George
Greece - Dirk
Nigeria - Alf
South Korea - Ron
Algeria - Bob
Slovenia - Tony
USA - Dirk
Australia - Jim
Ghana - Alf
Serbia - George
Cameroon - Tony
Denmark - Ben
Japan - Ron
New Zealand - Bob
Paraguay - Dirk
Slovakia - Alf
Ivory Coast - Jim
North Korea - Ben
Portugal - Ron
Chile - Bob
Honduras - George
Switzerland - Tony

May 28, 2010

Send An Email Using Outlook

See KB Article 310263
Add a reference to Microsoft.Office.Interop.Outlook then use following code:
using Outlook = Microsoft.Office.Interop.Outlook;

public void SendPlainFormatEmail(
  string recipient, 
  string subject, 
  string body, 
  string filePath, 
  bool silently)
{
    try
    {
        //Check file exists before the method is called
        FileInfo fi = new FileInfo(filePath);

        bool exists = fi.Exists;
        if (!exists)
            return;

        string fileName = fi.Name;

        // Create the Outlook application by using inline initialization.
        Outlook.Application outlookApp = new Outlook.Application();

        //Create the new message by using the simplest approach.
        Outlook.MailItem msg = (Outlook.MailItem)outlookApp.CreateItem(
          Outlook.OlItemType.olMailItem);

        Outlook.Recipient outlookRecip = null;
        //Add a recipient.
        if (!string.IsNullOrEmpty(recipient))
        {
          outlookRecip = (Outlook.Recipient)msg.Recipients.Add(recipient);
          outlookRecip.Resolve();
        }

        //Set the basic properties.
        msg.Subject = subject;
        msg.Body = body;
        msg.BodyFormat = Microsoft.Office.Interop.Outlook.
             OlBodyFormat.olFormatPlain;

        //Add an attachment.
        long sizeKB = fi.Length / 1024;
        string sDisplayName = fileName + "(" + 
                     sizeKB.ToString() + " KB)";
        int position = (int)msg.Body.Length + 1;
        int iAttachType = (int)Outlook.OlAttachmentType.olByValue;
        Outlook.Attachment attachment = msg.Attachments.Add(
          filePath, iAttachType, position, sDisplayName);

        //msg.Save();
        if (silently)
        {
            //Send the message.
            msg.Send();
        }
        else
        {
            msg.Display(true);  //modal
        }

        //Explicitly release objects.
        outlookRecip = null;
        attachment = null;
        msg = null;
        outlookApp = null;
    }

    // Simple error handler.
    catch (Exception e)
    {
        Console.WriteLine("{0} Exception caught: ", e);
    }
}
Following code uses the above method to email a file:
public void EmailFile(string recipient, string filePath)
{
    FileInfo fi = new FileInfo(filePath);
    bool exists = fi.Exists;
    if (!exists)
        return;

    string fileName = fi.Name;
    string subject = "Emailing: " + fileName;
    string body = "Your message is ready to be sent with the following "
           "file or link attachments:" + Environment.NewLine +
           Environment.NewLine +
           fileName + Environment.NewLine +
           Environment.NewLine +
           "Note: To protect against computer viruses, e-mail programs"
           " may prevent sending or receiving certain types of file "
           "attachments.  Check your e-mail security settings to determine"
           " how attachments are handled."; ;
    SendPlainFormatEmail(recipient, subject, body, filePath, false);
}

May 27, 2010

WPF MessageBox Dialog Examples

Copy and paste type samples
using System.Windows;
Suprise type dialog:
MessageBox.Show(this,
 "Specified root directory \'" + rootDir + "\'does not exist",
 "Find files",
 MessageBoxButton.OK,
 MessageBoxImage.Exclamation);
Question type dialog:
MessageBox.Show(this,
 "Are you sure you want to delete these files" + Environment.NewLine +
 fileListStr,
 "Delete selected files",
 MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) 

May 26, 2010

Beware of using anonymous delegates for event handlers

First found a description of this issue here
There is a problem unsubscribing to an event
Check out the following code:
EventHandler onXXX =
 delegate(object sender, XXXEventArgs e)
 {
  onXXXCallCount++;
  xxxs.Add(e.LastXXX);
 };
... 
m_YYY.OnXXX += onXXX;
...
// The anonymous event handler stays on the event handler until removed
// or the parent object is disposed of!
m_YYY.OnXXX -= onXXX;

May 20, 2010

Open Explorer At Some Directory in C#

Use the following code snippet:
// Open explorer in user temporary directory
OpenExplorerInDir(System.IO.Path.GetTempPath());
...
private void OpenExplorerInDir(string dir)
{
  string exe = "explorer.exe";

  //Driectory.Exists
  Process exeProcess = new Process();
  exeProcess.StartInfo.FileName = exe;
  exeProcess.StartInfo.Arguments = "/e,/root," + dir;
  exeProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
  exeProcess.Start();
}

C++ Pattern: Use pointer behind a reference

The advantage is in the header file the member variable is forward declared so the user of the class only needs the header file in their class source file (and not in their header) In header file declare the reference object:
...
namespace aaaa { namespace bbbb {
  class xxxx;
}}
...
  /// Reference member variable.
 aaaa::bbbb::xxxx& memberVar;
In source file assign reference using *new()
SomeClass::SomeClass(const SomeClass &time)
    // Here is the trick, new() and dereference (*) 
    // immediately into a reference object
  : memberVar(*new xxxx(time.memberVar)) {
}

SomeClass::~SomeClass() {
  delete &memberVar;
}

Null Pointers and Delete in C++

From here: Null Pointers and Delete C++ guarantees that delete operator checks its argument for null-ness. If the argument is 0, the delete expression has no effect. In other words, deleting a null pointer is a safe (yet useless) operation.
 
if (ptr == NULL) // useless, delete already checks for null value
{
  delete(ptr);
}
// but always set ptr = NULL after deleting it otherwise another 
// call to delete(ptr) will throw an exception
ptr = NULL; 

C++ Virtual Destructor

Look at this Why are destructors not virtual by default? Basic rule is if a class has at least one virtual function, it should have a virtual destructor
Example:
class Base 
{
...
  virtual ~Base();
...
};

class Derived : public Base 
{
 ...
  ~Derived();
};

C++ auto_ptr and shared_ptr

std::auto_ptr

Good article on why and how to use it Another good article on when and how to use it
{
  auto_ptr<T> ptr( new T );
  ...
} // when ptr goes out of scope it deletes the underlying object!
  • auto_ptr employs the "exclusive ownership" model. This means that you can't bind more than one auto_ptr object to the same resource.
  • Be careful when passing as a parameter to a method by value because on returning the pointer will point to nothing. The copy in the method will destroy the pointer when the method ends, so pass it by reference.
  • Also because of this do not use it with template libraries!

std::tr1::shared_ptr

  • Unlike auto_ptr, shared_ptr uses reference counting so multiple "shared_ptr"s can point to the same resource.
  • Destructor decrement the reference count. When it reaches zero the resource is "delete"d.
  • Can have a user defined deleter, so shared_ptr need not necessarily hold a "pointer to something" type resource, it could be a file handle, etc. Define a "delete" function to delete the custom resource
  • Be careful of cyclic references, the objects will not be able to delete one another

May 14, 2010

Example Using std::map<> In C++

See Wikipedia reference
std::map<XXX*, DataAveragerPtr> averagerCache;
const XXX* xxx = ...

DataAveragerPtr averager;
if(averagerCache.find(xxx) == averagerCache.end())
{ // Not found so create a new one
  averager.reset(new SomeDataAverager(...));     
  averager->DoSomethingExpensive();
  averagerCache.insert (std::pair<XXX*, DataAveragerPtr>(
  xxx, averager));
}
else // found it
{
  // for parametric types <A, B> use an A to get a B,
  // B b = mymap[a]; where a is of type A
  averager = averagerCache[xxx]; // lookup
}
...
if (!averagerCache.empty())
averagerCache.clear();
When passing it as a parameter, the best option is to pass it by reference and just create it on the stack.

Events In C#

Look here: How to: Implement Events in Your Class - A good beginners guide.
and here: C# events vs. delegates - Good detailed explanation of events and how they differ from delegates

Implementing the event in Your Publisher Class

Step 1 (Optional): Define your custom "EventArgs" class
public class SomethingChangedEventArgs : EventArgs
{
...
}
The following steps take place in the event publisher class
class SomethingChangedEventPublisher
{
Step 2: Expose your custom EventHandler
public event EventHandler<SomethingChangedEventArgs> SomethingChanged; // for events with custom arguments
  public event EventHandler SomethingChanged; // otherwise use this for standard events
Step 3: Define an event publishing helper method (this is agood practice)
  void PublishSomethingChangedEvent(SomethingChangedEventArgs ea)
  {
    EventHandler<SomethingChangedEventArgs> eh = this.SomethingChanged
    if (eh != null)
      eh(this, ea);
  }
Step 4: Publish/Fire/Trigger the event in the appropriate places
  ...
  PublishSomethingChangedEvent(this, new SomethingChangedEventArgs(...));
  
  ...
}

Subscribing to the event in the Listener/Observer/Subscriber Class

The following steps take place in the event observer/subscriber class
class SomethingChangedEventSubscriber
{
Step 5: Subscribe to the event
  // VS Intellisense will do most of the work for you here!
  SomethingChangedEventSource.SomethingChanged += new EventHandler<SomethingChangedEventArgs>(OnSomethingChanged);
...
Step 6: Handle the event
  // The method where the SomethingChangedEvent is processed 
  private void OnSomethingChanged(object sender, SomethingChangedEventArgs eargs)
  {
    // SomethingChanged detected, now do something with it here
  }
...
Step 7: Unsubscribe to the event (not always required but you should consider it)
  // VS Intellisense will do most of the work for you here!
  SomethingChangedEventSource.SomethingChanged -= new EventHandler<SomethingChangedEventArgs>(OnSomethingChanged);
  
...
}
Alternatively, use an inline delegate to handle the event
SomethingChangedEventSource.SomethingChanged += delegate (object sender, SomethingChangedEventArgs e)
{
  ...
};
But there is a danger to doing this see here

May 12, 2010

Creating A Single Instance WPF Application

Look here: Initial idea. - Did not seem to work!
How can I provide my own Main() method in my WPF application?
and here

Simplest solution found:
  1. On App.xaml build properties, set "Build Action" to Page.
  2. Add following code to "App.xml.cs" or equivalent, the "App" class source.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;

...

[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

/// <summary>
/// Application Entry Point.
/// </summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
  // Use mutex to ensure only single instance is running
  bool mutexOwnershipGranted = true;
  using (Mutex mutex = new Mutex(
    true, 
    "$SOMEUNIQUESTRING$", // Use GUID or string id here
    out mutexOwnershipGranted))
  {
    // If this is the only running instance
    if (mutexOwnershipGranted) 
    { // Then run app 
      DevHelperWpf.App app = new DevHelperWpf.App();
      app.InitializeComponent();
      app.Run();
    }
    else 
    { // Bring current running instance to the front
      Process current = Process.GetCurrentProcess();
      foreach (Process process in Process.GetProcessesByName(
          current.ProcessName))
      {
         if (process.Id != current.Id)
         {
            SetForegroundWindow(process.MainWindowHandle);
            break;
         }
      }
    }
  }
}

March 24, 2010

Environment Variables

Environment.SetEnvironmentVariable Method on MSDN provides some comprehensive samples

Use of "Environment.GetEnvironmentVariables()" Example:
...
  ShowEnvironmentVariables(EnvironmentVariableTarget.User);
  ShowEnvironmentVariables(EnvironmentVariableTarget.Machine);
  ShowEnvironmentVariables(EnvironmentVariableTarget.Process);
...
private static void ShowEnvironmentVariables(EnvironmentVariableTarget targ)
{
    IDictionary ret = Environment.GetEnvironmentVariables(targ);
    string[] keys = new string[ret.Count];
    string[] values = new string[ret.Count];
    ret.Keys.CopyTo(keys, 0);
    ret.Values.CopyTo(values, 0);

    string targStr = targ.ToString().ToUpper();
    string underlineStr = "=======";
    Debug.WriteLine(targStr);
    Debug.WriteLine(underlineStr);
    for (int ix = 0; ix < ret.Count; ix++)
    {
        Console.WriteLine(keys[ix] + " = " + values[ix]);
    }
    Debug.WriteLine("");
}
To write an Environment variable use "Environment.SetEnvironmentVariable"
// by default the environment variable is set on the "Process"
Environment.SetEnvironmentVariable("TestEnvVar", "change1");
// but can be set for the user
Environment.SetEnvironmentVariable("TestEnvVar", "andagain", EnvironmentVariableTarget.User);
// or the system (if the program is executed through an account 
// with the appropriate permissions)
Environment.SetEnvironmentVariable("TestEnvVar", "andagain", EnvironmentVariableTarget.Machine);

February 16, 2010

ZipStorer

Quick way to include Zip file functionality.
Find up to date version at http://zipstorer.codeplex.com/
The nice thing about ZipStorer is that you dont need to reference any other assemblies, you just include the class file in your project and away you go.
Quick and dirty example of using ZipStorer to compress a bunch of files:
...
saveFilePath = System.IO.Path.ChangeExtension(saveFilePath, "zip");
StringBuilder fileList = new StringBuilder();
string comment = "File changes from: " + 
                 DateTime.UtcNow.ToLongDateString() +
                 Environment.NewLine +
                 "Original path: " + rootDir;                                  
using (ZipStorer zipStore = ZipStorer.Create(saveFilePath, comment))
{
    foreach (CheckedListItem cli in listBoxFilesChanged.SelectedItems)
    {
        if (cli.FileSystemInfo.Exists)
        {
            zipStore.AddFile(ZipStorer.Compression.Deflate, // Compress
               cli.FullName,     // Full path of file to be added
               cli.RelativeName, // Stored as a relative path in zip file
               cli.FullName);    // Comment for stored file (source of the original)
            fileList.AppendLine(cli.FullName);
        }
    }
}
...

Show and ShowDialog in WPF

Show a WPF window in modal form use ShowDialog()
private void ShowXXXDialogModal()
{
    // Instantiate the dialog box
    XXXDlg dlg = new XXXDlg();

    // Configure the dialog box
    dlg.Owner = this;
    // Open the dialog box modally 
    dlg.ShowDialog();
}
Show a WPF window in modaless form (ie dont wait for the opened window to close before returning) use Show()
private void ShowXXXDialogModaless()
{
    // Instantiate the dialog box
    XXXDlg dlg = new XXXDlg();

    // Configure the dialog box
    dlg.Owner = this;
    // Open the dialog box modalessly 
    dlg.Show();
}

February 9, 2010

Dialog To Browse For A Folder

Need these refences
using System.Windows.Forms;
using System.IO;
Sample button logic
  
private void butBrowse_Click(object sender, RoutedEventArgs e)
{
  tbRootDir.Text = BrowseForFolder("Browse to root directory of source", tbRootDir.Text);
}
Browser dialog usage:
private string BrowseForFolder(string descr, string dir)
{
  using (FolderBrowserDialog fbd = new FolderBrowserDialog())
  {
    fbd.Description = descr;
    if (Directory.Exists(dir))
      fbd.SelectedPath = dir;
    if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
      if (Directory.Exists(fbd.SelectedPath))
      {
        dir = fbd.SelectedPath;
      }
    }
  }
  return dir;
}

January 27, 2010

Generic Constraints Syntax In C# (using where keyword)

On MSDN
or
Search for MSDN page with this

Takes the form:
... class SomeClass<args>
     where Args : class

Here is a list of valid constraints

where T: struct
The type argument must be a value type. Any value type except Nullable can be specified.

where T : class
The type argument must be a reference type; this applies also to any class, interface, delegate, or array type.

where T : new()
The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last.

where T : <base class name>
The type argument must be or derive from the specified base class.

where T : <interface name>
The type argument must be or implement the specified interface. Can be more than one and the interfaces specified can be generic

where T : U
The type argument supplied for T must be or derive from the argument supplied for U.

January 22, 2010

Hosting WPF Controls In Windows Forms

Host WPF controls in a windows form control (using an ElementHost)
WPF for those who know Windows Forms (Large document)

You must add references to "WindowsBase", "WindowsFormsIntergration", "PresentationCore" and "PresentationFramework"
private ElementHost wpfCtrlHost;
private TestWpfControl testWpfCtrl;

public WindowsFormHost()
{
    InitializeComponent();
 ...
    HostWpfControl();
}

private void HostWpfControl()
{
    wpfCtrlHost = new ElementHost();
    wpfCtrlHost.Dock = DockStyle.Fill;
    this.Controls.Add(wpfCtrlHost);
    testWpfCtrl = new TestWpfControl();
    testWpfCtrl.InitializeComponent();
    wpfCtrlHost.Child = testWpfCtrl;
}