While working on a Silverlight business application, I was startled at the proliferation of value converters within my project. It started out innocent enough. One class named BooleanToVisibilityConverter, and its cousin BooleanInverseToVisibilityConverter. Eventually, there were IValueConverters implementations for entirely non-reuable situations, such as MyEntityCollectionToBrushBasedOnSomeDetailedCriteraConverter.

That's when this idea struck me. To avoid the need to manage a limitless library of value converters, why not create an event-driven value converter that delegates the conversion to a listener, such as a user control or page. Implementing the solution turned out to be fairly simple.


First, I created an EventArgs derivative to carry the converter's method arguments and collect the result, like so:

public class ValueConvertingEventArgs : EventArgs
{
   
public ValueConvertingEventArgs(
       
object value,
       
Type targetType,
       
object parameter,
       
CultureInfo culture)
   
{
       
this.Value = value;
       
this.TargetType = targetType;
       
this.Parameter = parameter;
       
this.Culture = culture;
   
}

   
public object Value { get; private set; }
   
public Type TargetType { get; private set; }
   
public object Parameter { get; private set; }
   
public CultureInfo Culture { get; private set; }
   
public object Result { get; set; }
}

Next, I implemented an IValueConverter that "forwards" the Convert and ConvertBack handling to event listeners, as shown below:

public class DelegatedValueConverter : IValueConverter
{
   
public event EventHandler<ValueConvertingEventArgs> Converting;
   
public event EventHandler<ValueConvertingEventArgs> ConvertingBack;

   
public object Convert(
       
object value,
       
Type targetType,
       
object parameter,
       
CultureInfo culture)
   
{
       
if (DesignerProperties.IsInDesignTool)
        return null;

        var handler = this.Converting;
       
if (handler == null)
            throw new InvalidOperationException(
"Subscription to 'Converting' is required.");

       
var args = new ValueConvertingEventArgs(
value
,
 targetType
,
 parameter
,
 culture
);
        handler
(this, args);
       
return args.Result;
    }

   
public object ConvertBack(
       
object value,
       
Type targetType,
       
object parameter,
       
CultureInfo culture)
   
{
       
if (DesignerProperties.IsInDesignTool)
        return null;

        var handler = this.ConvertingBack;
       
if (handler == null)
            throw new InvalidOperationException(
"Subscription to 'ConvertingBack' is required.");

       
var args = new ValueConvertingEventArgs(
value,
 targetType,
 parameter,
 culture);
        handler
(this, args);
       
return args.Result;
    }
}

This implementation throws an InvalidOperationException if the related event does not have any subscribers. After the event executes, the method returns the value of the Result property on the event arguments. The snippets below show the value converter in action:

XAML:

<navigation:Page.Resources>
   
<local:DelegatedValueConverter
       
x:Key="DelegatedConverter"
       
Converting="OnConverting" />
</navigation:Page.Resources>

...

<TextBlock Text={Binding Stuff, Converter={StaticResource DelegatedConverter}} />

C# Code-Behind:

private void OnConverting(object sender, ValueConvertingEventArgs e)
{
    e
.Result = string.Format(e.Culture, "Hello, {0}", e.Value);
}

With the DelegatedValueConverter in tow, I was able to reduce the overall number of IValueConverter implementations in my project. The new converter allowed the page to take responsibility for conversions each time it seemed appropriate. I still make new value converter classes if they seem to have general reusability; however, for one-offs or page-specific conversions, this approach meets the conversion need without flooding the assembly with converter classes.

Happy Coding!


 
Categories: C# | Silverlight

Sample code: Noc.Demo.Clipboard.zip (163.36 KB)

I came across an interesting problem, the other day. The task at had was to copy an image to the Clipboard and maintain the alpha channel for pasting into applications such as Word, PowerPoint, Gimp, or Paint.NET.

What boggles my mind is: this is actually somewhat... well... hard.

Clipboard handling that the .NET Framework forgot

Don't get me wrong. Getting an image onto the clipboard is somewhat easy. It's a one-liner, really:

Clipboard.SetImage(myImage);

The problem with this approach is that the image loses its alpha channel, converting an image like this:

... to an image like this:

 

The resulting image is a 24-bit per pixel bitmap, with the transparent background filled by a soft gray. I have it on good report that older versions of windows swap out the transparent color for a virulent blue.

Now, I've seen applications cop out of transparency by using black or white, or even a gut wrenching magenta, for the intent of masking out the default color at run time. But gray? Why not try to be right some of the time instead of being wrong all of the time. Who ever really wants to swap out transparency for soft gray?

Covering up the gray with a little color

We all know someone who does it. Age creeps up and it's off to the salon for some dye.

When transparency is not strictly a requirement, a little bit of code can get us on the way to filling an image with a solid background. For this, I prefer the following extension method:

