Main contents

I recently had the pleasurable and painful experience of learning WiX to build a windows installer for a Windows Mobile application. The pain came first: there is quite a learning curve to both WiX and the underlying Windows Installer technology; and the documentation is a little thin on WiX 3, the latest version as I write this. But after about two weeks struggling, the switch flipped, the sun shone, the chorus of angels began singing – I understood how it worked and saw that its design is very well thought out. It became a joy to use WiX. And it’s free. And it has pretty good Visual Studio support.

That’s not to say there aren’t problems. The documentation is definitely a problem, one to which I’ll try to make a small contribution in solving. I intend to post more about WiX in the future, but for now, I’ll cover one small thing which is setting properties for your WiX project in an automated build with MSBuild.

Part of the pretty good Visual Studio support is it’s own MSBuild-based project file. It has custom tasks that wrap the underlying command-line utilities, candle, lit, light, etc. This makes it really easy to make it part of your daily build or CI builds. One thing that often gets taken over by the build process is setting the version number of your assemblies. Versioning is especially important when it comes to installers, so keeping the installer version in sync with the application version is definitely a good practice.

To do this with your WiX installer project, we first have turn the version value to a variable. (Forgive me if I use the incorrect terms here, I don’t know the formal WiX names for these things. I’ll gladly change it if someone corrects me.)

Suppose we have the following, default wxs file given to us by Votive in Visual Studio:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" 
            Name="WixProject1" 
            Language="1033" 
            Version="1.0.0.0" 
            Manufacturer="WixProject1" 
            UpgradeCode="c93e09b9-9e8f-444c-a35b-84beb2c3788f">
        <Package InstallerVersion="200" Compressed="yes" />

        <Media Id="1" Cabinet="WixProject1.cab" EmbedCab="yes" />

        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLLOCATION" Name="WixProject1">
                    <!-- TODO: Remove the comments around this Component element
               and the ComponentRef below in order to add resources to this installer. -->
                    <!-- <Component Id="ProductComponent" Guid="51acaaef-c2fb-4ef3-a641-9475c81ac948"> -->
                        <!-- TODO: Insert files, registry keys, and other resources here. -->
                    <!-- </Component> -->
                </Directory>
            </Directory>
        </Directory>

        <Feature Id="ProductFeature" Title="WixProject1" Level="1">
            <!-- TODO: Remove the comments around this ComponentRef element
         and the Component above in order to add resources to this installer. -->
            <!-- <ComponentRef Id="ProductComponent" /> -->
        </Feature>
    </Product>
</Wix>

The property we’re concerned with is the Version attribute on the Product element. Right now it’s set to 1.0.0.0. The first thing we need to do is change this into a variable so that we can set the value from elsewhere. To do that we change the Version attribute above into the following:

<Product Id="*" 
         Name="WixProject1" 
         Language="1033" 
         Version="$(var.ProductVersion)" 
         Manufacturer="WixProject1" 
         UpgradeCode="c93e09b9-9e8f-444c-a35b-84beb2c3788f">
...
</Product>

This creates a preprocessor variable called ProductVersion (you can call it whatever you want). If you build the installer now, you get an error saying “Error    1    Undefined preprocessor variable ‘$(var.ProductVersion)’. “

Now we have to define that elsewhere. And where else but the project file!

To edit that, right click on the project node in solution explorer, select Unload Project… Then right click again and select Edit <ProjectName>.wixproj. You’ll get the proj file loaded as XML in the editor. Let me draw your attention to one of the PropertyGroups:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug</DefineConstants>
</PropertyGroup>

This is a property group that is only defined if the Configuration property is Debug and Platform is x86. You see that DefineConstants property set to Debug? That’s where we want to define our ProductVersion variable, like so:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug;ProductVersion=1.0.0.0</DefineConstants>
</PropertyGroup>

In general, you want to define all your properties in Name=Value; format. OK, we have that entered, so close the file, right click on the project node again in SOlution Explorer, select Reload Project. This makes Visual Studio read the project file again. Build again. No errors! Neat, eh?

