July 2007 - Posts

I'm working on a WPF app for my wife who coaches our local high school track team.  One of the features she wanted was to have some images for different players and teams and, religious debate aside, I have chosen to save these in SQL Server 2005.  I'm using NHibernate for all my data access which has been a great experience (my first).  Impressively, NHibernate supports SQL Server Image columns via the inherent Byte[]  type (See Section 5.1.9 of the docs).  This made adding the Image to my classes very easy. 

Here's a snippet of my class with the corresponding NHibernate Mapping Xml File:

/* Class */
[Serializable]
public class Organization : INotifyPropertyChanged
{
//...

public byte[] Image
{
get { return _image; }
set
{
_image = value;
OnPropertyChanged("Image");
}
}

//...
}
<!-- Mapping -->
<
hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
<
class name="Cromwell.MyApp.Organization, Cromwell.MyApp"
table="dbo.Organization">
<
id name="Id" type="Int32" column="Id" unsaved-value="0">
<
generator class="native" />
</
id>
<
many-to-one name="Type"
class="Cromwell.MyApp.OrganizationType, Cromwell.MyApp"
column="Type"
not-null="true"/>
<
property name="Name" type="String" length="255" column="Name" not-null="true"/>
<
property name="Address1" type="String" length="255" column="Address1" not-null="false"/>
<
property name="Address2" type="String" length="255" column="Address2" not-null="false"/>
<
property name="City" type="String" length="255" column="City" not-null="false"/>
<
property name="State" type="String" length="2" column="State" not-null="false"/>
<
property name="PostalCode" type="String" length="7" column="PostalCode" not-null="false"/>
<
property name="Image" type="Byte[]" column="Image" not-null="false" />
</
class>
</
hibernate-mapping>

The key to binding this Image property on my WPF forms is the Binding Converter property.  This property allows you to specify any class which implements System.Windows.Data.IValueConverter to act as an intermediary between your bind data and WPF.  In my case, the goal was to turn a byte[] property, Image, into a BitmapImage which the an <Image /> control/element can display. 

Here's what I came up with:

class BinaryImageConverter : IValueConverter
{
object IValueConverter.Convert( object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture )
{
if(value != null && value is byte[])
{
byte[] bytes = value as byte[];

MemoryStream stream = new MemoryStream( bytes );

BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.EndInit();

return image;
}

return null;
}

object IValueConverter.ConvertBack( object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture )
{
throw new Exception( "The method or operation is not implemented." );
}
}

Here's how I used it in my WPF Page:

<Page x:Class="Cromwell.MyApp.EditOrganization"
...
xmlns:converts="clr-namespace:Cromwell.MyApp.Converters"
>
<
Page.Resources>
<
converts:BinaryImageConverter x:Key="imgConverter" />
</
Page.Resources>
<
Grid>
<!--
Image Control -->
<
Image Source="{Binding Path=Image,
Converter={StaticResource imgConverter}}
"
Stretch="UniformToFill"
StretchDirection="Both">
<
Image.BitmapEffect>
<
DropShadowBitmapEffect Color="Black" />
</
Image.BitmapEffect>
</
Image>
</
Grid>
</
Page>

I'm going to have to spend some time in the WPF profiler, but this is a big step.  Pretty cool how simple it was and very clean.

with 1 comment(s)
Filed under: ,