public static Image CreateOpaqueBitmap(this Image image, Color backgroundColor)
{
   
var bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
   
using (var graphics = Graphics.FromImage(bitmap))
   
{
        graphics
.Clear(backgroundColor);
        graphics
.DrawImage(image, 0, 0, image.Width, image.Height);
   
}

   
return bitmap;
}

With the extension method in hand, adding an opaque image to the clipboard was never easier:

using (var bitmap = image.CreateOpaqueBitmap(Color.Magenta))
{
   
Clipboard.Clear();
   
Clipboard.SetImage(bitmap);
}

But I really need transparency!

Creating opaque images really only massages an ache, but really doesn't solve the problem. What can be done about transparency?

A complete answer requires a little understanding of Windows clipboard formats. It helps to think of clipboard data as a set of key-value pairs. The key identifies what format the data is expected to be, and the value is the actual data. The clipboard can have multiple kinds of data, all representing (supposedly) the same thing, at any given time.

Some applications can take advantage of this fact to provide text data along with an image, so that pastes will provide something if pasting in a text editor, and something else if pasted in an image editor.

What is more often the case is that an application will provide multiple image formats to increase compatibility with other image software. As a matter of fact, Windows provides free type conversions between some of the most common image types (e.g. Bitmap to System.Drawing.Bitmap).

It helps to get a feel for what formats are supported for pasting in an application and their relative priority by inspecting what formats are generated when copying from the application. Here are formats generated when copying images from a few favorite applications:

  • GIMP: PNG, DeviceIndependentBitmap, System.Drawing.Bitmap, Bitmap, Format17
  • Paint.NET: System.Drawing.Bitmap, Bitmap, PaintDotNet.MaskedSurface
  • Fireworks: Fireworks Internal Clipboard Format 3.0, PNG, DeviceIndependentBitmap
  • MS Paint: Embed Source, Object Descriptor, MetaFilePict, DeviceIndependentBitmap
  • Print Screen (on the keyboard): System.Drawing.Bitmap, Bitmap, DeviceIndependentBitmap, Format17
  • PhotoScape: System.Drawing.Bitmap, Bitmap, DeviceIndependentBitmap, Format17
  • MS Word: Art::GVML ClipFormat, System.Drawing.Bitmap, Bitmap, PNG, JFIF, GIF, EnhancedMetafile, MetaFilePict, Object Descriptor
  • MS PowerPoint: Preferred DropEffect, InShellDragLoop, PowerPoint 12.0 Internal Shapes, Object Descriptor, Art::GVML ClipFormat, PNG, JFIF, GIF, System.Drawing.Bitmap, Bitmap, EnhancedMetafile, MetaFilePict, PowerPoint 12.0 Internal Theme, PowerPoint 12.0 Internal Color Scheme

Adding transparency with PNG.

It would seem that DeviceIndependentBitmap, Bitmap, and System.Drawing.Bitmap are the most common formats in the group; however, they are 24-bit opaque bitmaps. Generating a transparent clipboard data type requires that we properly encode data using a format that supports an alpha channel.

In the applications above, the PNG (Portable Network Graphics) format is generated by GIMP, Fireworks, Word, and PowerPoint. We can hope for transparency success in a broad array of applications merely by placing PNG-formatted data on the clipboard. Lucky for us, it doesn't take much to write:

using (var stream = new MemoryStream())
{
    image
.Save(stream, ImageFormat.Png);
   
var data = new DataObject("PNG", stream);

   
Clipboard.Clear();
   
Clipboard.SetDataObject(data, true);
}

Adding transparency with 32-bit ARGB bitmap

Not every application supports PNG formats. It is probably a good idea to have a secondary format at hand to deal with transparency. It is possible to get a 32bpp image into the clipboard with a little bit of work.

The format at hand, Format17 (a.k.a. CF_DIBV5)

I almost hate to bring it up, really, mostly because so many applications either mishandle it, or ignore it altogether. Take the print screen utility, for example. It does generate valid 32bpp Format17 content, but zeroes all the alpha bytes, and keeps a zero-ing byte mask for alpha just in case. In other words, it may be using a transparency-enabled format, but the content isn't transparent. GIMP mishandles the BITMAPV5HEADER, by accidentally omitting the bV5SizeImage field that is responsible for identifying how many bits are in the bitmap. (In fairness to GIMP, a clever application could compute the value based on other fields in the header.) Paint.NET appears to disregard formats other than 24bpp bitmaps and its own internal MaskedSurface format. Both PhotoScape and GIMP either disregard Format17 altogether (in favor of Bitmap formats) or mishandle the alpha, giving paste results like this:

So, with "You probably won't find this useful," as a disclaimer, let's see how it's done.

The first method is a utility method that copies a 32bpp image into global memory using interop and a little native marshalling. Basically, the method creates a bitmap header and marshals it out into memory, then adds the image bits, last row first, after the header. Note that all the interop methods and constants are omitted for brevity.