Now we’ve managed to move setting the of the ProductVersion property into the MSBuild file, but remember what I said about that property group? It’s only defined in Debug builds. If we switched our project to Release, then we’d get the Undefined variable error again. (This is actually a big usability problem in VS, IMO; this happens all the time for many properties and settings).

What we really want is the ProductVersion property defined for all build configurations. But while we’re developing our installer, we want to use Visual Studio, so we don’t have to constantly go into the proj file to change things. No problem, I got you covered.

The good folks developing WiX for all of us obviously have our interests in mind by setting that Debug property by default, so we’re going to want to save that. We also want to define ProductVersion for Release builds. Let’s consider all the PropertyGroups in the wixproj file, the answer is practically shouting at us:

<PropertyGroup>
  <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
  <ProductVersion>3.0</ProductVersion>
  <ProjectGuid>{9b84d861-ac5d-4559-bc9c-0e59cb95ad90}</ProjectGuid>
  <SchemaVersion>2.0</SchemaVersion>
  <OutputName>WixProject1</OutputName>
  <OutputType>Package</OutputType>
  <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.0\Wix.targets</WixTargetsPath>
  <Name>WixProject1</Name>     
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug;ProductVersion=1.0.0.0</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>

First thing we should do is add the DefineConstants property with our ProductVersion property in Release:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>ProductVersion=1.0.0.0</DefineConstants>
</PropertyGroup>

Got it, now: check out the Configuration and Platform properties in the first PropertyGroup. See what they do? They define a default if empty. Now why couldn’t we do that? We’ll define another property in the first property group called Version (you can call it what you like):

<Version Condition=" '$(Version)' == '' ">1.0.0.0</Version>

Alright, one more step, we replace the value in our DefineConstants declarations. I’ll do the Debug version, you do the Release:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug;ProductVersion=$(Version)</DefineConstants>
</PropertyGroup>

Got the Release section finished? Awesome. Reload the project. Build. Awesome.

It works!

Now, we can build this project on the command line and set any version we want.

msbuild installer.proj /p:Version=3.1.0.0

One thing to note though. Ideally, you’re going to mix and match your product assemblies and product installer. Well, maybe not, but if you have any code-based custom actions, you’re probably going to have a C# or VB project in you product installer solution.

The bad news is that the CSharp.targets file, the file with all the default C# build tasks also defines a DefineConstants property. It’s used for setting DEBUG and TRACE and all those. They conform to a certain format; one that doesn’t include Name=Value; formatting. So you get unnecessary warnings.

I’m not sure why they decided to reuse a well-known property name. Was it really so hard to type <PreprocessorVariable>?

Posted in Software, WiX | 8 Comments »

Have I mentioned lately how much I like data binding, lately? I use it everywhere I can: it makes writing UI code much easier. Anyway, working on a project at work I ran into the following problem:

Consider this code:

this.checkBox1.DataBindings.Add("Checked", this.myBindingSource, "MyBooleanProperty",
                                 true, DataSourceUpdateMode.OnPropertyChanged);

This declares a data binding on checkBox1, a CheckBox control. It uses the giant overload taking arguments that, respectively, declare the name of the property on the Control to bind to, the source of the data property, the data property, a boolean for formatting, and a DataSourceUpdateMode enum value.

The DataSourceUpdateMode.OnPropertyChanged value means that as soon as the control’s Checked property changes, then change the MyBooleanProperty on the data source. OnPropertyChanged is not the default; the default is OnValidation which typically happens when the control loses focus. I’m a fan of PropertyChanged because there are problems with OnValidation when focus doesn’t change.

Anyway, if you bind to the Checked property on CheckBox, and select DataSourceUpdateMode.OnPropertyChanged, then it updates on validation.

BZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZT! Wrong! Bug!

Fortunately, there is a workaround. If you use the useless, and practically redundant, CheckState property on CheckBox, instead of Checked, then you get OnPropertyChanged updating. Important: Make sure ‘true’ is passed for the formattingEnabled parameter, otherwise the value expected by the control will be of type CheckState, an enum.

The following code works:

this.checkBox1.DataBindings.Add("CheckState", this.myBindingSource, "MyBooleanProperty", 
                                true, DataSourceUpdateMode.OnPropertyChanged);

Your welcome.

Posted in .NET | 4 Comments »

The .NET framework is huge, but not so huge that it does everything for everyone; there are things that they in Redmond miss or don’t do for whatever reason but is still generally applicable to many developers. So, dear reader, I present to you a series of posts on stuff I find missing in .NET, typically where even the Google fails to find the answer. It could be a useful class, a technique, a good practice or documentation that should be in the framework but isn’t or isn’t widely publicized. Click here for a complete list of Missing .NET entries.


I’m sure, Dear Reader, if you wait long enough, I’ll do all the exercises I leave to you in my posts. I wrote last time about displaying enums in WPF using databinding. I left it as an exercise to do this in Windows Forms and ASP.NET. Well, it turns out that I needed a Windows Forms version for more than just a programming exercise. So I figured I’d share something like it with all 5 of you regular readers.

In case you don’t want to click through to the WPF article, let me recap it here for you since we’ll be using some of the code from that article. I’m ambivalent towards enums; I often think they are less useful in many situations in which they are used than a custom class. I do, however, have little choice when it comes to using other libraries or .NET Framework code which may use them. One of the troublesome areas of enums is showing them in UI without going insane writing the same code over and over again with only the slight difference of the enum type to distinguish each version.  Last time I used the first-rate data-binding in WPF to show enums in a WPF ComboBox. Then I handled the case where you don’t own the enum. I also dealt with the issues of internationalization and human readable text instead of the names of each enum value. In this article, I’ll use the somewhat less first-rate data-binding in Windows Forms to do the same thing.

The first thing we need is a way to mark our enum with the text we want in place of the value name. The perfect way to do that, as I said last time, is to use attributes. The class I presented last time is independent of UI frameworks, so I’ll use it again here. It’s reproduced below:

using System;

namespace MissingNet.ComponentModel
{
   [AttributeUsage(AttributeTargets.Field)]
   public sealed class DisplayStringAttribute : Attribute
   {
      private readonly string value;
      public string Value
      {
         get { return value; }
      }

      public string ResourceKey { get; set; }

      public DisplayStringAttribute(string value)
      {
         this.value = value;
      }

      public DisplayStringAttribute()
      {
      }
   }
}

We can apply this to the values of our custom enum, like so:

 

public enum MyEnum
{
   [DisplayString("My Value")]
    Value,
   [DisplayString("This means On")]
    On,
   [DisplayString("Off means not On")]
    Off,
   [DisplayString("The great unknown")]
    Unknown,
   [DisplayString("I ain't got none")]
    None
}

Doesn’t look as pretty as an unadorned enum, but commercial quality code often looks less attractive than you’re typical code snippet on MSDN. If we want to internationalize our app, we can have different display strings for different languages by setting the ResourceKey property instead of the value constructor:

 

public enum MyEnum
{
   [DisplayString(ResourceKey="MyEnum_Value")] Value,
   [DisplayString(ResourceKey="MyEnum_On")] On,
   [DisplayString(ResourceKey="MyEnum_Off")] Off,
   [DisplayString(ResourceKey="MyEnum_Unknown")] Unknown,
   [DisplayString(ResourceKey="MyEnum_None")] None
}

Don’t forget to add the strings to your resx file for each language!

Now that we’ve reviewed how to associate the display strings with the enum values, we need a way to get that data into the UI. For that I’ll be using one of the unsung heroes of the .NET 2.0 WinForms improvements: BindingSource.

Earlier I called the data-binding in WinForms less than first-rate. It’s only in comparison to WPF, which isn’t really fair: WPF was designed from the ground up with data-binding as a feature. By contrast, Windows Forms is an elegant OO wrapper around the flat Win32 API, an API that is ancient in computer years. It’s so old they didn’t even have data when it was designed. So there is nothing as elegant as, say, DataContext in Windows Forms, but it’s still world’s better than the alternative of doing it all yourself.

