The blog of dlaa.me

Blogging code samples stays easy [Update to free ConvertClipboardRtfToHtmlText tool and source code for Visual Studio 2010!]

A big part of my blog is sharing code and so most of the posts I write include sample source. Therefore, it's pretty important to me that the code I share be easy for readers to understand and use. For me, that means I want it to be static, syntax-highlighted, and in text form so it's copy+paste-able and indexable by search engines.

I'm a big fan of keeping things simple and avoiding dependencies, so I ended up writing a very simple tool about two years ago called ConvertClipboardRtfToHtmlText. As you can see from the original ConvertClipboardRtfToHtmlText post and the subsequent follow-up, it's a very simple tool intended for a very specific scenario. That said, it works beautifully for my purposes and I've used it to blog every snippet of source code since then!

So I was surprised and a little disappointed when it stopped working recently... Why? Because I switched to Visual Studio 2010 (Beta 2) and they've changed the RTF clipboard format slightly with that release. Now, while I'm sure VS 2010's RTF is still 100% legal RTF, it's different enough from VS 2008's output that ConvertClipboardRtfToHtmlText chokes on it. Clearly, it was time to dust off the source code and make it work again! :)

Not surprisingly, the update process was quite painless - and by tweaking the code slightly, I've arrived at an implementation that works well for both versions of Visual Studio: 2008 and 2010! The source code download includes a VS 2010 solution and project, but takes advantage of the multi-targeting capabilities of Visual Studio to compile for the .NET 2.0 Framework, ensuring that the resulting executable runs pretty much everywhere.

As long as I was touching the code, I added the following simple banner text:

ConvertClipboardRtfToHtmlText
Version 2009-12-19 - http://blogs.msdn.com/delay/

Converts the Visual Studio 2008/2010 RTF clipboard format to HTML by replacing
the RTF clipboard contents with its HTML representation in text form.

Instructions for use:
1. Copy syntax-highlighted text to the clipboard in Visual Studio
2. Run ConvertClipboardRtfToHtmlText (which has no UI and exits immediately)
3. Paste HTML text into an editor, web page, blog post, etc.

So if you're in the market for a nice way to blog code and you're using Visual Studio 2008 or 2010, maybe ConvertClipboardRtfToHtmlText can help you out!

 

[Click here to download the ConvertClipboardRtfToHtmlText tool along with its complete source code.]

 

Here's the complete source code for ConvertClipboardRtfToHtmlText, provided by - you guessed it! - ConvertClipboardRtfToHtmlText:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

// NOTE: This is NOT a general-purpose RTF-to-HTML converter! It works well
// enough on the input I've tried, but may break in other scenarios.
// TODO: Convert into a real application with a notify icon and hotkey.
namespace ConvertClipboardRtfToHtmlText
{
    static class ConvertClipboardRtfToHtmlText
    {
        private const string colorTbl = "\\colortbl;";
        private const string colorFieldTag = "cf";
        private const string tabExpansion = "    ";

        [STAThread]
        static void Main()
        {
            Console.WriteLine("ConvertClipboardRtfToHtmlText");
            Console.WriteLine("Version 2009-12-19 - http://blogs.msdn.com/delay/");
            Console.WriteLine();
            Console.WriteLine("Converts the Visual Studio 2008/2010 RTF clipboard format to HTML by replacing");
            Console.WriteLine("the RTF clipboard contents with its HTML representation in text form.");
            Console.WriteLine();
            Console.WriteLine("Instructions for use:");
            Console.WriteLine("1. Copy syntax-highlighted text to the clipboard in Visual Studio");
            Console.WriteLine("2. Run ConvertClipboardRtfToHtmlText (which has no UI and exits immediately)");
            Console.WriteLine("3. Paste HTML text into an editor, web page, blog post, etc.");
            if (Clipboard.ContainsText(TextDataFormat.Rtf))
            {
                // Create color table, populate with default color
                List<Color> colors = new List<Color>();
                Color defaultColor = Color.FromArgb(0, 0, 0);
                colors.Add(defaultColor);

                // Get RTF
                string rtf = Clipboard.GetText(TextDataFormat.Rtf);

                // Strip meaningless "\r\n" pairs (used by VS 2008)
                rtf = rtf.Replace("\r\n", "");

                // Parse color table
                int i = rtf.IndexOf(colorTbl);
                if (-1 != i)
                {
                    i += colorTbl.Length;
                    while ((i < rtf.Length) && ('}' != rtf[i]))
                    {
                        // Add color to color table
                        SkipExpectedText(rtf, ref i, "\\red");
                        byte red = (byte)ParseNumericField(rtf, ref i);
                        SkipExpectedText(rtf, ref i, "\\green");
                        byte green = (byte)ParseNumericField(rtf, ref i);
                        SkipExpectedText(rtf, ref i, "\\blue");
                        byte blue = (byte)ParseNumericField(rtf, ref i);
                        colors.Add(Color.FromArgb(red, green, blue));
                        SkipExpectedText(rtf, ref i, ";");
                    }
                }
                else
                {
                    throw new NotSupportedException("Missing/unknown colorTbl.");
                }

                // Find start of text and parse
                i = rtf.IndexOf("\\fs");
                if (-1 != i)
                {
                    // Skip font size tag
                    while ((i < rtf.Length) && (' ' != rtf[i]))
                    {
                        i++;
                    }
                    i++;

                    // Begin building HTML text
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("<pre><span style='color:#{0:x2}{1:x2}{2:x2}'>",
                        defaultColor.R, defaultColor.G, defaultColor.B);
                    while (i < rtf.Length)
                    {
                        if ('\\' == rtf[i])
                        {
                            // Parse escape code
                            i++;
                            if ((i < rtf.Length) &&
                                (('{' == rtf[i]) || ('}' == rtf[i]) || ('\\' == rtf[i])))
                            {
                                // Escaped '{' or '}' or '\'
                                sb.Append(rtf[i]);
                            }
                            else
                            {
                                // Parse tag
                                int tagEnd = rtf.IndexOf(' ', i);
                                if (-1 != tagEnd)
                                {
                                    if (rtf.Substring(i, tagEnd - i).StartsWith(colorFieldTag))
                                    {
                                        // Parse color field tag
                                        i += colorFieldTag.Length;
                                        int colorIndex = ParseNumericField(rtf, ref i);
                                        if ((colorIndex < 0) || (colors.Count <= colorIndex))
                                        {
                                            throw new NotSupportedException("Bad color index.");
                                        }

                                        // Change to new color
                                        sb.AppendFormat(
                                            "</span><span style='color:#{0:x2}{1:x2}{2:x2}'>",
                                            colors[colorIndex].R, colors[colorIndex].G,
                                            colors[colorIndex].B);
                                    }
                                    else if ("tab" == rtf.Substring(i, tagEnd - i))
                                    {
                                        sb.Append(tabExpansion);
                                    }
                                    else if ("par" == rtf.Substring(i, tagEnd - i))
                                    {
                                        sb.Append("\r\n");
                                    }

                                    // Skip tag
                                    i = tagEnd;
                                }
                                else
                                {
                                    throw new NotSupportedException("Malformed tag.");
                                }
                            }
                        }
                        else if ('}' == rtf[i])
                        {
                            // Terminal curly; done
                            break;
                        }
                        else
                        {
                            // Normal character; HTML-escape '<', '>', and '&'
                            switch (rtf[i])
                            {
                                case '<':
                                    sb.Append("&lt;");
                                    break;
                                case '>':
                                    sb.Append("&gt;");
                                    break;
                                case '&':
                                    sb.Append("&amp;");
                                    break;
                                default:
                                    sb.Append(rtf[i]);
                                    break;
                            }
                        }
                        i++;
                    }

                    // Trim any trailing empty lines
                    while ((2 <= sb.Length) && ('\r' == sb[sb.Length - 2]) && ('\n' == sb[sb.Length - 1]))
                    {
                        sb.Length -= 2;
                    }

                    // Finish building HTML text
                    sb.Append("</span></pre>");

                    // Update the clipboard text
                    Clipboard.SetText(sb.ToString());
                }
                else
                {
                    throw new NotSupportedException("Missing text section.");
                }
            }
        }

        // Skip the specified text
        private static void SkipExpectedText(string s, ref int i, string text)
        {
            foreach (char c in text)
            {
                if ((s.Length <= i) || (c != s[i]))
                {
                    throw new NotSupportedException("Expected text missing.");
                }
                i++;
            }
        }

        // Parse a numeric field
        private static int ParseNumericField(string s, ref int i)
        {
            int value = 0;
            while ((i < s.Length) && char.IsDigit(s[i]))
            {
                value *= 10;
                value += s[i] - '0';
                i++;
            }
            return value;
        }
    }
}

Wrap music [A more flexible balanced WrapPanel implementation for Silverlight and WPF!]