private static IntPtr CreatePackedDIBV5(this Bitmap bitmap)
{
   
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
   
uint bufferLen = (uint)(Marshal.SizeOf(typeof(BITMAPV5HEADER)) + bmData.Height * bmData.Stride);
   
IntPtr hMem = Kernel32.NativeMethods.GlobalAlloc(User32.GHND | User32.GMEM_DDESHARE, bufferLen);
   
IntPtr packedDIBV5 = Kernel32.NativeMethods.GlobalLock(hMem);

    BITMAPV5HEADER bmi
= (BITMAPV5HEADER)Marshal.PtrToStructure(packedDIBV5, typeof(BITMAPV5HEADER));
    bmi
.bV5Size = (uint)Marshal.SizeOf(typeof(BITMAPV5HEADER));
    bmi
.bV5Width = bmData.Width;
    bmi
.bV5Height = bmData.Height;
    bmi
.bV5Planes = 1;
    bmi
.bV5BitCount = 32;
    bmi
.bV5Compression = User32.BI_BITFIELDS;
    bmi
.bV5SizeImage = (uint)(bmData.Height * bmData.Stride);
    bmi
.bV5XPelsPerMeter = 0;
    bmi
.bV5YPelsPerMeter = 0;
    bmi
.bV5ClrUsed = 0;
    bmi
.bV5ClrImportant = 0;
    bmi
.bV5RedMask = 0x00FF0000;
    bmi
.bV5GreenMask = 0x0000FF00;
    bmi
.bV5BlueMask = 0x000000FF;
    bmi
.bV5AlphaMask = 0xFF000000;
    bmi
.bV5CSType = 0x73524742; // User32.LCS_WINDOWS_COLOR_SPACE;
    bmi
.bV5Endpoints.ciexyzBlue.ciexyzX = 0;
    bmi
.bV5Endpoints.ciexyzBlue.ciexyzY = 0;
    bmi
.bV5Endpoints.ciexyzBlue.ciexyzZ = 0;
    bmi
.bV5Endpoints.ciexyzGreen.ciexyzX = 0;
    bmi
.bV5Endpoints.ciexyzGreen.ciexyzY = 0;
    bmi
.bV5Endpoints.ciexyzGreen.ciexyzZ = 0;
    bmi
.bV5Endpoints.ciexyzRed.ciexyzX = 0;
    bmi
.bV5Endpoints.ciexyzRed.ciexyzY = 0;
    bmi
.bV5Endpoints.ciexyzRed.ciexyzZ = 0;
    bmi
.bV5GammaRed = 0;
    bmi
.bV5GammaGreen = 0;
    bmi
.bV5GammaBlue = 0;
    bmi
.bV5ProfileData = 0;
    bmi
.bV5ProfileSize = 0;
    bmi
.bV5Reserved = 0;
    bmi
.bV5Intent = User32.LCS_GM_IMAGES;
   
Marshal.StructureToPtr(bmi, packedDIBV5, false);

   
long offsetBits = bmi.bV5Size;
   
IntPtr bits = (IntPtr)(packedDIBV5.ToInt32() + offsetBits);
   
for (int y = 0; y < bmData.Height; y++)
   
{
       
IntPtr DstDib = (IntPtr)(bits.ToInt32() + (y * bmData.Stride));
       
IntPtr SrcDib = (IntPtr)(bmData.Scan0.ToInt32() + ((bmData.Height - 1 - y) * bmData.Stride));
       
for (int x = 0; x < bmData.Width; x++)
       
{
           
Marshal.WriteInt32(DstDib, Marshal.ReadInt32(SrcDib));
           
DstDib = (IntPtr)(DstDib.ToInt32() + 4);
           
SrcDib = (IntPtr)(SrcDib.ToInt32() + 4);
       
}
   
}

    bitmap
.UnlockBits(bmData);
   
Kernel32.NativeMethods.GlobalUnlock(hMem);
   
return hMem;
}

With the utility method at hand, adding an image to the clipboard using CF_DIBV5 (Format17) formatting is as easy as:

public static void Copy32BppBitmapToClipboard(this Image image)
{
   
using (var bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
   
{
       
using (var bitmapGraphics = Graphics.FromImage(bitmap))
       
{
            bitmapGraphics
.DrawImage(image, 0, 0, image.Width, image.Height);
       
}

       
var packedDIBV5 = CreatePackedDIBV5(bitmap);
       
User32.NativeMethods.OpenClipboard(IntPtr.Zero);
       
User32.NativeMethods.EmptyClipboard();
       
User32.NativeMethods.SetClipboardData(User32.CF_DIBV5, packedDIBV5);
       
User32.NativeMethods.CloseClipboard();
   
}
}

Bringing it home

I consider the 32bpp CF_DIBV5 format to be a lot of work, considering the poor support of the format offered by so many applications. I've found that taking a pragmatic approach, aiming for PNG transparency where supported, and accepting the background color of my choice otherwise, made for a manageable codebase with decent application compatibility. I use the following extension method to add images to the clipboard:

public static void CopyMultiFormatBitmapToClipboard(this Image image)
{
   
using (var opaque = image.CreateOpaqueBitmap(Color.White))
   
using (var stream = new MemoryStream())
   
{
        image
.Save(stream, ImageFormat.Png);

       
Clipboard.Clear();
       
var data = new DataObject();
        data
.SetData(DataFormats.Bitmap, true, opaque);
        data
.SetData("PNG", true, stream);
       
Clipboard.SetDataObject(data, true);
   
}
}

The results are fairly decent. Here are a few of the applications I tried:

  • GIMP: transparency supported
  • Fireworks: transparency supported
  • PhotoScape: white background
  • Paint.NET: white background
  • MS PowerPoint: transparency supported
  • MS Word: white background
  • MS Paint: white background

Happy coding!


Noc.Demo.Clipboard.zip (163.36 KB)


 
Categories: C# | Extension Methods

I ran two different benchmark tests to demonstrate different approaches for updating a WinForms TextBox. I built a simple form a rich text box. On separate runs, I filled the text box with text as shown below:

// The += approach:

richTextBox1.Text = string.Empty;
for (int i = 0; i < 1000; i++)
{
    richTextBox1
.Text += "Hello, World!\r\n";
}

// The StringBuilder approach:

richTextBox2.Text = string.Empty;
var builder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    builder
.AppendLine("Hello, World!\r\n");
    richTextBox2
.Text = builder.ToString();
}

The AppendText() approach

richTextBox3.Text = string.Empty;
for (int i = 0; i < 1000; i++)
{
    richTextBox3
.AppendText("Hello, World!");
}

And the results on my old machine?

  • += Approach: 10.7470703 seconds.
  • StringBuilder: 10.7353515 seconds.
  • AppendText(): 0.7041015 seconds.

I ran another benchmark. I filled a text box with 10MB of dummy text, then used the three approaches to append "Hello, World!" to the text box.

And the results of this?

  • += Approach: 11.7470703 seconds.
  • StringBuilder: 12.0615235 seconds.
  • AppendText(): 0.1142578 seconds.

Conclusion: It is much faster to append to a text box using AppendText() than to assign text using string concatenation or from an intermediate data store such as s StringBuilder. Also note that the update rate drops substantially (9 per second) when the text box contained 10MB of text. Depending on the circumstances, a form designer might use multi-threading and caching (via StringBuilder) of text in chunks to reduce the frequency of updates required in the text box.

Note that the .NET text box is a wrapper of the Win32 text box which doesn't use immutable strings, thus giving it this edge in appending performance.


 
Categories: C# | Windows Forms

Welcome to part 3 in the Powerful Extension Methods series. This article takes a look at a common class in ASP.NET, the System.Web.UI.WebControls.ListItemCollection. The ListItemCollection is used in many places: the ListBox, DropDownList, CheckBoxList, and RadioButtonList all store their items in this type of collection.

ListItemCollection implements IEnumerable, but not the strongly typed IEnumerable<ListItem>. Many collections throughout the base class libraries are that way. One of the disadvantages of implementing only IEnumerable is that when you want to process the collection using LINQ, you must first cast the collection to the desired type. Something like this:

items.Cast<ListItem>()

The Cast<TResult>() function gets called implicitly in LINQ expressions such as this:

var selectedItems = from ListItem item in items
                    where item.Selected
                    select item;

The query feels slightly less elegant when written using the LINQ methods directly.

var selectedItems = items.Cast<TResult>().Where(item => item.Selected);

Adding a quick extension method like the one below allows calls to the Where<TResult>() method without including the inelegant call to Cast<TResult>().

/// <summary>
/// Filters a <c>ListItemCollection</c> based on a predicate.
/// </summary>
/// <param name="items">The <c>ListItemCollection</c> to filter.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns>An enumerable set of list items filtered by the predicate.</returns>
public static IEnumerable<ListItem> Where(this ListItemCollection items, Func<ListItem, bool>
                                          predicate)
{
  return items.Cast<ListItem>().Where(predicate);
}

This allows the simpler call:

var selectedItems = items.Where(item => item.Selected);

The ASP.NET ListItemCollection does not ship with a SelectedItems or SelectedIndices property. We can elegantly compensate for the missing property with two short extension methods.

/// <summary>
/// Filters a <c>ListItemCollection</c> based on whether the item is selected.
/// </summary>
/// <param name="items">The <c>ListItemCollection</c> to filter.</param>
/// <returns>An enumerable set of list items filtered by whether the item is selected.</returns>
public static IEnumerable<ListItem> WhereSelected(this ListItemCollection items)
{
  return items.Cast<ListItem>().Where(i => i.Selected);
}

