C#, WPF – Fast image resize

Today I read a blog on http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx which introduces a new way to resize image with WPF. This new method does not use GDI+ (Microsoft Windows GDI+ is the portion of the Windows XP operating system or Windows Server 2003 operating system that provides two-dimensional vector graphics, imaging, and typography.) but it uses WPF library for resizing images very fast. You can read more details about this method in that blog. In the code below, I would like to summarize and explain a little how it works.
The main concept, that we will read all bytes of image, read the first frame of image and use class TransformedBitmap to create a new bitmap frame with the specific scale transform. In the code snippet below shows how the process runs. So that code below works we need to add reference to following assemblies: PresentationCore and WindowsBase.

static void Main(string[] args)
{
	string strFileName = "Marshimaro.jpg";
	string strThumbnail = "MarshimaroThumb.png";
	byte[] baSource = File.ReadAllBytes(strFileName);
	using (Stream streamPhoto = new MemoryStream(baSource))
	{
		BitmapFrame bfPhoto = ReadBitmapFrame(streamPhoto);

		int nThumbnailSize = 200, nWidth, nHeight;
		if (bfPhoto.Width > bfPhoto.Height)
		{
			nWidth = nThumbnailSize;
			nHeight = (int)(bfPhoto.Height * nThumbnailSize / bfPhoto.Width);
		}
		else
		{
			nHeight = nThumbnailSize;
			nWidth = (int)(bfPhoto.Width * nThumbnailSize / bfPhoto.Height);
		}
		BitmapFrame bfResize = FastResize(bfPhoto, nWidth, nHeight);
		byte[] baResize = ToByteArray(bfResize);

		File.WriteAllBytes(@"Thumbnails\" + Path.GetFileNameWithoutExtension(strThumbnail) + ".png", baResize);
		Console.WriteLine("Resize done!!!");
		Console.ReadLine();
	}
}

BitmapFrame represents image data returned by a decoder and accepted by encoders. You can see in the function ReadBitmapFrame how we can use BitmapDecoder to decode stream data of image and get the first bitmap frame.
The core of complete code is the class TransformedBitmap which supports only orthogonal transforms such as rotation transforms of 90° increments and scale transforms. In FastResize function, we apply ScaleTransform to resize image as following.

private static BitmapFrame FastResize(BitmapFrame bfPhoto, int nWidth, int nHeight)
{
	TransformedBitmap tbBitmap = new TransformedBitmap(bfPhoto, new ScaleTransform(nWidth / bfPhoto.Width, nHeight / bfPhoto.Height, 0, 0));
	return BitmapFrame.Create(tbBitmap);
}

private static byte[] ToByteArray(BitmapFrame bfResize)
{
	using (MemoryStream msStream = new MemoryStream())
	{
		PngBitmapEncoder pbdDecoder = new PngBitmapEncoder();
		pbdDecoder.Frames.Add(bfResize);
		pbdDecoder.Save(msStream);
		return msStream.ToArray();
	}
}

private static BitmapFrame ReadBitmapFrame(Stream streamPhoto)
{
	BitmapDecoder bdDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
	return bdDecoder.Frames[0];
}

You can parse the code into class and use it everywhere. The complete code can be downloaded here “WPF Fast image resize

UPDATE 11.03.2010 : Insert (using Stream) to prevent memory leak.

WPF – Use DataTrigger to set property of component

In Windows Form Programming, if we want to create a data trigger for a component we need to write code to process data change event and give suitable data back. In WPF Microsoft allows us to bind trigger direct to component in XAML code without writing any code. DataTrigger represents a trigger that applies property values or performs actions when the bound data meets a specified condition.

In this example below I would like to demonstrate how we create data trigger in WPF.
I place on grid a combo box and two text box. Then I add 3 combo box item “One”, “Two”, and “Tree”.

<ComboBox Height="26" Margin="34,32,137,0" Name="cbSelection" VerticalAlignment="Top">
	<ComboBoxItem>One</ComboBoxItem>
	<ComboBoxItem>Two</ComboBoxItem>
	<ComboBoxItem>Three</ComboBoxItem>
</ComboBox>
<TextBox  Height="23" Margin="34,0,138,90" Name="txtSelectionOne" VerticalAlignment="Bottom">
</TextBox>
<TextBox  Height="23" Margin="34,0,138,45" Name="txtSelectionTwo" VerticalAlignment="Bottom" >
</TextBox>

Style, ControlTemplate, and DataTemplate all have a triggers collection. A DataTrigger allows you to set property values when the property value of the data object matches a specified Value. Note that you must specify both the Binding and Value properties on a DataTrigger for the data trigger to be meaningful. If one or both of the properties are not specified, an exception is thrown. The Setters property of a DataTrigger object can only consist of Setter objects.
In our example we want when the combo box has value “One”, the text box one will be selected and “Two” for text box two. So we insert the data trigger into Style of each text box.

<TextBox  Height="23" Margin="34,0,138,90" Name="txtSelectionOne" VerticalAlignment="Bottom">
	<TextBox.Style>
		<Style>
			<Setter Property="TextBox.IsEnabled" Value="False"/>
			<Style.Triggers>
				<DataTrigger Binding="{Binding ElementName=cbSelection , Path=Text}" Value="One">
					<Setter  Property="TextBox.IsEnabled" Value="true"/>
				</DataTrigger>
			</Style.Triggers>
		</Style>
	</TextBox.Style>
</TextBox>

The property value produced by this binding is compared with the value specified by the Value property. That value is first converted to the type of the value of the binding (if possible), and then the two values are compared using the Object.Equals method. If the two values are equal, then the associated actions or setters are applied.
For the text box two, you just need to copy same code with small changes and everything will work.