In my last post, I told the story of a customer who asked for an easy way to make the Silverlight/WPF WrapPanel use all available space to spread its children out evenly instead of bunching them up together. The following sample shows off the default WrapPanel behavior on top - and my alternate BalancedWrapPanel behavior on the bottom:

BalancedWrapPanel, Horizontal, ItemWidth and ItemHeight set

The default WrapPanel behavior fills each horizontal (or vertical) line as much as it can before moving on to the next line, but leaves any extra space at the end of each line. BalancedWrapPanel began as a copy of the WrapPanel code (available as part of the Silverlight Toolkit) and contains a modified copy of one of the helper methods that instead distributes the unsightly chunk of extra space evenly through the entire column (or row). That was what I set out to do with BalancedWrapPanel, so I was fairly happy with the results. Unfortunately, the customer wasn't 100% satisfied...

In particular, the desire was for those items in the last line to align with the items above instead of centering like they do in my initial implementation. It's a perfectly reasonable request - and something I thought about when I first started on BalancedWrapPanel! But things are a little tricky because those orderly columns only show up when the ItemWidth and/or ItemHeight properties are set. In fact, the WrapPanel code doesn't actually have any concept of columns at all! Rather, the columns you see are a natural consequence of the algorithm laying out lots of constant-width items within constant-width bounds. So the columns are very real, but the code doesn't really know anything about them. And they don't even exist when ItemWidth/ItemHeight aren't set; despite each column of this vertically-oriented BalancedWrapPanel being vertically balanced, there are no overall rows in the horizontal direction because all the elements are different sizes:

BalancedWrapPanel, Vertical, ItemWidth and ItemHeight not set

When I was first thinking about this scenario, it seemed to me that I'd need to add some code to track the columns and then do things differently for the last line in order to keep everything aligned properly. I was afraid this additional code would overly complicate the original sample, and decided not to implement it until and unless someone asked. Besides, it's called BalancedWrapPanel, so it seemed natural that everything should be balanced! :)

But now that I had a specific request, I thought more carefully and realized that not only was it easy to align the last items, but that it was also a tad more efficient to do so! I didn't want to change the current behavior of BalancedWrapPanel (because I think that's what people expect), but I wanted to enable the new aligning behavior, too. So I added a new property to align the last items, but it only works when ItemWidth/ItemHeight are set (otherwise it has no effect because items can be all different sizes and don't line up to begin with). I considered trying to explain this technicality in the name of the new property, but everything I came up with was long and cumbersome. So the new property is simply named AlignLastItems - setting it to True changes the first example to look like this instead:

BalancedWrapPanel, Horizontal, ItemWidth and ItemHeight set, AlignLastItems set

Notice how the basic WrapPanel behavior is maintained, but the items are spread out evenly and there are no gaping holes. And there you have it - a balanced WrapPanel implementation that should work for most common scenarios. What's more, the customer is satisfied and maybe other folks will start using BalancedWrapPanel in their projects, too!

 

Click here to download the source code for BalancedWrapPanel and the Silverlight/WPF demo application.

 

PS - Please refer to my previous BalancedWrapPanel post for information about why I coded it like I did along with some other details.

PPS - As I mention above, the changes from what I'd already written were surprisingly minimal. Other than adding the AlignLastItems DependencyProperty, the only differences are highlighted below:

private void ArrangeLine(int lineStart, int lineEnd, double? directDelta, double directMaximum, double indirectOffset, double indirectGrowth)
{
    Orientation o = Orientation;
    bool isHorizontal = o == Orientation.Horizontal;
    UIElementCollection children = Children;
    double directLength = 0.0;
    double itemCount = 0.0;
    double itemLength = isHorizontal ? ItemWidth : ItemHeight;

    if (AlignLastItems && !itemLength.IsNaN())
    {
        // Length is easy to calculate in this case
        itemCount = Math.Floor(directMaximum / itemLength);
        directLength = itemCount * itemLength;
    }
    else
    {
        // Make first pass to calculate the slack space
        itemCount = lineEnd - lineStart;
        for (int index = lineStart; index < lineEnd; index++)
        {
            // Get the size of the element
            UIElement element = children[index];
            OrientedSize elementSize = new OrientedSize(o, element.DesiredSize.Width, element.DesiredSize.Height);

            // Determine if we should use the element's desired size or the
            // fixed item width or height
            double directGrowth = directDelta != null ?
                directDelta.Value :
                elementSize.Direct;

            // Update total length
            directLength += directGrowth;
        }
    }

    // Determine slack
    double directSlack = directMaximum - directLength;
    double directSlackSlice = directSlack / (itemCount + 1.0);
    double directOffset = directSlackSlice;

    // Make second pass to arrange items
    for (int index = lineStart; index < lineEnd; index++)
    {
        // Get the size of the element
        UIElement element = children[index];
        OrientedSize elementSize = new OrientedSize(o, element.DesiredSize.Width, element.DesiredSize.Height);

        // Determine if we should use the element's desired size or the
        // fixed item width or height
        double directGrowth = directDelta != null ?
            directDelta.Value :
            elementSize.Direct;

        // Arrange the element
        Rect bounds = isHorizontal ?
            new Rect(directOffset, indirectOffset, directGrowth, indirectGrowth) :
            new Rect(indirectOffset, directOffset, indirectGrowth, directGrowth);
        element.Arrange(bounds);

        // Update offset for next time
        directOffset += directGrowth + directSlackSlice;
    }
}

That's a WrapPanel and I am outta here... [A balanced WrapPanel implementation for Silverlight and WPF!]

A customer contacted me a few days ago asking whether there was an easy way to make the Silverlight/WPF WrapPanel use all available space to spread its children out evenly instead of bunching them up against each other as it usually does. Instead of trying to explain what I mean by that, please have a look at the top half of the following screen shot:

BalancedWrapPanel, Horizontal, ItemWidth and ItemHeight set

The default WrapPanel behavior fills each horizontal (or vertical) line as much as it can before moving on to the next line - but it leaves all the extra space at the end of the line. That big, vertical gap of empty space at the right of the colored boxes is a bit unsightly, so what would be nice is if WrapPanel distributed the extra space across the entire line - kind of like you see in the bottom half of the window above!

My reply to the customer was that I didn't know of a way to do this with WrapPanel as-is, but that it should be pretty straightforward to modify the code and add the balancing logic. Well, I got curious on the bus yesterday, so I went ahead and implemented BalancedWrapPanel, the control I used in the second example above.

 

Click here to download the source code for BalancedWrapPanel and the Silverlight/WPF demo application.

 

To implement this alternate behavior, I started with the WrapPanel source code that comes with the Silverlight Toolkit. For WPF 3.5 (or Silverlight 4), I needed to copy WrapPanel.cs, OrientedSize.cs, and NumericExtensions.cs. For Silverlight 3 (which doesn't have LengthConverter) I also needed to copy LengthConverter.cs and TypeConverters.cs. I renamed "WrapPanel" to "BalancedWrapPanel" everywhere, linked the copied files into a new Visual Studio solution containing sample projects for Silverlight 3 and WPF 3.5, and compiled successfully. After that, it was just a matter of tweaking the code a bit, and I was done!

I've highlighted my additions to the existing helper method below:

private void ArrangeLine(int lineStart, int lineEnd, double? directDelta, double directMaximum, double indirectOffset, double indirectGrowth
{
    Orientation o = Orientation;
    bool isHorizontal = o == Orientation.Horizontal;
    UIElementCollection children = Children;

    // Make first pass to calculate the slack space
    double directLength = 0.0;
    for (int index = lineStart; index < lineEnd; index++)
    {
        // Get the size of the element
        UIElement element = children[index];
        OrientedSize elementSize = new OrientedSize(o, element.DesiredSize.Width, element.DesiredSize.Height);

        // Determine if we should use the element's desired size or the
        // fixed item width or height
        double directGrowth = directDelta != null ?
            directDelta.Value :
            elementSize.Direct;

        // Update total length
        directLength += directGrowth;
    }

    // Determine slack
    double directSlack = directMaximum - directLength;
    double directSlackSlice = directSlack / (lineEnd - lineStart + 1.0);
    double directOffset = directSlackSlice;

    // Make second pass to arrange items
    for (int index = lineStart; index < lineEnd; index++)
    {
        // Get the size of the element
        UIElement element = children[index];
        OrientedSize elementSize = new OrientedSize(o, element.DesiredSize.Width, element.DesiredSize.Height);

        // Determine if we should use the element's desired size or the
        // fixed item width or height
        double directGrowth = directDelta != null ?
            directDelta.Value :
            elementSize.Direct;

        // Arrange the element
        Rect bounds = isHorizontal ?
            new Rect(directOffset, indirectOffset, directGrowth, indirectGrowth) :
            new Rect(indirectOffset, directOffset, indirectGrowth, directGrowth);
        element.Arrange(bounds);

        // Update offset for next time
        directOffset += directGrowth + directSlackSlice;
    }
}

That's all there is to it: a few simple changes, a wave of the magic compiler wand, and POOF! a more pleasing layout for both platforms.

Woot! :)

 

Notes:

  • I could have refactored the method above for a slightly more efficient solution, but decided the code would be easier to understand if I changed as little as possible and kept the edits distinct.

  • Alternatively, I could have subclassed WrapPanel and implemented my changes in ArrangeOverride. This would have had the benefit of requiring fewer files from the Silverlight Toolkit, but would have required somewhat more code on my part. I didn't see a particularly compelling argument for either option, so I chose copy+edit because it demonstrates how really easy it is to reuse code from the Silverlight Toolkit, because it's more flexible in general, and because it makes it easy to add further enhancements in the future.

  • The example screen shot above uses WrapPanel's ItemWidth and ItemHeight properties to specify that all items should take up the same space. That seemed like the most likely case for someone who wants to use the new balancing behavior. However, the changes I've made work just as well when these properties are unset (i.e., NaN). They also work well when using a vertically-oriented BalancedWrapPanel - as the following screen shot shows:

    BalancedWrapPanel, Vertical, ItemWidth and ItemHeight not set
  • If you download the source code and build for Silverlight 4, you'll get the following warning because Silverlight 4 adds support for LengthConverter:

    warning CS0436: The type 'System.Windows.LengthConverter' in '...\LengthConverter.cs' conflicts with the imported type 'System.Windows.LengthConverter' in '...\System.Windows.Controls.Toolkit.dll'.

    You can either ignore the warning or remove the files LengthConverter.cs and TypeConverters.cs from the project.

Normal booting is old school [Windows 7 tricks detailed: USB key install, VHD creation, and native VHD boot/dual-boot!]

Windows 7 can boot and run in some ways that are a little surprising when you first learn about them. One example is that it can seamlessly install from any sufficiently large USB key; another is that it can natively boot VHD files. Neither of these is hard to configure and both have already been discussed by other web sites. However, it has been my experience that some of the relevant information on the web is confusing, misleading, or incomplete. So in the interest of saving others - and myself! - some trouble, I explain a few interesting scenarios below with exact steps and brief notes on what each step does. (That way, if anything goes wrong, troubleshooting is a lot easier.)

If you already do this kind of thing and have a process that's working for you, there's probably little here that's new. But if you've been thinking of getting your feet wet with any of this, maybe I can help make things a little easier! :)

 

Notes:

  • The tasks described here are potentially dangerous and can result in the complete loss of your data if done improperly. Always back up your data first, think about what you're doing, check your work, and otherwise take sensible precautions! While I've done my best to ensure the steps below work as intended, I can offer no guarantee they'll work the same under all conditions. Caveat emptor.
  • Things you type look like this - things you do look like [this] - things you need to replace when you type them (like drive letters that vary by machine) look like this.
  • Unless otherwise noted, all tasks should be carried out on a machine running Windows 7.
  • Each task is self-contained and can be done independently of the others.
  • Although I refer to Windows 7 everywhere, Windows Server 2008 R2 supports these same scenarios as well.
  • While it's possible to apply BitLocker drive encryption to a natively-booted VHD, the host drive (i.e., the drive containing the VHD) can not be encrypted. (And if you're going to use BitLocker, you should be aware that the default location for pagefile.sys of a natively-booted VHD is on the host disk outside the VHD.)
  • Here are some good resources for more information:

 