/// <summary>
/// Filters a <c>ListItemCollection</c> based on whether the item is not selected.
/// </summary>
/// <param name="items">The <c>ListItemCollection</c> to filter.</param>
/// <returns>An enumerable set of list items filtered by whether the item is not selected.</returns>
public static IEnumerable<ListItem> WhereNotSelected(this ListItemCollection items)
{
  return items.Cast<ListItem>().Where(i => !i.Selected);
}

Often times, it is not the list items themselves that we are interested in, but only their values. A few brief extension methods make for an elegant way to retrieve SelectedValues.

/// <summary>
/// Filters a <c>ListItemCollection</c> based on whether the item is selected, and retrieves each
value.
/// </summary>
/// <param name="items">The <c>ListItemCollection</c> to filter.</param>
/// <returns>An enumerable set of list items filtered by whether the item is selected.</returns>
public static IEnumerable<string> ValuesWhereSelected(this ListItemCollection items)
{
  return items.Cast<ListItem>().Where(i => i.Selected).Select(i => i.Value);
}

/// <summary>
/// Filters a <c>ListItemCollection</c> based on whether the item is not selected, and retrieves
each value.
/// </summary>
/// <param name="items">The <c>ListItemCollection</c> to filter.</param>
/// <returns>An enumerable set of list items filtered by whether the item is not selected.</returns>
public static IEnumerable<string> ValuesWhereNotSelected(this ListItemCollection items)
{
  return items.Cast<ListItem>().Where(i => !i.Selected).Select(i => i.Value);
}

Collecting selected values is helpful, but setting selected values is also helpful. The next two extension methods enable selecting multiple items provided a list of values or a Lambda expression predicate.

/// <summary>
/// Sets each element in the collection as selected based on whether the item is contained in the
set of values.
/// </summary>
/// <param name="items">The items to update.</param>
/// <param name="values">The values to select. All other items will be deselected.</param>
public static void SetAllSelectedByValue(this ListItemCollection items, IEnumerable<string> values)
{
  var hashValues = new HashSet<string>(values);
  if (values == null)
  {
    items.SetAllSelectedIf(i => false);
  }
  else
  {
    items.SetAllSelectedIf(i => hashValues.Contains(i.Value));
  }
}

/// <summary>
/// Sets each element in the collection as selected based on a predicate.
/// </summary>
/// <param name="items">The items to update.</param>
/// <param name="predicate">A function to test each element for a condition, and update the selected
                status of the element to the test result.</param>
public static void SetAllSelectedIf(this ListItemCollection items, Func<ListItem, bool> predicate)
{
  foreach (ListItem item in items)
  {
    item.Selected = predicate(item);
  }
}

These few extension methods don't perform any earth-shaking operations, but they do provide a clean, elegant way to operate on ListItemCollection objects.

Happy Coding!


 
Categories: ASP.NET | C# | Extension Methods

Welcome to part 2 of the Powerful Extension Methods series. Today, we take a look at XML serialization. If you have ever used the .NET XmlSerializer class, you know how powerful it is. Decorate your class with appropriate attributes, and you can generate or parse complex XML structures without having to write any XML parsing code.

To find out more about .NET and XML serialization, check out the documentation here. If you want to see excellent examples of the XmlSerializer in action, check out the code of DasBlog and look at what they do with RSS and Atom feeds. It's pretty neat.

It doesn't take long, working with the XmlSerializer, before the good ol' DRY principle drives us to create serialization helper classes, like Andrew Gunn created. Utility classes are usually perfect candidates for extension methods.

So here we go: one extension method for serializing any object (if it is not serializable, expect an exception), and a generic method each for deserializing streams, strings, and XmlNodes.

/// <summary>
/// This class provides extension methods related to serialization.
/// </summary>
public static class SerializationExtensions
{
  /// <summary>
  /// Serializes an object as XML.
  /// </summary>
  /// <param name="value">The object to serialize.</param>
  /// <returns>An XML string representing the serialized object.</returns>
  public static string SerializeAsXml(this object value)
  {
      var ser = new XmlSerializer(value.GetType());
      using (var stream = new MemoryStream())
      using (var xmlwriter = new XmlTextWriter(stream, Encoding.UTF8))
      {
          ser.Serialize(xmlwriter, value);
          var buffer = stream.GetBuffer();

          // skip the byte order mask
          return Encoding.UTF8.GetString(buffer, 3, buffer.Length - 3);
      }
  }

  /// <summary>
  /// Deserializes the specified string using an XML serializer.
  /// </summary>
  /// <typeparam name="T">The type of object to deserialize.</typeparam>
  /// <param name="stream">The stream to deserialize.</param>
  /// <returns>A deserialized object of the specified type.</returns>
  public static T Deserialize<T>(this Stream stream)
  {
    using (var reader = new XmlTextReader(reader))
    {
      var ser = new XmlSerializer(typeof(T));
      return (T)ser.Deserialize(reader);
    }
  }

