This article discusses how to set up C# Windows Forms and Presentation Foundation projects using Visual Studio.
On Windows, the Leap Motion C# API is provided as a set of wrapper classes that reference a C-language shared library, LeapC.dll.
When your application calls a function in the Leap Motion C# API, the C# code calls the matching function defined in LeapC.dll. The Leap Motion libraries are designed to be loaded from the same directory as your application executable. You are expected to distribute the appropriate Leap Motion library with your application. The Leap Motion libraries are located in the lib folder of the Leap SDK package.
This section illustrates how to create a project from scratch. Most of the steps also apply to adding Leap Motion support to an existing project. The example uses Visual Studio 2012.
To add Leap Motion support to a new or existing project:
Make a LEAP_SDK system environment variable to point to your Leap Motion SDK folder. This step is optional, but does simplify creating references to the SDK files in your projects. You must restart your IDE after creating this variable or it will not be recognized. (As a reminder, you can create and change system environment variables from the Windows System Properties dialog.)
Open or create a new project of the desired Windows Forms or WPF type.
Either add the Leap Motion .NET source code from the SDK src folder, or add a reference to one of the pre-built .NET libraries:
Note: do not add any of the other Leap Motion library files as a reference.
Set the target platform to x86 or x64:
Edit the Post Build Event to copy the native libraries to the project’s target executable directory.
Important: If you are creating a 64-bit application, use the libraries in the lib\x64 directory of the SDK, not the 32-bit libraries in the lib\x86 directory.
Open the project properties page.
Select the Build Events page.
Click Edit Post-build...
Edit the event field to copy the libraries.
For 32-bit, x86 projects add:
xcopy /yr "$(LEAP_SDK)\lib\x86\LeapC.dll" "$(TargetDir)"
For 64-bit, x64 projects add:
xcopy /yr "$(LEAP_SDK)\lib\x64\LeapC.dll" "$(TargetDir)"
Note: if you didn’t create the LEAP_SDK environment variable, use the path to your SDK library in place of $(LEAP_SDK).
Add your source code...
The data of controls in a Windows Forms or WPF application can only be accessed from the main application thread. The Controller will post events to the window’s event loop if an appropriate SynchronizationContext is implemented (as should always be the case in Windows Forms and WPF applications). Otherwise, events are dispatched on the Controller’s background thread and you must handle synchronization with the GUI thread yourself.
To run either example, your form or window definition must have four Label controls with the names shown in the newFrameHandler() method, a 640x480 pixel Image or PictureBox control, and a TextBox or RichTextBox control.
using Leap;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace WinFormSample {
public partial class FrameDataForm : Form {
private byte[] imagedata = new byte[1];
private Controller controller = new Controller();
Bitmap bitmap = new Bitmap (640, 480, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
public FrameDataForm () {
InitializeComponent();
controller.EventContext = WindowsFormsSynchronizationContext.Current;
controller.FrameReady += newFrameHandler;
controller.ImageReady += onImageReady;
controller.ImageRequestFailed += onImageRequestFailed;
//set greyscale palette for image Bitmap object
ColorPalette grayscale = bitmap.Palette;
for (int i = 0; i < 256; i++) {
grayscale.Entries[i] = Color.FromArgb((int)255, i, i, i);
}
bitmap.Palette = grayscale;
}
void newFrameHandler (object sender, FrameEventArgs eventArgs) {
Frame frame = eventArgs.frame;
//The following are Label controls added in design view for the form
this.displayID.Text = frame.Id.ToString();
this.displayTimestamp.Text = frame.Timestamp.ToString();
this.displayFPS.Text = frame.CurrentFramesPerSecond.ToString();
this.displayHandCount.Text = frame.Hands.Count.ToString();
controller.RequestImages(frame.Id, Leap.Image.ImageType.DEFAULT, imagedata);
}
void onImageRequestFailed (object sender, ImageRequestFailedEventArgs e) {
if (e.reason == Leap.Image.RequestFailureReason.Insufficient_Buffer) {
imagedata = new byte[e.requiredBufferSize];
}
Console.WriteLine()"Image request failed: " + e.message);
}
void onImageReady (object sender, ImageEventArgs e) {
Rectangle lockArea = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(lockArea, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
byte[] rawImageData = imagedata;
System.Runtime.InteropServices.Marshal.Copy(rawImageData, 0, bitmapData.Scan0, e.image.Width * e.image.Height * 2 * e.image.BytesPerPixel);
bitmap.UnlockBits(bitmapData);
displayImages.Image = bitmap;
}
}
}
namespace WinFormSample {
partial class FrameDataForm {
private System.ComponentModel.IContainer components = null;
protected override void Dispose (bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent () {
this.displayID = new System.Windows.Forms.Label();
this.displayTimestamp = new System.Windows.Forms.Label();
this.displayFPS = new System.Windows.Forms.Label();
this.displayHandCount = new System.Windows.Forms.Label();
this.displayImages = new System.Windows.Forms.PictureBox();
this.debugText = new System.Windows.Forms.RichTextBox();
((System.ComponentModel.ISupportInitialize)(this.displayImages)).BeginInit();
this.SuspendLayout();
//
// displayID
//
this.displayID.AutoSize = true;
this.displayID.Location = new System.Drawing.Point(12, 9);
this.displayID.Name = "displayID";
this.displayID.Size = new System.Drawing.Size(35, 13);
this.displayID.TabIndex = 0;
this.displayID.Text = "label1";
//
// displayTimestamp
//
this.displayTimestamp.AutoSize = true;
this.displayTimestamp.Location = new System.Drawing.Point(111, 9);
this.displayTimestamp.Name = "displayTimestamp";
this.displayTimestamp.Size = new System.Drawing.Size(35, 13);
this.displayTimestamp.TabIndex = 1;
this.displayTimestamp.Text = "label2";
//
// displayFPS
//
this.displayFPS.AutoSize = true;
this.displayFPS.Location = new System.Drawing.Point(222, 9);
this.displayFPS.Name = "displayFPS";
this.displayFPS.Size = new System.Drawing.Size(35, 13);
this.displayFPS.TabIndex = 2;
this.displayFPS.Text = "label3";
//
// displayHandCount
//
this.displayHandCount.AutoSize = true;
this.displayHandCount.Location = new System.Drawing.Point(310, 9);
this.displayHandCount.Name = "displayHandCount";
this.displayHandCount.Size = new System.Drawing.Size(35, 13);
this.displayHandCount.TabIndex = 3;
this.displayHandCount.Text = "label4";
//
// displayImages
//
this.displayImages.Location = new System.Drawing.Point(15, 25);
this.displayImages.Name = "displayImages";
this.displayImages.Size = new System.Drawing.Size(640, 480);
this.displayImages.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.displayImages.TabIndex = 4;
this.displayImages.TabStop = false;
//
// debugText
//
this.debugText.Location = new System.Drawing.Point(15, 511);
this.debugText.Name = "debugText";
this.debugText.Size = new System.Drawing.Size(640, 165);
this.debugText.TabIndex = 5;
this.debugText.Text = "";
//
// FrameDataForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(670, 693);
this.Controls.Add(this.debugText);
this.Controls.Add(this.displayImages);
this.Controls.Add(this.displayHandCount);
this.Controls.Add(this.displayFPS);
this.Controls.Add(this.displayTimestamp);
this.Controls.Add(this.displayID);
this.Name = "FrameDataForm";
this.Text = "Frame Data";
((System.ComponentModel.ISupportInitialize)(this.displayImages)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label displayID;
private System.Windows.Forms.Label displayTimestamp;
private System.Windows.Forms.Label displayFPS;
private System.Windows.Forms.Label displayHandCount;
private System.Windows.Forms.PictureBox displayImages;
private System.Windows.Forms.RichTextBox debugText;
}
}
using Leap;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WPFSample {
public partial class MainWindow : Window {
private byte[] imagedata = new byte[1];
private Controller controller = new Controller();
WriteableBitmap bitmap;
public MainWindow () {
InitializeComponent();
//set greyscale palette for WriteableBitmap object
List<Color> grayscale = new List<Color>();
for (byte i = 0; i < 0xff; i++) {
grayscale.Add(Color.FromArgb(0xff, i, i, i));
}
BitmapPalette palette = new BitmapPalette(grayscale);
bitmap = new WriteableBitmap (640, 480, 72, 72, PixelFormats.Gray8, palette);
displayImages.Source = bitmap;
controller.EventContext = SynchronizationContext.Current;
controller.FrameReady += newFrameHandler;
controller.ImageReady += onImageReady;
controller.ImageRequestFailed += onImageRequestFailed;
}
void newFrameHandler (object sender, FrameEventArgs eventArgs) {
Leap.Frame frame = eventArgs.frame;
//The following are Label controls added in design view for the form
this.displayID.Content = frame.Id.ToString();
this.displayTimestamp.Content = frame.Timestamp.ToString();
this.displayFPS.Content = frame.CurrentFramesPerSecond.ToString();
this.displayHandCount.Content = frame.Hands.Count.ToString();
controller.RequestImages(frame.Id, Leap.Image.ImageType.DEFAULT, imagedata);
}
void onImageRequestFailed (object sender, ImageRequestFailedEventArgs e) {
if (e.reason == Leap.Image.RequestFailureReason.Insufficient_Buffer) {
imagedata = new byte[e.requiredBufferSize];
}
debugText.AppendText("Image request failed: " + e.message + "\n");
}
void onImageReady (object sender, ImageEventArgs e) {
bitmap.WritePixels(new Int32Rect(0, 0, 640, 480), imagedata, 640, 0);
}
}
}
<Window x:Class="WPFSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Frame Data" Height="751.567" Width="726.881">
<Grid>
<Label x:Name="displayID" Content="Label" HorizontalAlignment="Left" Margin="35,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="displayTimestamp" Content="Label" HorizontalAlignment="Left" Margin="107,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="displayFPS" Content="Label" HorizontalAlignment="Left" Margin="197,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="displayHandCount" Content="Label" HorizontalAlignment="Left" Margin="287,10,0,0" VerticalAlignment="Top"/>
<Image x:Name="displayImages" HorizontalAlignment="Left" Height="480" VerticalAlignment="Top" Width="640" Margin="10,36,0,0"/>
<TextBox x:Name="debugText" HorizontalAlignment="Left" Height="178" Margin="10,533,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="699"/>
</Grid>
</Window>