Installing Windows 7 (or Vista) from a USB Key

Besides making it possible to install Windows on machines without a DVD drive, installing from a USB key has a big advantage: it's fast. All you need is a blank USB key that's big enough to hold everything on the DVD (typically about 3GB of data). Then follow these easy steps and - BAM! - install runs faster than ever!

  1. [Open a Command Prompt as administrator]

    Open the Start Menu, expand "All Programs", "Accessories", right-click on "Command Prompt", then "Run as administrator".

  2. [Insert the USB key]

    Give the system a moment to identify it.

  3. diskpart

    Run the interactive disk partitioning tool.

  4. list disk

    Display the available disks.

  5. select disk #

    Select the USB key; identify it using the "Size" column. USB keys will usually be near the bottom of the list, especially if they've just been plugged-in (when they're likely to be last).

  6. list disk

    Look for the '*' identifying the selected disk and be sure you've selected the right one because the next step will delete all data on that disk.

  7. clean

    Remove all partition/formatting information from the selected disk.

  8. create partition primary

    Create a primary partition on the selected disk.

  9. format quick

    Format the new partition of the selected disk with the default file system.

  10. active

    Mark the new partition on the selected disk active and bootable.

  11. assign

    Assign a drive letter to the new volume of the selected disk.

  12. list volume

    Display the available volumes. Look for the '*' identifying the newly created volume.

  13. exit

    Exit the interactive disk partitioning tool.

  14. robocopy W:\ U:\ /e

    Copy the entire contents of the Windows 7 (or Vista) DVD in drive W to the USB key at drive U.

  15. [Safely remove/unplug the USB key]

    This key can now be inserted in any machine that supports booting from USB (nearly all of them do these days). The experience will be just the same as if the original DVD were used - but it runs considerably faster!

    Note: You may need to hit a special key as the machine starts to tell it to boot from the USB key instead of the internal hard drive - it's usually one of F2/F12/DEL/ESC, but check the manual if you're not sure.

 

Create a VHD containing an up-to-date Windows 7 image

Installing from USB may be fast, but what's even faster is not having to install at all! If you'll be running Windows 7 in Windows Virtual PC, Hyper-V, or natively from a VHD, it's convenient to start out with an image that already has the bits in the right places and the latest security patches applied. What's cool is that there's a script to make creating one of these VHDs easy: WIM2VHD by Mike Kolitz. Start by going to that web site and following the directions to download WIM2VHD and its dependencies before carrying out the steps below.

  1. [Open a Command Prompt as administrator]

    Open the Start Menu, expand "All Programs", "Accessories", right-click on "Command Prompt", then "Run as administrator".

  2. [Change to the directory containing WIM2VHD.wsf]

    For convenience, this directory should also contain all the QFE patches that will be applied.

  3. cscript WIM2VHD.wsf
      /wim:W:\sources\install.wim
      /sku:ULTIMATE
      /disktype:fixed
      /size:10000
      /vhd:Windows7Ultimate.vhd
      /qfe:Windows6.1-KB973525-x86.msu,Windows6.1-KB974332-x86.msu,Windows6.1-KB974431-x86.msu,Windows6.1-KB974455-x86.msu,Windows6.1-KB974571-x86.msu,Windows6.1-KB975364-x86.msu,Windows6.1-KB975467-x86.msu,Windows6.1-KB976749-x86.msu
    

    Create a VHD named Windows7Ultimate.vhd from the original Windows 7 DVD in drive W using the Ultimate SKU, a fixed size disk of 10GB, and with the listed QFEs pre-applied.

    Note: I'm using the x86 DVD here, so I'm providing the x86 versions of the relevant security patches. This should all work the same for 64-bit, but I prefer 32-bit because it's smaller and works pretty much everywhere.

    Note: You can create a dynamic disk (which starts small and grows as necessary) by omitting the italic /disktype and /size options above. That's going to be faster and easier to deal with for Virtual PC and Hyper-V - but for native VHD boot a fixed size is easier and performs better. That's what I'm going to do in the next task, so I've specified a small, fixed disk above. (Please refer to the FAQ for more detail.)

    Note: Per the FAQ, "Native boot from VHD is only available with Windows 7 Enterprise, Windows 7 Ultimate and all versions of Windows Server 2008 R2."

 

Configure a clean machine for native VHD booting

Is it possible to boot a machine without a "real" operating system? Yes!