If Win32 were a Star Wars character

If Win32 were a Star Wars character: really powerful, but really, really old

They faked it pretty good in .NET 1.1 for simple databinding (simple in the Windows Forms data binding sense of just hooking up to a property); complex databinding (binding to a list or collection or DataSet) was a bit more onerous if you modified the collection independent of the Form that was displaying it. In .NET 2.0, they improved things tremendously by following the old adage: You can at least partially solve any problem in computer science with one more level of indirection. [The full quote, attributed to David Wheeler is, “Any problem in computer science can be solved with another layer of indirection. But that usually will create another problem.”]

And that level of indirection takes the form of the BindingSource. To describe the BindingSource, let me quote directly from the excellent book on the subject of Windows Forms databinding, Data Binding with Windows Forms 2.0, by Brian Noyes:

The BindingSource component solves a number of tricky problems that surfaced with the approach of directly binding data sources to controls in .NET 1.X. It provides a layer of indirection between a data source and bound controls that makes a number of things easier. Additionally, it surfaces important control points and access to the underlying data source in a way that saves you from having to delve into the data-binding mechanisms of a form the way you had to in the past. A binding source also gives you a single API to program against from the form’s perspective, and lets a lot of your form code remain decoupled from the specific data source type. This prevents you from having to adapt your programmatic coding patterns to each different type of data that your application works with… [p111-112]

Sounds perfect for our needs, doesn’t it? Displaying enums, from a Windows Forms data-binding perspective, is actually fairly easy: it’s a read-only data source with read-only items, so there is no need to deal with adding, removing or editing. In fact the only tough part is filling the “list” with the entries. We’ve already done something similar with in WPF.

Here’s the EnumBindingSource class:

public class EnumBindingSource : BindingSource
{
  private readonly object dataSource;
  private readonly PropertyInfo property;

  public EnumBindingSource(object dataSource, string propertyName)
  {
     this.dataSource = dataSource;
     this.property = dataSource.GetType().GetProperty(propertyName);
     Type enumType = property.PropertyType;
     FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
     List<NameEnumPair> source = new List<NameEnumPair>(fields.Length);
     foreach (FieldInfo f in fields)
     {
        DisplayStringAttribute[] a = (DisplayStringAttribute[])f.GetCustomAttributes(typeof(DisplayStringAttribute), false);
        string displayString = GetDisplayStringValue(a, enumType);
        object enumValue = f.GetValue(null);
        NameEnumPair pair = new NameEnumPair(displayString, enumValue);
        source.Add(pair);
     }
     int index = source.FindIndex(value => value.Value.Equals(property.GetValue(dataSource, null)));
     this.DataSource = source;
     this.Position = index;
  }

  public override bool AllowEdit
  {
     get { return false; }
  }

  public override bool AllowNew
  {
     get { return false; }
     set { throw new NotSupportedException();}
  }

  public override bool AllowRemove
  {
     get { return false; }
  }

  protected override void OnCurrentChanged(EventArgs e)
  {
     base.OnCurrentChanged(e);
     //set the value
     NameEnumPair value = (NameEnumPair)this.Current;
     property.SetValue(dataSource, value.Value, null);
  }

  private static string GetDisplayStringValue(DisplayStringAttribute[] a, Type type)
  {
     if (a == null || a.Length == 0) return null;
     DisplayStringAttribute dsa = a[0];
     if (!string.IsNullOrEmpty(dsa.ResourceKey))
     {
        ResourceManager rm = new ResourceManager(type);
        return rm.GetString(dsa.ResourceKey);
     }
     return dsa.Value;
  }

  private class NameEnumPair
  {
     private readonly string displayName;
     private readonly object value;

     public NameEnumPair(string displayName, object value)
     {
        this.displayName = displayName;
        this.value = value;
     }

     public string DisplayName
     {
        [DebuggerStepThrough]
        get { return displayName; }
     }

     public object Value
     {
        [DebuggerStepThrough]
        get { return value; }
     }

     public override string ToString()
     {
        return DisplayName;
     }
  }
}

