C#, WPF – Play swf in wpf application

When I was trying to extract the first frame of SWF file into image I discovered that there is a small problem when we try to host a SWF file in WPF application because WPF does not allow us to integrate ActiveX control directly in its application. To enable interaction through ActiveX control, we must do a walkthrough with WindowsFormsHost. In this small example below I would like to guide you step by step to host a SWF file in your WPF application.

1. In Visual Studio, File –> New –> Project –> Windows Forms Control

2. Set Platform target to x86

3. In Visual Studio, right click on Toolbox window, then “Choose Items”

4. Navigate to COM components tab and choose Shockwave Flash Object. Press OK.

5. Drag “Schockwave Flash Object” from Toolbox to our UserControl and change its name to axShockwaveFlash. Rename our UserControl to FlashAxControl and add following code to it.

public partial class FlashAxControl : UserControl
{
	public FlashAxControl()
	{
		InitializeComponent();
	}

	public new int Width
	{
		get { return axShockwaveFlash.Width; }
		set { axShockwaveFlash.Width = value; }
	}

	public new int Height
	{
		get { return axShockwaveFlash.Height; }
		set { axShockwaveFlash.Height = value; }
	}

	public void LoadMovie(string strPath)
	{
		axShockwaveFlash.LoadMovie(0, strPath);
	}

	public void Play()
	{
		axShockwaveFlash.Play();
	}

	public void Stop()
	{
		axShockwaveFlash.Stop();
	}
}

6. Build our UserControl. Start new WPF Application and reference to our UserControl, System.Windows.Forms and WindowsFormsIntegration as following

7. Set Platform Target to x86

8. Add following code to play SWF in our application

string strFilePath = @"e:\temp.swf";
SWFFileHeader swfFile = new SWFFileHeader(strFilePath);
this.Width = swfFile.FrameSize.WidthInPixels;
this.Height = swfFile.FrameSize.HeightInPixels;

WindowsFormsHost host = new WindowsFormsHost();
FormFlashLibrary.FlashAxControl player = new FormFlashLibrary.FlashAxControl();

//the Windows Forms Host hosts the Flash Player
host.Child = player;

//the WPF Grid hosts the Windows Forms Host
grdMain.Children.Add(host);

//set size
player.Width = (int) this.Width;
player.Height = (int) this.Height;

//load & play the movie
player.LoadMovie(strFilePath);
player.Play();

The grdMain is the grid of WPF Application. The complete source code you can download at “Form Flash Library” and “WPF Play SWF”.
If you want to play SWF in Windows Form Application, you can read at this blog “C# – How to play a .swf file?”

C#, WPF – Hit Testing Example

When we create interactive 2D drawing applications in WPF, we can deal directly with the user’s
interaction with the graphics objects using mouse event handlers. However, WPF provides powerful hit-
testing for graphics objects through the static VisualTreeHelper.HitTest method.
In order to use this advanced hit-testing feature, we need to create a callback. The VisualTreeHelper
will then walk through your visuals from top to bottom. Whenever it finds a match, it calls the callback
with the details. We can then choose to stop the search or to continue until no more visuals remain
.
The following example will illustrate how to use this advanced feature. In this example we have many rectangles overlapping with each other, when we click on a point on windows there is a popup telling us at point how many rectangles are locating.
Use Microsoft Expression Blend to draw many overlapped rectangle as proposed as below.

Name our grid as grdMain and add handler for MouseLeftButtonDown.

<Grid x:Name="grdMain" MouseLeftButtonDown="cvMain_MouseLeftButtonDown">

In the function cvMain_MouseLeftButtonDown, we define the hit area and call the function VisualTreeHelper.HitTest to find out the intersection area between our hit area and painted rectangle

private void cvMain_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
	Point ptCurrent = e.GetPosition(grdMain);
	m_egHitArea = new EllipseGeometry(ptCurrent, 1, 1);
	m_lstHitList.Clear();
	VisualTreeHelper.HitTest(grdMain, null, new HitTestResultCallback(HitTestCallback), new GeometryHitTestParameters(m_egHitArea));
	if (m_lstHitList.Count > 0)
	{
		MessageBox.Show("You hit " + m_lstHitList.Count + " rectangles");
	}
}

We have expanded our hit-test area using an EllipseGeometry. When the user clicks on the grid, the program starts the hit-test process by calling HitTestCallback. The VisualTreeHelper will then walk through visuals from top to bottom. If it hits any rectangle,the rectangle will be added to m_lstHitList.

public HitTestResultBehavior HitTestCallback(HitTestResult htrResult)
{
	IntersectionDetail idDetail = ((GeometryHitTestResult)htrResult).IntersectionDetail;
	switch (idDetail)
	{
		case IntersectionDetail.FullyContains:
			m_lstHitList.Add((Rectangle)htrResult.VisualHit);
			return HitTestResultBehavior.Continue;
		case IntersectionDetail.Intersects:
			return HitTestResultBehavior.Continue;
		case IntersectionDetail.FullyInside:
			return HitTestResultBehavior.Continue;
		default:
			return HitTestResultBehavior.Stop;
	}
}

Usually, the HitTestResult object provides just a single property (VisualHit), but we can cast it to one of two derived types, depending on the type of hit-test we’re performing.
If we’re hit-testing a point, we can cast HitTestResult to PointHitTestResult, which provides a
PointHit property that returns the original point you used to perform the hit-test. If you are hit-testing a
Geometry object (or shape), as you are in this example, you can cast HitTestResult to
GeometryHitTestResult and get access to the IntersectionDetail property as example above.

The complete source code of this example you can download “Hit Testing Example

This example I extracted from book “Practical WPF Charts and Graphics“. Password: 4aedF0xFZN0BBPE

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.