The steps below will clean a machine and configure it to boot into a VHD image from a nearly-empty hard drive. While there are some sensible reasons to do this (e.g., implementing poor-man's undo disks or making it easy to transfer a pre-configured Windows 7 install around), this is mainly just a cool way to show off. :) Rumor has it that these steps can even be used to create a USB key that hosts and boots a running copy of Windows 7, though I don't have a USB key large enough to try myself. (And besides, that would probably wear out the USB key's flash memory quite quickly.)

Note: If you already have Windows 7 installed on a machine, and want to add an additional boot option for VHD, please scroll down to the next task instead.

  1. [Boot the machine from the Windows 7 DVD or a Windows 7 USB key created by the steps above]

    Load a simple shell that can be used to make low-level changes to the disk.

  2. [Wait for the "Install Windows" dialog to display]

    Allow the system to boot completely.

  3. [Optional: Plug in an external USB drive containing the VHD image]

    If you're booting from the DVD or using a USB key that's too small to hold the 10GB VHD image, you can store the VHD file on a separate USB hard disk. Plug that disk in now, and give the system a moment to find it and assign it a drive letter.

  4. [Press Shift+F10]

    Open an interactive command prompt with administrator permissions

  5. diskpart

    Run the interactive disk partitioning tool.

  6. list disk

    Display the available disks.

  7. select disk #

    Select the primary hard disk; it will usually be at index 0.

  8. list disk

    Look for the '*' identifying the selected disk and be sure you've selected the right one because the next step will delete all data on that disk.

  9. clean

    Remove all partition/formatting information from the selected disk.

  10. create partition primary

    Create a primary partition on the selected disk.

  11. format quick

    Format the new partition of the selected disk with the default file system.

  12. active

    Mark the new partition on the selected disk active and bootable.

  13. assign

    Assign a drive letter to the new volume of the selected disk.

  14. list volume

    Display the available volumes. Look for the '*' identifying the newly created volume.

  15. exit

    Exit the interactive disk partitioning tool.

  16. copy E:\Windows7Ultimate.vhd C:\

    Copy the VHD file Windows7Ultimate.vhd from (external) drive E to the now-empty primary hard drive C. This may take a little while...

  17. diskpart

    Run the interactive disk partitioning tool.

  18. select vdisk file=C:\Windows7Ultimate.vhd

    Select the VHD file Windows7Ultimate.vhd just copied to the primary hard drive C. Make sure not to reference the VHD file on the removable disk because that disk won't be available in the future.

  19. attach vdisk

    Attach the selected virtual disk to the system.

  20. list volume

    Display the available volumes. Look for the new virtual disk volume; identify it using the "Size" column (it will probably be the last one listed).

  21. exit

    Exit the interactive disk partitioning tool.

  22. bcdboot V:\Windows /s C:

    Configure primary hard disk drive C to boot into the copy of Windows installed on virtual drive V (the newly attached virtual disk volume).

  23. exit

    Close the Command Prompt window.

  24. [Close the "Install Windows" dialog by clicking the 'X' in the upper-right corner]

    Cancel the install process.

  25. [Confirm you want to cancel the install process]

    Yes, really cancel the install process.

  26. [Wait while the machine automatically reboots]

    Allow the machine to boot into the new VHD image. (Note: This may require unplugging the USB key and/or external USB drive.) After booting into the VHD image, Windows will run through the last stages of setup (e.g., user name, time zone, etc.) and finalize the install.

    Enjoy your new VHD-based Windows!

 

Add native VHD booting to a machine with Windows 7

If you already have a machine with Windows 7 installed (perhaps via the previous set of steps), you can modify it to boot a separate instance of Windows 7 from VHD.

  1. [Open a Command Prompt as administrator]

    Open the Start Menu, expand "All Programs", "Accessories", right-click on "Command Prompt", then "Run as administrator".

  2. copy E:\Windows7Ultimate.vhd C:\

    Copy the VHD file Windows7Ultimate2.vhd from (any) drive E to drive C which should be the machine's primary hard disk. (It will already contain a VHD file if you're continuing along from the previous task.) Make sure drive C does not correspond to a VHD-based disk.

  3. bcdedit /copy {default} /d "Windows 7 VHD"

    Creates a copy of the default boot configuration with the name Windows 7 VHD. Note the GUID that is returned by this command; use it in place of GUID in the following commands.

  4. bcdedit /set GUID device vhd=[C:]\Windows7Ultimate2.vhd

    Set the device for the new boot configuration to the Windows7Ultimate2.vhd file on drive C. (Note: Use the "[C:]\..." syntax exactly as shown above with the square brackets.)

  5. bcdedit /set GUID osdevice vhd=[C:]\Windows7Ultimate2.vhd

    Set the OS device for the new configuration to the Windows7Ultimate2.vhd file on drive C. (Note: Use the "[C:]\..." syntax exactly as shown above with the square brackets.)

  6. [Reboot and choose the new ""Windows 7 VHD" option]

    Boot into the new VHD.

    Note: If that doesn't work, please have a look at the end of this document and try the "detecthal on" step. I haven't found this to be necessary, so I haven't listed it - but if others find that it's helpful, I'll call that out.

Tags: Technical

Two birds, squared! [Silverlight/WPF Data Visualization Development Release 3 and a DataVisualizationDemos update]

We shipped the November 2009 release of the Silverlight Toolkit a little over a week ago and it includes a handful of improvements to the Data Visualization assembly. It also adds support for the new Silverlight 4 Beta! And while we were busy getting the October/November Toolkits out the door, the WPF team previewed WPF 4 along with the .NET 4 and Visual Studio 2010 Beta!

Which means there are now four platforms of interest to developers: Silverlight 3, Silverlight 4, WPF 3.5, and WPF 4. And the Silverlight/WPF Data Visualization assembly supports them all!

 

Silverlight/WPF Data Visualization Development Release 3

As with previous Data Visualization Development Releases, I've updated to the most recent Toolkit code. And like last time, the Silverlight Toolkit shipped most recently so the code in the new Development Release is identical to what just went out with the Silverlight 3/4 Toolkits. However, people using Data Visualization on WPF 3.5 or 4 can take advantage of the latest changes by updating to the binaries included with this Development Release or by compiling the corresponding code themselves.

[Click here to download the SilverlightWpfDataVisualization solution including complete source code and pre-compiled binaries for all four platforms.]

Notes:

  • This is the first release I know of that supports four different platforms with distinct implementations on each. While the code and XAML are 99+% identical across the platforms, each has at least one customization that makes it unique. [Bonus points for identifying them all! :) ]
  • Previously, there was a single Visual Studio 2008 SilverlightWpfDataVisualization.sln file for both Silverlight 3 and WPF 3.5. Because Silverlight 4 and WPF 4 use Visual Studio 2010, there's a new SilverlightWpfDataVisualization4.sln file for those two platforms. The two solutions (and the projects within) look and act exactly the same - they're separate because they compile with different tools and because separation lets people who haven't upgraded continue to use the VS 2008 solution.
  • I always strive for code that builds with no compile or code analysis warnings, and that's still the case when compiling for Silverlight 3, WPF 3.5, and Silverlight 4 (though the last is a bit of a cheat because code analysis doesn't work there yet). But the new .NET 4 tools include some improvements, and there were three new kinds of warnings when I first compiled for WPF 4. Two of them were easily addressed with trivial changes I already made, but the third requires a bit more (potentially destabilizing) work that was not done for this release: Warning 2 CA1062 : Microsoft.Design : In externally visible method 'Foo', validate parameter 'Bar' before using it. As it happens, there was already a work item for this task because we knew we weren't checking everywhere we should. So if you see this warning when compiling the WPF 4 assembly, please don't be alarmed!
  • Whereas it used to be fine to wrap platform-specific code in #if SILVERLIGHT blocks, that doesn't work anymore because there are now cross-version changes within each platform. Therefore, I've switched to the form #if !NO_FEATURE instead. It's usually best to avoid double-negatives, but their use here is a consequence of a deliberate decision. My goal is that, by default, the code tries to use every feature it supports and if the target platform doesn't implement something, there's a compile error. At which point it's easy to identify the problem and add the relevant #define to the project file to "turn off" the unsupported feature. This seem better to me than forcing developers to know all the relevant flags and use them to "turn on" features for each platform. Just another example of the pit of success, really... :)
  • WPF 4 includes the Visual State Manager classes in the framework, so the WPF 4 Data Visualization assembly no longer has a dependency on the WPF Toolkit!

 

DataVisualizationDemos on WPF 4

 

DataVisualizationDemos Sample Project Updated

The DataVisualizationDemos application is a collection of all the Data Visualization samples I've posted to my blog. Like the Data Visualization assembly itself, the demo application runs on Silverlight 3 and 4 as well as WPF 3.5 and 4 and shares the same code and XAML across all four platforms. Not only is it a convenient way to look at a variety of sample code, it also has links back to the relevant blog posts for more detail about each sample.

Click here to download the complete source code for the cross-platform DataVisualizationDemos sample application.

Notes:

  • I've followed the same dual-solution and #if approaches that I describe above.
  • Because WPF 4 now supports easing functions, the "Gelatin Sales" example on the "Charting Introduction" tab runs on that platform - as does the entire "Jelly Charting" demo. And boy, is it smooth on WPF! :)

 

With the release of Silverlight/WPF Data Visualization Development Release 3, it's easy for anyone to compile and run the Data Visualization assembly on any of Microsoft's four premier development platforms. So what are you waiting for? ;)

Sharing isn't easy for anyone [Tricks for sharing the same XAML files across Silverlight and WPF]

I casually mentioned two tricks for sharing the same XAML across Silverlight and WPF in the notes of a post a couple weeks ago. I used both techniques in my DataVisualizationDemos project (which compiles for Silverlight and WPF from the same source code) and wanted to call them out for others in similar situations. The feedback I got was quite positive - until Brian Elgaard Bennett tried the subclassing trick with HierarchicalDataTemplate. While there's no reason that shouldn't work, it doesn't.