All the work happens in the constructor: we use reflection to get the type of the property passed in, get the enum values in the type, read the DisplayStringAttribute values, add them to a list, set the DataSource property, and we’re done. I’ve overridden the AllowXxx properties to make this an immutable BindingSource; there are more properties to override to lock this down as a framework type, but this is a good start. If I were really hardcore, I’d implement all the interfaces that BindingSource does myself, but I don’t see the value.

So, this works for enums that you own, but like I said in the WPF article, that is really rare. What you need is a way to declare the strings for enums that you don’t own. Again, using the WPF implmentation as a reference, we merely need to create a class that will contain the data and set it appropriately. But here, we don’t have the requirement to support XAML, so we have a bit more freedom. Since this is pretty much an immutable type, setting it in the constructor is the way to go. I just need an overloaded constructor. We also need a type to hold the data we want to set. We already have that type with the nested class NameEnumPair, we just need to make it public, and probably move it to the outer scope, as most .NET types are wont to do.

Here’re the new constructors:

public EnumBindingSource(object dataSource, string propertyName)
   : this(dataSource, propertyName, null)
{
}

public EnumBindingSource(object dataSource, string propertyName, IEnumerable<NameEnumPair> overriddenDisplayValues)
{
   this.dataSource = dataSource;
   this.property = dataSource.GetType().GetProperty(propertyName);
   List<NameEnumPair> source;
   if (overriddenDisplayValues == null)
   {
      Type enumType = property.PropertyType;
      FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
      source = new List<NameEnumPair>(fields.Length);
      foreach (FieldInfo f in fields)
      {
         DisplayStringAttribute[] a = (DisplayStringAttribute[])f.GetCustomAttributes(typeof(DisplayStringAttribute), false);
         string displayString = GetDisplayStringValue(a, enumType);
         object enumValue = f.GetValue(null);
         NameEnumPair pair = new NameEnumPair(displayString, enumValue);
         source.Add(pair);
      }
   }
   else
   {
      source = new List<NameEnumPair>(overriddenDisplayValues);
   }
   int index = source.FindIndex(value => value.Value.Equals(property.GetValue(dataSource, null)));
   this.DataSource = source;
   this.Position = index;
}

Now we have a way to display enums in ComboBoxes in Windows Forms. Client code looks like the following:

public class MyCustomClass
{
  public MyEnum MyEnumProperty { get; set; }

  public MyCustomClass()
  {
     MyEnumProperty = MyEnum.On;
  }
}

public partial class Form1 : Form
{
  public Form1()
  {
     InitializeComponent();
     MyCustomClass myCustomObject = new MyCustomClass();
     this.comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
     this.comboBox1.DataSource = new EnumBindingSource(myCustomObject, "MyEnumProperty");
  }
}

Posted in .NET, Missing .NET | 3 Comments »

Keep it DRAFT-y

10 September 2008

The other day I was grabbing screenshots from an app I’m working on. I noticed a huge visual problem with a a list in the product: in a one-column ListView in Details view, each item’s text was truncated, as though the column’s width was just left to the default of 20 pixels (this is in .NET CF, but the problem below applies to the full framework WinForms as well). This is code I inherited, mind you! I certainly wouldn’t have coded it this way (No sir, nah-uh), but I found the offending code that that was trying to set the column width to the largest item; something like this:

private void ArrangeColumnWidth()
{
    //this is called after all the items have been added
    using (Graphics g = this.listView.CreateGraphics())
    {
        int maxWidth = 0;
        foreach (object item in this.listView.Items)
        {
            SizeF s = g.MeasureString(item.ToString());
            if (s.Width > maxWidth)
                maxWidth = s.Width;
        }
        this.listView.Columns[0].Width = maxWidth;
    }
}

Pretty standard find-the-max code, right? Except it wasn’t working. Since I knew there would always be one column, and we’d never change out of Details view, I, without thinking too hard about it, changed the above code to the following:

private void ArrangeColumnWidth()
{
    this.listView.Columns[0].Width = this.listView.Width;
}