  /// <summary>
  /// Deserializes the specified string using an XML serializer.
  /// </summary>
  /// <typeparam name="T">The type of object to deserialize.</typeparam>
  /// <param name="xml">The XML to deserialize.</param>
  /// <returns>A deserialized object of the specified type.</returns>
  public static T Deserialize<T>(this string xml)
  {
    using (var reader = new StringReader(xml))
    {
      var ser = new XmlSerializer(typeof(T));
      return (T)ser.Deserialize(reader);
    }
  }

  /// <summary>
  /// Deserializes the specified node using an XML serializer.
  /// </summary>
  /// <typeparam name="T">The type of object to deserialize.</typeparam>
  /// <param name="node">The node containing XML to deserialize.</param>
  /// <returns>A deserialized object of the specified type.</returns>
  public static T Deserialize<T>(this XmlNode node)
  {
    using (var reader = new StringReader(node.OuterXml))
    {
      var ser = new XmlSerializer(typeof(T));
      return (T)ser.Deserialize(reader);
    }
  }
}

Happy Coding!


 
Categories: C# | Extension Methods

Welcome to part 1 of the Powerful Extension Methods series. The extension method concept is an excellent feature of the latest breeds of the .NET framework. They allow us to abstract away static utility classes in a way that makes the utility methods appear to belong to the object they operate upon. Beginners can check things out here and here for a good introduction.

I do WinForms programming from time to time. In any Windows application, whether through traditional WinForms or through WPF, managing event handling in the face of threading is critical. Exceptions are thrown when code updates any UI Control property from any thread except the thread that created the control. This problem surfaces when some background thread needs to update the UI.

This led to a lot of creative solutions involving InvokeRequired and Invoke. One solution involves creating an event on the threaded object which the UI can subscribe to. The OnMyEvent method of the object handles invoking each event handler methods on the thread from which they subscribed. The full idea is described here.

It only took me an event handler or two before the DRY principle screamed for attention. Enter, the SafeInvoke extension method:

/// <summary>
/// Raises the event asynchronously.
/// </summary>
/// <param name="handler">The handler.</param>
/// <param name="source">The source.</param>
/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
public static void SafeInvoke(this EventHandler handler, object source, EventArgs args)
{
  if (handler != null)
  {
    foreach (EventHandler singleCast in handler.GetInvocationList())
    {
      var syncInvoke = singleCast.Target as ISynchronizeInvoke;
      try
      {
        if (syncInvoke != null && syncInvoke.InvokeRequired)
        {
          syncInvoke.Invoke(singleCast, new object[] { source, args });
        }
        else
        {
          singleCast(source, args);
        }
      }
      catch
      {
        // Do nothing. The event handler may have been detached asynchronously.
      }
    }
  }
}

With this extension method, we can construct our threaded object to look something like this:

public class MyWorker
{
  public event EventHandler SomeEvent;

  protected virtual void OnSomeEvent(EventArgs e)
  {
    this.SomeEvent.SafeInvoke(this, e);
  }
}

You can see that using this pattern, the event will manage on its own the process of ensuring the event is handled on the subscribing threads. No more threading exceptions when updating the UI from a background worker!

Another common event delegate that deserves a similar extension method is the generic EventHandler<T> class. The model of the function is no surprise; but, here it is in full.