What's going on is that WPF doesn't support subclassing DataTemplate or any of its children. What's worse, nobody expects this when they bump into it! [I know a few of us have independently reported the same issue to the WPF team - it's kind of like a rite of passage. :) ] So the right people are aware of the problem, and maybe - just maybe - the new XAML parser in .NET 4 will help fix things. But that doesn't matter for WPF 3.5 development today - and because my XAML sharing trick relies on subclassing, the situation seems pretty dire.

 

But I'm stubborn, so I thought about the situation a little and foolishly said I had an idea that should work. Unfortunately - like most of my ideas - it didn't work the first time. Or the second time. Or the third. Or...

I think it was something like the ninety-ninth idea that finally worked.

"Genius is one percent inspiration, ninety-nine percent perspiration." - Thomas Edison

Well, I won't claim this is genius or anything, but the ratio sure seems right. :)

 

[Click here to download the complete source code for the SharingXamlSilverlightWpf sample.]

 

The way I've solved the HierarchicalDataTemplate problem is a lot like the way I went about things before. However, I've implemented it a little differently this time around, so if you want a quick-and-dirty solution and don't care about HierarchicalDataTemplate, then do it the way I did in my previous post. But if you want a more complete, more comprehensive solution, please keep reading...

 

First, here's the XAML we'll be sharing. There's nothing fancy, just some standard controls that are part of the core framework for WPF, and part of the SDK/Toolkit for Silverlight.

<UserControl x:Class="SharingXamlSilverlightWpf_SL.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:systemWindows="clr-namespace:System.Windows;assembly=PresentationFramework"
    xmlns:systemWindowsControls="clr-namespace:System.Windows.Controls;assembly=PresentationFramework">
    <StackPanel>
        <systemWindowsControls:DockPanel>
            <TextBlock Text="Inside a DockPanel"/>
        </systemWindowsControls:DockPanel>

        <systemWindowsControls:Viewbox Height="40">
            <TextBlock Text="Inside a ViewBox"/>
        </systemWindowsControls:Viewbox>

        <systemWindowsControls:TreeView>
            <systemWindowsControls:TreeViewItem
                Header="Inside a TreeView(Item)"
                ItemsSource="{Binding}"
                IsExpanded="True">
                <systemWindowsControls:TreeViewItem.ItemTemplate>
                    <systemWindows:HierarchicalDataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </systemWindows:HierarchicalDataTemplate>
                </systemWindowsControls:TreeViewItem.ItemTemplate>
            </systemWindowsControls:TreeViewItem>
        </systemWindowsControls:TreeView>
    </StackPanel>
</UserControl>
Aside: In the ideal world, we wouldn't need to use an XML namespace prefix under Silverlight and therefore this problem would never come up in the first place. Unfortunately, Silverlight 3 and the Silverlight 4 Beta don't support XmlnsDefinitionAttribute, so the fact that the SDK/Toolkit assemblies already properly implement it doesn't help us.

The interesting thing to note above is that all the relevant controls (DockPanel, Viewbox, TreeView, TreeViewItem, and HierarchicalDataTemplate) have a prefix and that things look just like you'd expect them to under WPF if you went to the trouble of specifying the namespace explicitly. And, in fact, that's all there is for WPF - just be explicit with the XAML and you're done!

So what's the magic that makes this work on Silverlight? Well, it's nothing in the application project there, either - just like with WPF there are no special changes required! However, there is an extra assembly on the Silverlight side...

Before I totally spoil the surprise, here's the code for that assembly:

extern alias SWC;
extern alias SWCT;
using System.Windows.Markup;
using SystemWindows = SWC::System.Windows;
using SystemWindowsControls = SWC::System.Windows.Controls;
using SystemWindowsControlsToolkit = SWCT::System.Windows.Controls;

namespace System.Windows
{
    // Stub class for HierarchicalDataTemplate
    public class HierarchicalDataTemplate : SystemWindows.HierarchicalDataTemplate
    {
    }
}

namespace System.Windows.Controls
{
    // Stub class for DockPanel
    public class DockPanel : SystemWindowsControlsToolkit.DockPanel
    {
    }

    // Stub class for TreeView
    public class TreeView : SystemWindowsControls.TreeView
    {
    }

    // Stub class for TreeViewItem
    public class TreeViewItem : SystemWindowsControls.TreeViewItem
    {
    }

    // Stub class for Viewbox
    // Silverlight's Viewbox is sealed, so simulate it with a ContentControl wrapper
    public class Viewbox : ContentControl
    {
        public Viewbox()
        {
            Template = (ControlTemplate)XamlReader.Load(@"
                <ControlTemplate
                    xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                    xmlns:controls=""clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"">
                    <controls:Viewbox>
                        <ContentPresenter/>
                    </controls:Viewbox>
                </ControlTemplate>");
        }
    }
}

Aside from some extern alias/using stuff at the top to disambiguate references, there's nothing here we didn't see last time around - in fact, the subclassing trick for HierarchicalDataTemplate looks just like it does for the other classes.

But I thought that trick didn't work for HierarchicalDataTemplate?

Right, it doesn't work on WPF - but this assembly is Silverlight-only.

Then you need a matching assembly for WPF or else the project won't compile because of the XMLNS reference.

Unless the corresponding assembly already exists on WPF.

Oh, no you didn't...

Oh, yes I did! :)

 

The Silverlight-only assembly is named PresentationFramework.dll, the same name as the platform assembly that contains the actual implementations of the controls for WPF. The Silverlight platform doesn't have a PresentationFramework.dll, and this latest trick takes advantage of that fact to sneak one in. Because Silverlight doesn't suffer from the same DataTemplate subclassing bug, it's perfectly okay to subclass HierarchicalDataTemplate there.

So the original subclassing trick does 95% of what we want, the control wrapping trick adds Viewbox, and the PresentationFramework trick adds HierarchicalDataTemplate - which means we're 100% covered!

 

Well, at least until someone contacts me to report another problem. Which would no doubt be interesting to debug and I'd certainly want to have a look.

But not right now. Adding that same-named assembly has left me feeling a little slimy and I think I need a shower before I spend more time on this. :)

Silverlight 4 Beta is out - and the Toolkit has it covered! [Silverlight Toolkit November 2009 release now available for Silverlight 3 and 4!]

We've just published the November 09 release of the Silverlight Toolkit in conjunction with today's unveiling of the Silverlight 4 Beta! The Silverlight 4 Beta offers a bunch of neat new features which enable even more compelling online (and offline!) applications. Stuff like bi-directional text, web cam and microphone support, a RichTextArea control, clipboard access, ICommand, an elevated-trust model, printing, notifications, right-click, and more!

Of course, the big news for this post is a new version of the Silverlight Toolkit that's perfectly suited for developing great applications on Silverlight 4. And though it seems like just yesterday that we finished off the October 09 release, there are some neat, new things in today's release for Silverlight 3, as well. You can be confident that the Silverlight Toolkit has your back whichever platform you choose! :)

 

My announcements are usually all about the Toolkit's Data Visualization assembly - and there are some improvements in that area - but it wasn't my primary focus this time around. If you have a look at the official release notes, you'll see there are all kinds of new things across the board. I'm not going to repeat everything here, but I do want to highlight a few things:

Partial Release Notes

  • SL3/SL4: We've added a new control, BusyIndicator, based on the ActivityControl found in the RIA Services project template and samples. BusyIndicator makes it easy to add a "Please wait..." progress indicator to applications and does so in a way that's easy to customize. It's a simple control, but a handy one. I have a few examples below; please see David Poll's blog for more.
  • SL3/SL4: Minor tweaks to Data Visualization: Support more flexible subclassing scenarios of core classes, provide more helpful exception messages in some scenarios, tweak default Chart template to respect Padding, change type of Chart.LegendItems collection items to object for flexibility, convert Legend to a HeaderedItemsControl for consistency.
  • SL3/SL4: It's now possible to collect code coverage statistics when running unit tests with the Silverlight Unit Test Framework! Jeff spent some time this release productizing some clever hackery Ted Glaza originally did about a year ago, and this new capability fills a pretty big hole in the unit testing story for Silverlight. We're also including a couple of the custom build actions that we use internally - now you'll be able to use them in your projects! And we're including the XML files to make it easy for customers to enable Application Library Caching for their projects. For more information on these topics, please have a look at Jeff Wilcox's blog.
  • SL4: We've modified all the Toolkit/SDK control visuals and input handlers to behave correctly for right-to-left cultures and added mouse wheel support in some of the obvious places.
  • SL4: We've removed ImplicitStyleManager because Silverlight 4 supports implicit styles natively. Correspondingly, we've modified the Theme-based wrapper classes (TwilightBlue, ShinyRed, etc.) to use implicit styles.
  • SL4: Viewbox has moved into the Silverlight 4 core (i.e., it's available in the System.Windows.dll assembly that every Silverlight 4 install includes), so it has been removed from the Silverlight 4 Toolkit.
  • SL4: Along with the new MEF (Managed Extensibility Framework) features in the Silverlight 4 SDK, there's an experimental System.ComponentModel.Composition.Packaging.Toolkit assembly in the Toolkit that adds support for downloading secondary XAP’s and loading them into a MEF catalog. For more information, please have a look at Wes Haggard's blog.

 

BusyIndicator is in the house...

At its core, BusyIndicator is a simple wrapper control into which you put whatever makes up the UI of your application. (You can think of it as a special kind of Border with special abilities.) BusyIndicator exposes an IsBusy property which should be set to true (possibly via data binding) whenever the relevant portion of the application is busy and won't respond to user input. When this happens, BusyIndicator automatically disables its content and shows a simple UI to let the user know what's going on. It's really quite simple! :)

Here's the most basic scenario:

<controlsToolkit:BusyIndicator
    IsBusy="{Binding MyBusyProperty}">

    <!-- Content goes here... -->

</controlsToolkit:BusyIndicator>

And this is how it looks when IsBusy is set:

Default BusyIndicator

The most common change is to customize the message, and of course that's simple to do:

<controlsToolkit:BusyIndicator
    IsBusy="{Binding MyBusyProperty}"
    BusyContent="My custom message...">

    <!-- Content goes here... -->

</controlsToolkit:BusyIndicator>

Yielding:

BusyIndicator with custom message

Note that the BusyContent property is of type object, so we could have used other UI elements (like Grid, Image, and Button) for a message with more than just text. Of course, sometimes you want things to be totally custom - so there are some straightforward ways to do that which don't require you to completely re-Template:

<controlsToolkit:BusyIndicator
    IsBusy="{Binding MyBusyProperty}"
    BusyContent="{Binding}">

    <!-- Provide custom UI for busy display -->
    <controlsToolkit:BusyIndicator.BusyContentTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="Downloading Email" FontWeight="Bold" HorizontalAlignment="Center"/>
                <StackPanel Margin="6">
                    <TextBlock Text="{Binding MyStatus}"/>
                    <ProgressBar Value="{Binding MyProgress}" Height="15"/>
                </StackPanel>
                <Button Content="Cancel" HorizontalAlignment="Center"/>
            </StackPanel>
        </DataTemplate>
    </controlsToolkit:BusyIndicator.BusyContentTemplate>

    <!-- Remove unnecessary default ProgressBar -->
    <controlsToolkit:BusyIndicator.ProgressBarStyle>
        <Style TargetType="ProgressBar">
            <Setter Property="Visibility" Value="Collapsed"/>
        </Style>
    </controlsToolkit:BusyIndicator.ProgressBarStyle>

    <!-- Content goes here... -->

</controlsToolkit:BusyIndicator>

Which looks like:

BusyIndicator with custom UI

Another property to be aware of is the DisplayAfter property which lets you configure the initial delay before the busy indicator is shown - to avoid the annoying "on/off" flicker that would otherwise result from a lot of quick operations in succession. The BusyIndicator page of the public sample project has an interactive section where you can experiment with various delays and durations to see how this looks in action.

 

ImplicitStyleManager has left the building...

I wanted to show how to convert an existing application using ImplicitStyleManager over to using Silverlight 4's new implicit styling support. Because I made this change for the 11 Toolkit themes, I can tell you it is quite easy. :) Basically, it's just a matter of removing the ImplicitStyleManager attached property/properties and - where relevant - moving the ResourceDictionary of Styles into the Resources section of the parent element (or all the way up to App.xaml). Also, be sure to go through and remove any explicit assignments to the Style property or it's friends (e.g., ItemContanerStyle). (This probably isn't common in most applications, but the Toolkit Themes did it all over the place.)

To make that a little more concrete, here's a simplified "before" example that makes Buttons have blue text and ListBoxItems purple:

<StackPanel controlsThemingToolkit:ImplicitStyleManager.ApplyMode="OneTime">

    <!-- Styles for controls -->
    <StackPanel.Resources>
        <Style TargetType="Button">
            <Setter Property="Foreground" Value="Blue"/>
        </Style>
        <Style TargetType="ListBoxItem">
            <Setter Property="Foreground" Value="Purple"/>
        </Style>
    </StackPanel.Resources>

    <!-- Styled controls -->
    <Button Content="Button"/>
    <ListBox>
        <ListBoxItem Content="Item 1"/>
        <ListBoxItem Content="Item 2"/>
    </ListBox>

</StackPanel>

To convert this XAML over to Silverlight 4's implicit style support, just remove the highlighted portion above. The visuals will look exactly the same, but everything is more efficient because the platform is handling it internally. And what's more, various scenarios that were tricky to get working with ImplicitStyleManager (like styling the contents of a TabControl) now "just work" thanks to the new framework support for implicit styles!

 

Silverlight 4 has a lot of great improvements that really raise the bar for rich, interactive web applications. I encourage everyone to check it out today and start thinking about how to take advantage of all the new stuff! :) And when you're done with that, please check out the live Toolkit samples for Silverlight 3 or Silverlight 4, download the Toolkit installer(s) for the platform/platforms of your choice, and enjoy!

Creating something from nothing - and knowing it [Developer-friendly virtual file implementation for .NET refined!]

A couple of weeks ago I wrote about VirtualFileDataObject, my developer-friendly virtual file implementation for .NET and WPF. I followed that up by adding support for asynchronous behavior to improve the user experience during long-running operations. Last week, these posts got a shout-out from blogging legend Raymond Chen, whose work provided the inspiration for the project. [Best week ever! :) ] Now it's time for one last tweak to wrap things up!

 

Recall that the Windows APIs for drag-and-drop and clipboard operations deal with objects that implement the IDataObject COM interface. And while the native DoDragDrop function tries to provide a mechanism for the source and target to communicate (via the pdwEffect parameter and the function's return value), there's nothing similar available for the SetClipboardData function. And once you enable asynchronous drag-and-drop, it's clear that the return value of DoDragDrop isn't going to work, either, because the call returns immediately (before the operation has completed). So it seems like there must be some other way for the source and target to share information...

Sure enough, there are some shell clipboard formats specifically for enabling communication between the source and target! For the purposes of this sample, we're interested in using two of them:

And here's another which is relevant enough that I wrote code to support it, though I'll only mention it once more here:

  • CFSTR_PASTESUCCEEDED - Indication that a paste succeeded and what kind of operation (copy/move/link) it did

 

Armed with that knowledge, let's tweak the sample application so one of the scenarios does a move/cut instead of a copy:

VirtualFileDataObjectDemo sample application

The first tweak to the code modifies our helper function to accept a DragDropEffects parameter so the caller can indicate its copy/move preference with the new PreferredDropEffect property in the clipboard scenario:

private static void DoDragDropOrClipboardSetDataObject(MouseButton button, DependencyObject dragSource,
    VirtualFileDataObject virtualFileDataObject, DragDropEffects allowedEffects)
{
    try
    {
        if (button == MouseButton.Left)
        {
            // Left button is used to start a drag/drop operation
            VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, allowedEffects);
        }
        else if (button == MouseButton.Right)
        {
            // Right button is used to copy to the clipboard
            // Communicate the preferred behavior to the destination
            virtualFileDataObject.PreferredDropEffect = allowedEffects;
            Clipboard.SetDataObject(virtualFileDataObject);
        }
    }
    catch (COMException)
    {
        // Failure; no way to recover
    }
}

Then we can tweak the VirtualFileDataObject constructor to pass a custom "end" action that uses the new PerformedDropEffect property to find out what action took place. If the target performed a move (or a cut with the clipboard), then we'll hide the corresponding "button" to reflect that fact:

private void VirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e)
{
    var virtualFileDataObject = new VirtualFileDataObject(
        null,
        (vfdo) =>
        {
            if (DragDropEffects.Move == vfdo.PerformedDropEffect)
            {
                // Hide the element that was moved (or cut)
                // BeginInvoke ensures UI operations happen on the right thread
                Dispatcher.BeginInvoke((Action)(() => VirtualFile.Visibility = Visibility.Hidden));
            }
        });

    // Provide a virtual file (generated on demand) containing the letters 'a'-'z'
    ...

    DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Move);
}

Please note that the begin/end actions are now of type Action<VirtualFileDataObject>. The new parameter is always a reference to the active VirtualFileDataObject instance and is provided to make it easy to use anonymous methods like you see above.