“There! That should do it,” I thought — in the few seconds that it took me to write it. Of course, you’ve just read that, and thought, “What a fool! Anyone can see that if there are enough items in the ListView to cause scrolling vertically, then the column width will be too big, so you’ll get a horizontal scroll bar also. Duh! Everyone knows that. What was he thinking?”

And you’d be right.

But sometimes, when I’m in the flow, I have really fast edit, compile, run cycles. The first run after the above edit showed me the problem that you astute readers already found.

At this point, in my younger days, I’d bash away at determining the width of a standard scroll bar and hardcoding that number in my arithmetic. But I’ve learned the following refrain from using .NET since it came out: It’s in the Framework, Dummy!

So, I did a Google search, found a forum post, read it, couldn’t believe the solution was so simple, then went to the MSDN docs to confirm that it was true. It was! If you set the ColumnHeader.Width value to -1, it will size to the largest item. If you set the ColumnHeader.Width value to -2, it will size the header to the header text. It’s all right there on MSDN!

I didn’t know this little tidbit. So, I re-wrote the code. I got rid of the ArrangeColumnWidth() method altogether and now when I create the column, I set it’s width to -1. Problem solved!

Two things occurred to me when I read about those helpful values.

The first is that that is some old-ass .NET code. Microsoft doesn’t write .NET code like that anymore. Instead, I think it would be something like the following:

public enum ColumnWidthStyle
{
    Normal,
    SizeToMaximumItem,
    SizeToColumnHeader
}

ColumnHeader c = new ColumnHeader();
c.WidthStyle = ColumnWidthStyle.SizeToMaximumItem;

That code is more verbose, sure; but it’s way more discoverable, especially with IntelliSense. It’s also more readable; imagine coming back to this code a few months later: it would still make sense. The way we’re stuck with is essentially a magic number, a vestigial bump from the dark ol’ days of Win32 programming. I have no doubt that the ColumnHeader class is managed spackle over the Win32 equivalent, so we get those negative width numbers for free.

Even with the awkwardness of the API as it is, I’m still going to use it. That’s one method I don’t have to write. The whole point of using the .NET framework is that it does a lot for you. And yet…

I still see commercial code shipping with reams and reams of classes and methods that duplicate functionality in the Framework. Why!?!

We’ve all heard the principle of DRY (Don’t Repeat Yourself). I’ve got my own corollary, the DRAFT principle: Don’t Repeat A Framework Type. .NET, J2EE, Rails, Django, CPAN, whatever language and framework you prefer: assume they solve all your problems, learn them, use them, abuse them. And only if they are truly lacking should you write your own.

Posted in .NET, Rants, Software | 1 Comment »

How to Save XAML as an Image

2 September 2008

Here’s a quick note to self that you may enjoy. I suck at art. Didn’t use to always, but it requires too much brain power. But I’m not bad at getting WPF to draw what I want. I had a logo in mind for this site and used WPF to generate it after unsuccessfully getting Paint.NET to do the same.

I recently needed to use this code again, but forgot how I solved it. So, rather than hunt it down on Google again, I came home and loaded up ye olde Windows Live Writer to write this post.

To save the XAML you want as an image, put the elements you want to save into a canvas element called canvas. Then run the following code.

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
   Rect rect = new Rect(canvas.RenderSize);
   RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
     (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
   rtb.Render(canvas);
   //endcode as PNG
   BitmapEncoder pngEncoder = new PngBitmapEncoder();
   pngEncoder.Frames.Add(BitmapFrame.Create(rtb));

   //save to memory stream
   System.IO.MemoryStream ms = new System.IO.MemoryStream();

   pngEncoder.Save(ms);
   ms.Close();
   System.IO.File.WriteAllBytes("logo.png", ms.ToArray());
   Console.WriteLine("Done");
}

I hooked up a key binding to a command to execute this code, but you could use a button click too. It’ll save the elements in canvas to a file in the same folder as the executable, called logo.png, but you can change that if you want. You can also you a different encoder if you like. They’re in System.Windows.Media.Imaging.

Enjoy!

Posted in .NET, Note to self... | 3 Comments »