/// <summary>
/// Raises the event asynchronously.
/// </summary>
/// <typeparam name="T">The type of the event handler arguments</typeparam>
/// <param name="handler">The handler.</param>
/// <param name="source">The source.</param>
/// <param name="args">The arguments containing the event data.</param>
public static void SafeInvoke<T>(this EventHandler<T> handler, object source, T args)
where T : EventArgs { if (handler != null) { foreach (EventHandler<T> singleCast in handler.GetInvocationList()) { var syncInvoke = singleCast.Target as ISynchronizeInvoke; try { if (syncInvoke != null && syncInvoke.InvokeRequired) { syncInvoke.Invoke(singleCast, new object[] { source, args }); } else { singleCast(source, args); } } catch { // Do nothing. The event handler may have been detached asynchronously. } } } }

Happy Coding!


 
Categories: C# | Extension Methods

July 30, 2008
@ 09:33 PM

ASP.NET control collections are finicky. They are the most difficult to handle when trying to modify a control collection from a scope outside of that control. The mischievous error sneaked up on me some time ago. It manifests itself with the following error:

"The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases."

While fiddling around for a friend I came across some interesting behavior. Perhaps it is best to just show the code and disclaim my ignorance.

Suppose I have an ASP.NET 2.0 application containing two user controls implementing an interface IReadyable.

public interface IReadyable
{
  event EventHandler Ready;
}

Each user control contains handling code to satisfy the interface, like so:

public event EventHandler Ready;

protected override void OnLoad(EventArgs e)
{
  this.OnReady(EventArgs.Empty);
}

private void OnReady(EventArgs e)
{
  if (Ready != null)
  {
    this.Ready(this, e);
  }
}

The idea is that we can use the Ready event to signal a time to swap user controls in and out of our page. Allright, now to set up the straw man. We have a page that contains a placeholder. At some point, one of our user controls is added to the control collection of the placeholder. When the OnLoad event of the control fires, the handler activates the Ready event as well.

Our application code responds to the ready event by swapping the user control for another. This causes the peculiar exception.

/// <summary>
/// This method wires up the event handlers early in the page lifecycle,
/// but not early enough to allow us to modify some control collections.
/// </summary>
protected void Page_Load(object sender, EventArgs e)
{
  base.OnInit(e);
  IReadyable readyable = (IReadyable)this.LoadControl("~/MailControl01.ascx");
  readyable.Ready += new EventHandler(readyable_Ready);
  this.placeHolder.Controls.Add((Control)readyable);
}

/// <summary>
/// This method does not work because it is invoked too late in the page lifecycle.
/// </summary>
void readyable_Ready(object sender, EventArgs e)
{
  this.placeHolder.Controls.Clear(); // <-- An exception is thrown here.
  IReadyable readyable = (IReadyable)this.LoadControl("~/MailControl02.ascx");
  readyable.Ready += new EventHandler(again_Ready);
  this.placeHolder.Controls.Add((Control)readyable);
}

/// <summary>
/// Stub for an event handler.
/// </summary>
void again_Ready(object sender, EventArgs e)
{

}

Notice the first line in the readyable_Ready method?

Now for the interesting behavior. I say interesting because the error states that control collections are unmodifyable during the Init and Load phase; however, if you swap Page_Load for an override on the page's OnInit method, and swap the user control's OnLoad override for an OnInit override, no exception occurs. Everything works just fine. I suppose there are some arcane rules I am unaware of.

Granted, in my day-to-day development, I avoid the entire issue by creating custom server controls. I make each control responsible for its own control collection and ignorant of anything outside. It seems a control can modify its own collection any time it pleases, although there are some costs to doing it late in the page lifecycle. I found this page from Microsoft helpful: http://msdn.microsoft.com/en-us/library/ms178472.aspx.

I hope this turns out helpful for someone. A full demo is attached here: ControlCollections.zip (6.21 KB)

Happy coding.


 
Categories: ASP.NET | C#

Have you ever started out writing code one way and later discover that another way is much, much better? It happens to me daily. I face a difficulty, sometimes, when I want to refactor my code into a more concise, elegant form, but some of my teammates already use the existing methods.

One neat feature of C# is the ability to decorate methods and other structures in code. A useful decoration for this situation is the Obsolete decoration. Here are several examplse:

[Obsolete]
void ObsoleteMethod1() { }

[Obsolete("This is why we don't use this...")]
void ObsoleteMethod2() { }

[Obsolete("If you use this, there will be compile errors", true)]
void ObsoleteMethod3() { }

The description comes in handy because it will show up in the tooltip when typing in the method in Visual Studio. This ensures that it is not necessary for developers to have source code to detect whether a method should be discontinued.

Descriptions specified in the Obsolete decoration appear in the tooltip

You can also forcefully terminate the use of the method without blindly removing it. The third sample demonstrates this. Using the ObsoleteMethod3 method would cause a compile-time error. This provides a good opportunity to discontinue a method and provide an explanation as to why it is no longer used.

Happy coding!


 
Categories: C#

Copyright ©2006 By Keith Rimington

Screenshot Image - LineCounter counts the lines in files and places the results for each file in a DataGridView

Introduction

If you have ever wanted to know how quickly you write code, you have probably counted lines. As programs get more involved and work is shared among a team, it is difficult to know how many lines are "owned" by you.

Lines of code may not be the best way to measure performance (I am imagining a classroom full of monkeys striking Enter, Enter, Enter... ). But sometimes is satisfying to say, "Look what was done!"

This articles explores basic file IO, how to manually store results in a DataGridView, and basic event handling.

Wiring up the GUI

The GUI has just a few components. A TextBox provides a place where we can enter the file extensions we are interested in counting. A DataGridView provides a place to show results. A CheckBox provides us some options and a StatusStrip helps us summarize results to the user.

Use the Anchor property to adjust how each Control "Clings" to the walls of the Form. This allows us to resize the window to get a better view if we have long filenames or a large number of results. Anchoring is one of the convenient layout tools builtin to the environment.

New controls in the form editor make changing Anchoring a breeze.

Now we will wire up the columns in the DataGridView control. The form editor makes this a breeze. Highlight the DataGridView and select from the properties Columns.

The properties editor makes creating data columns easy.

We now have a dialog that can be used to generate our columns. Clicking on the Add button opens an additional dialog that can be used to create columns. Choose a name and header text, and the column type of DataGridViewTextBoxColumn. Other interesting column properties include FillWeight, which affects how much priority the column receives during layout.

Individual column properties can be adjusted in these dialogs.

The next step to giving the GUI functionality is to add event handlers. For example, when we click on the Go button, we want the application to find files that match our search criteria, open them, and count lines.

We can use the form editor to add this handler easily. Just double-click on the Go button, and the event handler is added for us, like so:

  private void goButton_Click(object sender, EventArgs e)
  {
    // TODO: Count files here...
  }

Also, the function is registered with the event delegate in the designer.cs file. This is what tells the runtime that we want to run the goButton_Click method when the goButton.Click event is fired.

  this.goButton.Click += new System.EventHandler(this.goButton_Click);

Because there may be files found by our program that we want to ignore, we will wire up the DataGridView to gather information about selected files only. We can then pick and choose which results we want to tally up for our final line count.

Using the form editor, we set the MultiSelect property to True and the SelectionMode property to FullRowSelect. All that's left is adding an event handler to respond to changes in the selection. Find the SelectionChanged property in the form editor and double-click to generate the following method:

  private void dgvResult_SelectionChanged(object sender, EventArgs e)
  {
    // TODO: Count the selection here.
  }

Which is wired up in the designer.cs file like so:

  this.dgvResult.SelectionChanged +=
      new System.EventHandler(this.dgvResult_SelectionChanged);

A lot of changes can happen by playing with the properties in the form editor, but lets move on to the meat of the project, cranking out our own lines of code.

Using a StreamReader to count lines

Reading a file from the system is very easy in C#. The key objects involved are File and StreamReader. The static method File.OpenText opens a file specified by a local or fully-qualified path name, and generates a StreamReader object to digest the file contents. StreamReader is built with the ReadLine method; perfect for our task.

  private int CountLinesInFile(string filename)
  {
    StreamReader sr = File.OpenText(filename);
    int count = 0;
    while (sr.ReadLine() != null)
      count++;
    sr.Close();
    return count;
  }

Finding Files in a Directory

Our next task is to determine which files we should open. We will make use of the System.Collections.Generic.List<> class to contain our filenames. The following code snippet demonstrates how we collect all the filenames that match the requested extensions.

  private void CountFiles()
  {
    // Repository for discovered filenames
    List<string> filenames = new List<string>();

    // Running linecount total
    int total = 0;

    // The Split method is a quick and dirty way to parse a string.
    string[] extensions = txtExts.Text.Split("" "".ToCharArray());

    foreach (string ext in extensions)
    {
      // The third parameter of this overload allows us to
      // automatically inspect subdirectories.  Default is
      // SearchOption.TopDirectoryOnly, and the argument
      // could have been omitted, but is included here for
      // information's sake.
      filenames.AddRange(
        chkSubDir.Checked ?
        Directory.GetFiles(
            Directory.GetCurrentDirectory(),
            ""*."" + ext,
            SearchOption.AllDirectories) :
        Directory.GetFiles(
            Directory.GetCurrentDirectory(),
            ""*."" + ext,
            SearchOption.TopDirectoryOnly));
    }

    // ...

Our next task is to open each file, count the lines, and add the results to the DataGridView. We will use the DataGridView.Rows.Add(object[] params) method to add each new row. With this method, each object should correspond, in order, to the columns of the DataGridView.

    // ...

    // Wipe out any previous results
    dgvResult.Rows.Clear();

    // No that we have all the filenames,
    // open the files to count their lines.
    foreach (string filename in filenames)
    {
      int count = CountLinesInFile(filename);
      total += count;

      // This line adds a new row to the DataGridView.
      // We choose the string.Substring method to display
      // only the relative path of the file
      dgvResult.Rows.Add(
        filename.Substring(Directory.GetCurrentDirectory().Length+1),
        count.ToString());
    }

    // Add the total to the bottom
    dgvResult.Rows.Add(
       new object[] { ""Total:"", total.ToString() });
  }

Call this method from the goButton_Clicked event handler and we have a line counter!

Using MultiSelect with the DataGridView

To wrap things up, we will add functionality to ignore some of the results, if we wish, in our total. The following code snippet counts the results in the selected rows, and writes the results to the StatusStrip below.

  private void CountSelection()
  {
    int selected = 0;
    int total = 0;
    foreach (DataGridViewRow row in dgvResult.Rows)
    {
      try
      {
        // Ignore rows that are either not selected
        // or are the total row.  Because some ""filler""
        // rows exist, use the catch block to effectively
        // ignore the invalid data in those rows.
        if (row.Selected &&
            row.Cells[0].Value.ToString() != ""Total:"")
        {
          selected++;
          total += int.Parse(row.Cells[1].Value.ToString());
        }
      }
      catch { }
    }

    // Update the status label with the results
    lblFileCt.Text = selected.ToString() + "" files selected"";
    lblLineCt.Text = total == 0 ?
        ""No lines"" :
        total.ToString() + "" lines"";
  }

Conclusion

This article just scratches the surface of file IO and data controls like the DataGridView. I hope you found this article useful. This article was published on CodeProject


 
Categories: C#