Aside: Otherwise you'd need to capture the VirtualFileDataObject instance so you could pass it to the action being provided to that same instance's constructor! (Catch-22 much?) While there may be a clever way to do this without making the begin/end actions properties of the class (which I didn't want to do because that wouldn't ensure they're invariant), passing the VirtualFileDataObject in this manner is both easy and obvious.
Further aside: As a consequence of the changes to support these new properties, VirtualFileDataObject now implements the IDataObject::SetData method for HGLOBAL-style data. So if there's some other CFSTR_ property that's relevant to your scenario, you can query it off the VirtualFileDataObject instance in much the same manner!

 

[Click here to download the complete code for VirtualFileDataObject and the sample application.]

 

And that's pretty much all there is to it! With the addition of PreferredDropEffect and PerformedDropEffect (and PasteSucceeded, though it's not demonstrated above), VirtualFileDataObject makes it easy for your application to provide a seamless virtual file drag/drop experience with all the sophisticated nuances users expect from a polished Windows application.

Enjoy!

Creating something from nothing, asynchronously [Developer-friendly virtual file implementation for .NET improved!]

Last week I posted the code for VirtualFileDataObject, an easy-to-use implementation of virtual files for .NET and WPF. This code implements the standard IDataObject COM interface for drag-and-drop and clipboard operations and is specifically targeted at scenarios where an application wants to allow the user to drag an element to a folder and create a file (or files) dynamically on the drop/paste. The standard .NET APIs for drag-and-drop don't support this scenario, so VirtualFileDataObject ended up being a custom implementation of the System.Runtime.InteropServices.ComTypes.IDataObject interface. Fortunately, the specifics aren't too difficult, and a series of posts by Raymond Chen paved the way (in native code).

VirtualFileDataObjectDemo sample application

 

If you read my previous post, you may recall there was an issue with the last scenario of the sample: the application became unresponsive while data for the virtual file was downloading from the web. While this unresponsiveness won't be a noticeable for scenarios involving local data, scenarios that create large files or hit the network are at risk. Well, it's time to find a solution!

And we don't have to look far: the answer is found in the MSDN documentation for Transferring Shell Objects with Drag-and-Drop and the Clipboard under the heading Using IAsyncOperation. As you might expect, we're not the first to notice this behavior; the IAsyncOperation interface exists to solve this very problem. So it seems like things ought to be easy - let's just define the interface, implement its five methods (none of which are very complicated), and watch as the sample application stays responsive during the time-consuming download...

FAIL.

 

Okay, so that didn't work out quite how we wanted it to. Maybe we defined the interface incorrectly? Maybe we implemented it incorrectly? Or maybe Windows just doesn't support this scenario??

No, no, and no. We've done everything right - it's the platform that has betrayed us. :( Specifically, the DragDrop.DoDragDrop method does something sneaky under the covers: it wraps our respectable System.Runtime.InteropServices.ComTypes.IDataObject instance in a System.Windows.DataObject wrapper. Because this wrapper object doesn't implement or forward IAsyncOperation, it's as if the interface doesn't exist!

Aside: I have the fortune of working with some of the people who wrote this code in the first place, and I asked why this extra level of indirection was necessary. The answer is that it probably isn't - or at least nobody remembers why it's there or why it couldn't be removed now. So the good news is they'll be looking at changing this behavior in a future release of WPF. The bad news is that the change probably won't happen in time for the upcoming WPF 4 release.

Be that as it may, it looks like we're going to need to call the COM DoDragDrop function directly. Fortunately, there's not much that happens between WPF's DragDrop.DoDragDrop and COM's DoDragDrop, so there's not much we have to duplicate. That said, we do need to define the IDropSource interface and write a custom DropSource implementation of its two methods. The nice thing is that both methods are pretty simple and straightforward, so our custom implementation can be private. (And for simplicity's sake, we're not going to bother raising DragDrop's (Preview)GiveFeedbackEvent or (Preview)QueryContinueDragEvent events.)

Because we've been careful to define the VirtualFileDataObject.DoDragDrop replacement method with the same signature as the DoDragDrop method it's replacing, updating the sample to use it is trivial. So run the sample again, and - BAM - no more unresponsive window during the transfer! (For real, this time.) You can switch to the window, resize it, drag it, etc. all during the creation of the virtual file.

 

But now we've got a bit of a dilemma: if things are happening asynchronously, how can we tell when they're done? The answer lies with the StartOperation and EndOperation methods of the IAsyncOperation interface. Per the interface contact, these methods are called at the beginning/end of the asynchronous operation. So if we just add another constructor to the VirtualFileDataObject class, we can wire things up in the obvious manner:

 /// <summary>
 /// Initializes a new instance of the VirtualFileDataObject class.
 /// </summary>
 /// <param name="startAction">Optional action to run at the start of the data transfer.</param>
 /// <param name="endAction">Optional action to run at the end of the data transfer.</param>
 public VirtualFileDataObject(Action startAction, Action endAction)

Well, almost... The catch is that while VirtualFileDataObject now supports asynchronous mode, there's no guarantee that the drop target will use it. Additionally, the developer may have specifically set the VirtualFileDataObject.IsAsynchronous property to false to disable asynchronous mode. And when you're in synchronous mode, there aren't any handy begin/end notifications to rely on...

So I've added support to VirtualFileDataObject for calling the begin/end actions in synchronous mode based on some semi-educated guesses. In my testing, the notifications during synchronous mode behave as identically as possible to those in asynchronous mode. Granted, in some scenarios startAction may run a little earlier in synchronous mode than it would have for asynchronous mode - but as far as the typical developer is concerned, VirtualFileDataObject offers the same handy behavior for both modes!

 

Let's celebrate by updating the sample to show a simple busy indicator during the download:

VirtualFileDataObjectDemo sample application

Code-wise, once we've updated the call to DoDragDrop:

VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, DragDropEffects.Copy);

Everything else stays the same except for the constructor:

var virtualFileDataObject = new VirtualFileDataObject(
    // BeginInvoke ensures UI operations happen on the right thread
    () => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Visible)),
    () => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Collapsed)));

The BusyScreen variable above corresponds to a new element in the sample application that provides the simple "Busy..." UI shown above. In real life, we'd probably use MVVM and a bool IsBusy property on our data model to trigger this, but for a sample application, toggling Visibility works fine. Because all the hard work is done by the VirtualFileDataObject class [you're welcome! :) ], the application remains unencumbered by complex logic for anything related to the management of virtual files.

Which is the way it should be!

 

[Click here to download the complete code for VirtualFileDataObject and the sample application.]

 

PS - I have one more post planned on this topic demonstrating something I haven't touched on yet to help applications coordinate better with the shell. Stay tuned...

As the platform evolves, so do the workarounds [Better SetterValueBindingHelper makes Silverlight Setters better-er!]

Back in May, I mentioned that Silverlight 2 and 3 don't support putting a Binding in the Value of a Setter. I explained why this is useful (ex: MVVM, TreeView expansion, developer/designer separation, etc.) and shared a helper class I wrote to implement the intended functionality on Silverlight. My workaround supported setters for normal DependencyPropertys as well as attached ones, so it covered all the bases. It worked well on both flavors of Silverlight and a bunch of you went off and used SetterValueBindingHelper successfully in your own projects.

The sun was shining, birds were chirping, and all was right with (that part of) the world...

SetterValueBindingHelperDemo sample

 

Now flash forward to a few days ago when I was contacted by fellow Silverlight team members RJ Boeke and Vinoo Cherian with a report that certain uses of SetterValueBindingHelper which worked fine on Silverlight 2 and 3 were likely to break if used in a possible future version of Silverlight that was more consistent with WPF's handling of such things. You can imagine my astonishment and dismay...

Important aside: The Silverlight team takes backward compatibility very seriously, so running any Silverlight 2 or 3 application with SetterValueBindingHelper on such a future version of Silverlight would continue to work in the expected manner. The Silverlight team makes a concerted effort to ensure that each version of Silverlight is "bug compatible" with previous versions to prevent existing applications from suddenly breaking when a new version of Silverlight comes out. However, were someone to recompile such an application to target a newer release of Silverlight, that application would no longer be subject to the backwards compatibility quirks and would begin seeing the new (more correct/consistent) platform behavior.

RJ and Vinoo pointed out that a more WPF-consistent handling of Styles would break one of the samples that was part of my original blog post. Specifically, the following example would not have the first Binding applied (note: per convention, code in italics is wrong):

<Style TargetType="Button">
    <!-- WPF syntax:
    <Setter Property="Grid.Column" Value="{Binding}"/>
    <Setter Property="Grid.Row" Value="{Binding}"/> -->
    <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
        <Setter.Value>
            <local:SetterValueBindingHelper
                Type="System.Windows.Controls.Grid, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
                Property="Column"
                Binding="{Binding}"/>
        </Setter.Value>
    </Setter>
    <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
        <Setter.Value>
            <local:SetterValueBindingHelper
                Type="Grid"
                Property="Row"
                Binding="{Binding}"/>
        </Setter.Value>
    </Setter>
</Style>

What's important to note is that two Setters are both setting the same Property (local:SetterValueBindingHelper.PropertyBinding) and WPF optimizes this scenario to only apply the last Value it sees. Clearly, it was time to think about how tweak SetterValueBindingHelper so it would work with this theoretical future release of Silverlight...

