Zip My Code






4.78/5 (17 votes)
A utility stripping your source code to the essential core and then compressing it to a nice CodeProject article attachment.
Contents
- Introduction
- Credits
- How to Use the Utility
- Grains of Gold
- Built Using MVVM and the Dialog Service
- New Way of Writing Properties
- Algorithm Searching Files and Folders with Exclude Pattern
- History
Introduction
This article will present you with a utility that may come in handy if you are a contributor on CodeProject. The utility eases stripping down the source to the pure essential of what article code attachments should be. It is also very handy to do so, using simple drag-and-drop or the console interface. If enough people find it useful, it may earn its place as an author tool on the Free Tools for Authors page, which according to me is rather scant.
I am aware that this article won't get any good ratings or popularity, it is after all a very basic application. It focuses on solving a minor problem rather than presenting you with really cool code. There are, however, some grains of gold buried within the application that might interest some of you.
Credits
- First and foremost credit goes to CodeProject member SAKryukov for taking his time to review the article and give extremely constructive feedback.
- MADEBITS for their NETZ, a utility I use for compressing and packing ZipMyCode into one assembly.
- The ICSharpCode team for their SharpZipLib, an assembly I use for Zip compression.
- CodeProject member Peter Palotas for his Plossum command line parser.
How to Use the Utility
The UI Part
- Type or press the Browse... button and select the source path.
- Press Start zipping my code.
- A compressed zip archive will be created alongside your source.
You have the option to add, edit, and remove exclude patterns by expanding Options.
The Console Part
The application has, in addition to the UI, a command line interface:
C:\>ZipMyCode /?
ZipMyCode version 1.0.2.0
Copyright © 2009
Usage:
ZipMyCode path [/c:file] [/e:value[+value]] [/o:file]
ZipMyCode /u
Strip and compress:
/c, Specifies the configuration file with exclude
/configuration patterns, one pattern per line. If file is
specified, the default exclude patterns are ignored.
/e, /exclude Specifies the list of exclude patterns. If patterns are
specified, the default exclude patterns are ignored.
/o, /output Specifies the name of the compressed output file.
Uninstallation:
/u, /uninstall Removes saved settings, i.e. cleans
up the application footprint.
Help:
/?, /h, /help Displays this help text.
The interface deliberately takes a single path as argument, because you are then capable of dropping any folder on top of the application (or shortcut, if you make one) using Windows drag-and-drop, since the first argument passed to the application is the dropped folder path.
Grains of Gold
Built Using MVVM and the Dialog Service
The application is built using MVVM, which seems to be on everybody's lips for the moment. Opening dialogs is accomplished with the dialog service from my previous article: Showing Dialogs When Using the MVVM Pattern. This time, the service features a FolderBrowserDialog
instead of a OpenFileDialog
. The idea of a having a service opening the dialogs still holds for these small applications, no clouds on the horizon yet. Feel free to give the article a glance, and give me your opinion on the idea.
New Way of Writing Properties
The ViewModels are implemented with a new way of writing properties. We usually write a ViewModel property like this:
private string someText;
public string SomeText
{
get { return someText; }
set
{
if (someText != value)
{
someText = value;
OnPropertyChanged("SomeText");
}
}
}
With a little help from some methods, we now can write:
public string SomeText
{
get { return Property(() => SomeText); }
set { Property(() => SomeText, value); }
}
We no longer have to write the repetitive setter code, and Visual Studio IntelliSense is better at picking up this format than the first containing a string in the OnPropertyChanged
method.
The code supporting this new format exists in ViewModelBase
:
/// <summary>
/// Gets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <returns>The property value if existing; otherwise default.</returns>
protected T Property<T>(Expression<Func<T>> nameExpression)
{
PropertyItem p;
if (properties.TryGetValue(nameExpression.ToString(), out p))
{
return (T)p.Value;
}
return default(T);
}
/// <summary>
/// Sets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <param name="value">The value to set.</param>
protected void Property<T>(Expression<Func<T>> nameExpression, T value)
{
// Get the key of the property
string key = nameExpression.ToString();
PropertyItem p;
if (properties.TryGetValue(key, out p))
{
// Make sure the property value has changed
if ((p.Value == null && value == null) ||
(p.Value != null && p.Value.Equals(value)))
{
return;
}
// Set the new value
p.Value = value;
}
else
{
// Create the new property item
p = new PropertyItem
{
Name = GetPropertyName(nameExpression),
Value = value
};
// Add the new propery item
properties.Add(key, p);
}
// Raise property changed event
OnPropertyChanged(new PropertyChangedEventArgs(p.Name));
}
/// <summary>
/// Gets the property name of the expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The name expression.</param>
/// <returns>The property name of the expression.</returns>
private static string GetPropertyName<T>(Expression<Func<T>> nameExpression)
{
UnaryExpression unaryExpression = nameExpression.Body as UnaryExpression;
// Convert name expression into MemberExpression
MemberExpression memberExpression = unaryExpression != null ?
(MemberExpression)unaryExpression.Operand :
(MemberExpression)nameExpression.Body;
return memberExpression.Member.Name;
}
/// <summary>
/// Class wrapping up the essential parts of a property.
/// </summary>
class PropertyItem
{
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
public object Value { get; set; }
}
Algorithm Searching Files and Folders with Exclude Pattern
The .NET framework supports searching for files and folders using DirectoryInfo.GetFiles(string searchPattern)
and DirectoryInfo.GetDirectories(string searchPattern)
, but it doesn't feature one to search using an exclude pattern. When it came to deciding what files and folders are acceptable in a compressed source attachment, I decided that it was easier for a user to state what he/she didn't want rather than specifying what he/she did want.
The following code exists in GetFilesTask
and uses LINQ to solve the problem:
/// <summary>
/// Recursive method finding all files from a directory and its sub-directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file and directory exclude pattern.</param>
private IEnumerable<string> RecursiveGetFiles(DirectoryInfo directory,
string[] excludePatterns)
{
// Find files not matching the exclude patterns
IEnumerable<FileInfo> files =
(from file in directory.GetFiles()
select file)
.Except(GetExcludedFiles(directory, excludePatterns), new FileInfoEqualityComparer());
// Yield files not matching the exclude patterns
foreach (FileInfo file in files)
{
yield return file.FullName;
}
// Find directories not matching the exclude patterns
IEnumerable<DirectoryInfo> subDirectories =
(from subDirectory in directory.GetDirectories()
select subDirectory)
.Except(GetExcludedDirectories(directory, excludePatterns),
new DirectoryInfoEqualityComparer());
// Search files in sub-directories not matching the exclude pattern
foreach (DirectoryInfo subDirectory in subDirectories)
{
// Yield all files not matching the exclude patterns
foreach (string file in RecursiveGetFiles(subDirectory, excludePatterns))
{
yield return file;
}
}
}
/// <summary>
/// Gets the excluded files in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file exclude pattern.</param>
private IEnumerable<FileInfo> GetExcludedFiles(DirectoryInfo directory,
string[] excludePatterns)
{
return
from excludePattern in excludePatterns
from filesMatchingExcludePattern in directory.GetFiles(excludePattern)
select filesMatchingExcludePattern;
}
/// <summary>
/// Gets the excluded sub-directories in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The directory exclude pattern.</param>
private IEnumerable<DirectoryInfo> GetExcludedDirectories(DirectoryInfo directory,
string[] excludePatterns)
{
return
from excludePattern in excludePatterns
from directoriesMatchingExcludePattern in directory.GetDirectories(excludePattern)
select directoriesMatchingExcludePattern;
}
class FileInfoEqualityComparer : IEqualityComparer<FileInfo>
{
public bool Equals(FileInfo x, FileInfo y)
{
return x.FullName.Equals(y.FullName);
}
public int GetHashCode(FileInfo obj)
{
return obj.FullName.GetHashCode();
}
}
class DirectoryInfoEqualityComparer : IEqualityComparer<DirectoryInfo>
{
public bool Equals(DirectoryInfo x, DirectoryInfo y)
{
return x.FullName.Equals(y.FullName);
}
public int GetHashCode(DirectoryInfo obj)
{
return obj.FullName.GetHashCode();
}
}
History
- 1.0.2.0 (20 December 2009)
- Console
- Implemented a console interface
- Implemented uninstall
- UI
- Source path is editable and handles validation
- Changed default path
- Fixed initial focus
- Added additional default ignore patterns
- 1.0.1.0 (25 October 2009)
- Implemented drag and drop with code from WPF Control Development Unleashed by Pavan Podila
- 1.0.0.0 (7 July 2009)
- Initial version