Tangential aside: This kind of platform change wouldn't affect just SetterValueBindingHelper - any place where multiple Setters targeted the same Property would behave differently. But that difference won't matter 99% of the time - SetterValueBindingHelper is fairly unique in its need that every Value be applied.

 

One idea for a fix is to expose something like PropertyBinding2 from SetterValueBindingHelper and treat it just like another PropertyBinding. While that would definitely work, how do we know that two properties is enough? What if you need three or four? No, despite its simplicity, this is not the flexible solution we're looking for.

Taking a step back, what we really want is to somehow provide an arbitrary number of Property/Binding pairs instead of being limited to just one. And if you read that last sentence and thought "Collection!", I like the way you think. :) Specifically, what if the same SetterValueBindingHelper class we're already using to provide the attached DependencyProperty and the data for it were also capable of storing a collection of other SetterValueBindingHelper objects? Yeah, sure, that would work!

 

So let's lay a few ground rules to help guide us:

  • Every current use of SetterValueBindingHelper should continue to be valid after we make our changes. In other words, upgrading should be a simple matter of dropping in the new SetterValueBindingHelper.cs file and that's all.
  • The new SetterValueBindingHelper syntax should work correctly for the current Silverlight 3 release as well as this mythical future version of Silverlight with the WPF-consistent Style changes.
  • The new collection syntax should be easy to use and easy to understand.
  • Arbitrary nesting is unnecessary; either someone's using a SetterValueBindingHelper on its own, or else they're using it as a container for a single, nested layer of SetterValueBindingHelper children.
  • We could try to be fancy and let children inherit things from their parent, but it's not actually as useful as it seems. Let's not go there and instead keep everything simple and consistent.

Keeping these guidelines in mind, the resulting changes to SetterValueBindingHelper give us the following alternate representation of the above XAML which works fine on Silverlight 3 today and will also give the desired effect on a possible future version of Silverlight with the WPF optimization:

<Style TargetType="Button">
    <!-- WPF syntax:
    <Setter Property="Grid.Column" Value="{Binding}"/>
    <Setter Property="Grid.Row" Value="{Binding}"/> -->
    <Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
        <Setter.Value>
            <delay:SetterValueBindingHelper>
                <delay:SetterValueBindingHelper
                    Type="System.Windows.Controls.Grid, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
                    Property="Column"
                    Binding="{Binding}"/>
                <delay:SetterValueBindingHelper
                    Type="Grid"
                    Property="Row"
                    Binding="{Binding}"/>
            </delay:SetterValueBindingHelper>
        </Setter.Value>
    </Setter>
</Style>
Aside: The two different ways of identifying Grid above are part of the original sample showing that both ways work - in practice, both instances would use the simple "Grid" form.

 

Other than the namespace change to "delay" (for consistency with my other samples), the only change here is the extra SetterValueBindingHelper wrapper you see highlighted. Everything else is pretty much the same and now it works on imaginary versions of Silverlight, too! :) So if you're working on an app and you find yourself needing SetterValueBindingHelper, please use this latest version; you can rest assured that you're future-proof.

 

[Click here to download the complete source code for SetterValueBindingHelper and its sample application.]

 

Here's the updated code in its entirety. Please note that I have used a normal (i.e., non-observable) collection, so dynamic updates to the Values property are not supported. This was a deliberate decision to minimize complexity. (And besides, I've never heard of anyone modifying the contents of a Style dynamically.)

/// <summary>
/// Class that implements a workaround for a Silverlight XAML parser
/// limitation that prevents the following syntax from working:
///    &lt;Setter Property="IsSelected" Value="{Binding IsSelected}"/&gt;
/// </summary>
[ContentProperty("Values")]
public class SetterValueBindingHelper
{
    /// <summary>
    /// Optional type parameter used to specify the type of an attached
    /// DependencyProperty as an assembly-qualified name, full name, or
    /// short name.
    /// </summary>
    [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
        Justification = "Unambiguous in XAML.")]
    public string Type { get; set; }

    /// <summary>
    /// Property name for the normal/attached DependencyProperty on which
    /// to set the Binding.
    /// </summary>
    public string Property { get; set; }

    /// <summary>
    /// Binding to set on the specified property.
    /// </summary>
    public Binding Binding { get; set; }

    /// <summary>
    /// Collection of SetterValueBindingHelper instances to apply to the
    /// target element.
    /// </summary>
    /// <remarks>
    /// Used when multiple Bindings need to be applied to the same element.
    /// </remarks>
    public Collection<SetterValueBindingHelper> Values
    {
        get
        {
            // Defer creating collection until needed
            if (null == _values)
            {
                _values = new Collection<SetterValueBindingHelper>();
            }
            return _values;
        }
    }
    private Collection<SetterValueBindingHelper> _values;

    /// <summary>
    /// Gets the value of the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="element">Element for which to get the property.</param>
    /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "SetBinding is only available on FrameworkElement.")]
    public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
    {
        if (null == element)
        {
            throw new ArgumentNullException("element");
        }
        return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
    }

    /// <summary>
    /// Sets the value of the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="element">Element on which to set the property.</param>
    /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "SetBinding is only available on FrameworkElement.")]
    public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
    {
        if (null == element)
        {
            throw new ArgumentNullException("element");
        }
        element.SetValue(PropertyBindingProperty, value);
    }

    /// <summary>
    /// PropertyBinding attached DependencyProperty.
    /// </summary>
    public static readonly DependencyProperty PropertyBindingProperty =
        DependencyProperty.RegisterAttached(
            "PropertyBinding",
            typeof(SetterValueBindingHelper),
            typeof(SetterValueBindingHelper),
            new PropertyMetadata(null, OnPropertyBindingPropertyChanged));

    /// <summary>
    /// Change handler for the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="d">Object on which the property was changed.</param>
    /// <param name="e">Property change arguments.</param>
    private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Get/validate parameters
        var element = (FrameworkElement)d;
        var item = (SetterValueBindingHelper)(e.NewValue);

        if ((null == item.Values) || (0 == item.Values.Count))
        {
            // No children; apply the relevant binding
            ApplyBinding(element, item);
        }
        else
        {
            // Apply the bindings of each child
            foreach (var child in item.Values)
            {
                if ((null != item.Property) || (null != item.Binding))
                {
                    throw new ArgumentException(
                        "A SetterValueBindingHelper with Values may not have its Property or Binding set.");
                }
                if (0 != child.Values.Count)
                {
                    throw new ArgumentException(
                        "Values of a SetterValueBindingHelper may not have Values themselves.");
                }
                ApplyBinding(element, child);
            }
        }
    }

    /// <summary>
    /// Applies the Binding represented by the SetterValueBindingHelper.
    /// </summary>
    /// <param name="element">Element to apply the Binding to.</param>
    /// <param name="item">SetterValueBindingHelper representing the Binding.</param>
    private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item)
    {
        if ((null == item.Property) || (null == item.Binding))
        {
            throw new ArgumentException(
                "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
        }

        // Get the type on which to set the Binding
        Type type = null;
        if (null == item.Type)
        {
            // No type specified; setting for the specified element
            type = element.GetType();
        }
        else
        {
            // Try to get the type from the type system
            type = System.Type.GetType(item.Type);
            if (null == type)
            {
                // Search for the type in the list of assemblies
                foreach (var assembly in AssembliesToSearch)
                {
                    // Match on short or full name
                    type = assembly.GetTypes()
                        .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
                        .FirstOrDefault();
                    if (null != type)
                    {
                        // Found; done searching
                        break;
                    }
                }
                if (null == type)
                {
                    // Unable to find the requested type anywhere
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                        "Unable to access type \"{0}\". Try using an assembly qualified type name.",
                        item.Type));
                }
            }
        }

        // Get the DependencyProperty for which to set the Binding
        DependencyProperty property = null;
        var field = type.GetField(item.Property + "Property",
            BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
        if (null != field)
        {
            property = field.GetValue(null) as DependencyProperty;
        }
        if (null == property)
        {
            // Unable to find the requsted property
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
                item.Property, type.Name));
        }

        // Set the specified Binding on the specified property
        element.SetBinding(property, item.Binding);
    }

    /// <summary>
    /// Returns a stream of assemblies to search for the provided type name.
    /// </summary>
    private static IEnumerable<Assembly> AssembliesToSearch
    {
        get
        {
            // Start with the System.Windows assembly (home of all core controls)
            yield return typeof(Control).Assembly;

            // Fall back by trying each of the assemblies in the Deployment's Parts list
            foreach (var part in Deployment.Current.Parts)
            {
                var streamResourceInfo = Application.GetResourceStream(
                    new Uri(part.Source, UriKind.Relative));
                using (var stream = streamResourceInfo.Stream)
                {
                    yield return part.Load(stream);
                }
            }
        }
    }
}