If broken it is, fix it you should

Using the powers of the debugger to solve the problems of the world - and a bag of chips    by Tess Ferrandez, ASP.NET Escalation Engineer (Microsoft)

Kinect SDK for Windows – Hover Button / Hover Control

Kinect SDK for Windows – Hover Button / Hover Control

Rate This
  • Comments 15


This is part of what will be a series of posts on Kinect SDK demos and howtos, go here to see the whole list.

In this post I will talk about how you can create a simple and versatile Hover Button / Hover Control for Kinect with a Metro look. 
The control is largely based on the Kinect Buttons from Shai Raiten, so all cred goes to him, this is just a customization of his work.

Hover button UI

The HoverButton control can have either an image or text or both and can be resized to fit your needs. It features a Click event that will occurr after a hovering for 2 seconds.

Below are two examples of how you could use the HoverButton control…

In the first example I have used 10 HoverButtons to create a metro style UI for
getting into different demos and I have used both images and just buttons with text together so you can see the difference. 

The center button is currently selected and the white overlay slowly covers the whole button… when the button is fully covered a Click event is fired.

KinectHub

In the second example I have used only images to show a completely different effect with the same hover button where the 3rd avatar is being selected.

 

Select-avatar

Creating the HoverButton

  1. Create a new WPF user control library (KinectControls) and add a new UserControl called HoverButton
  2. Add Dependency Properties

The dependency properties will be used to bind xaml controls in the HoverButton so that we can customize the button from the using application

Add a dependency property for BackgroundColor (the color of the button) like this

	public Brush BackgroundColor{
		get{ return (Brush)this.GetValue(BackgroundColorProperty);}
		set{ this.SetValue(BackgroundColorProperty, value);}
	} 
	public static readonly DependencyProperty BackgroundColorProperty = DependencyProperty.Register(
	   "BackgroundColor", typeof(Brush), typeof(HoverButton), new PropertyMetadata(Brushes.Red));

Do the same for the following properties

Property Type Default value
HoverColor Brush Brushes.White
TextColor Brush Brushes.White
TextSize int 36
Image string “”

3.  Create the XAML for the HoverButton and bind to the relevant controls

    <grid background="{Binding BackgroundColor}">
        <img stretch="Fill" source="{Binding Image}" />
        <textblock verticalalignment="Center" horizontalalignment="Center" fontfamily="Segoe UI" foreground="{Binding TextColor}" 
fontsize="{Binding TextSize}" text="{Binding Text}" /> <rectangle verticalalignment="Top" horizontalalignment="Left" height="0" rendertransformorigin="0,0" opacity="0.3"
width="{Binding Width}" fill="{Binding HoverColor}" name="Mask" /> </grid>

4. Set up the data binding in the HoverButton constructor to bind the properties

    this.DataContext = this;

Now we are done with the properties, it is time to do the hover magic… for this we will use the Mask rectangle and animate it to grow when the cursor enters the button, and reverse the animation when the cursor exits

5. Add the following fields to the HoverButton class

        private Duration hoverDuration = new Duration(new TimeSpan(0, 0, 2));
        private Duration reverseDuration = new Duration(new TimeSpan(0, 0, 1));
        private DoubleAnimation maskAnimation;
        private bool isHovering = false;

the hoverDuration is the time it will take to do the full hover

6.  Create two methods to start hovering and stop hovering that initiate and reverse the animation

        private void StartHovering()
        {
            double maxFillHeight = this.ActualHeight;

            if (!isHovering)
            {
                isHovering = true;
                maskAnimation = new DoubleAnimation(Mask.ActualHeight, maxFillHeight, hoverDuration);
                maskAnimation.Completed += new EventHandler(maskAnimation_Completed);
                Mask.BeginAnimation(Canvas.HeightProperty, maskAnimation);
            }
        }

        private void StopHovering()
        {
            if (isHovering)
            {
                isHovering = false;
                maskAnimation.Completed -= maskAnimation_Completed;
                maskAnimation = new DoubleAnimation(Mask.ActualHeight, 0, reverseDuration);
                Mask.BeginAnimation(Canvas.HeightProperty, maskAnimation);
            }
        }

7. Create a Click event that will fire when we have hovered for the hoverDuration

        public delegate void ClickHandler(object sender, EventArgs e);
        public event ClickHandler Click;

8.  Finally create a maskAnimation_Completed eventhandler we will fire the Click event

        void maskAnimation_Completed(object sender, EventArgs e)
        {
            isHovering = false;
            if (Click != null)
                Click(this, e);
            Mask.BeginAnimation(Canvas.HeightProperty, null);
        }

Now the animation logic is complete, we just have to trigger the StartHovering and StopHovering methods, and we will do this by manually checking when a cursor enters and exits the button area… from user code we will have to call a function Check whenever the cursor moves…

9. Implement the Check and IsCursorInButton methods that check if the cursor (a framework element) is inside or outside the button

        public bool Check(FrameworkElement cursor)
        {
            if (IsCursorInButton(cursor))
            {
                this.StartHovering();
                return true;
            }
            else
            {
                this.StopHovering();
                return false;
            }
        }

        private bool IsCursorInButton(FrameworkElement cursor)
        {
            try
            {
                //Cursor midpoint location
                Point cursorTopLeft = cursor.PointToScreen(new Point());
                double cursorCenterX = cursorTopLeft.X + (cursor.ActualWidth / 2);
                double cursorCenterY = cursorTopLeft.Y + (cursor.ActualHeight / 2);

                //Button location
                Point buttonTopLeft = this.PointToScreen(new Point());
                double buttonLeft = buttonTopLeft.X;
                double buttonRight = buttonLeft + this.ActualWidth;
                double buttonTop = buttonTopLeft.Y;
                double buttonBottom = buttonTop + this.ActualHeight;

                if (cursorCenterX < buttonLeft || cursorCenterX > buttonRight)
                    return false;

                if (cursorCenterY < buttonTop || cursorCenterY > buttonBottom)
                    return false;

                return true;
            }
            catch
            {
                return false;
            }
        }

And with that we are done with the button…

 

As a helper I have also created a HandCursor control that is extremely simple with the following XAML

    <Ellipse Height="40" Width="40" Name="hand">
        <Ellipse.Fill>
            <ImageBrush ImageSource="/KinectControls;component/Images/hand.png" />
        </Ellipse.Fill>
    </Ellipse>

… and just one function  (note: for this to work we need to include the Microsoft.Research.Kinect and Coding4Fun.Kinect.WPF dlls and add using statements for Microsoft.Research.Kinect.Nui and Coding4Fun.Kinect.WPF)

        public void SetPosition(Joint joint)
        {
            Joint scaledJoint = joint.ScaleTo(800, 600, 0.5f, 0.5f);
            Canvas.SetLeft(this, scaledJoint.Position.X);
            Canvas.SetTop(this, scaledJoint.Position.Y);
        }

 

Using the HoverButton

1. Add a reference to the KinectControls library in the application

2. Add the following XAML to the Window to add the HoverButton and HandCursor

    <Canvas Background="Black">
        <ctrl:HoverButton Name="btn1" BackgroundColor="Blue" Text="Test" HoverColor="White" Height="100" Width="200" Click="HoverButton_Click" Canvas.Top="100" 
Canvas.Left="100"/> <ctrl:HandCursor Name="hand" Canvas.Top="0" Canvas.Left="0"/> </Canvas>

3. Add a kinect Runtime to the window class

        Runtime kinect = null;

4. Initialize the kinect in window_loaded and uninitialize it when the window closes (this uses the RuntimeExtensions methods shown in the previous post

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            kinect = new Runtime();
            kinect.Initialize(RuntimeOptions.UseSkeletalTracking);
            kinect.SetSmoothTransform();
            kinect.SkeletonFrameReady += new EventHandler(kinect_SkeletonFrameReady); this.Cursor = Cursors.None; } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (kinect != null) kinect.Uninitialize(); kinect = null; }

5. And the last thing we need to do before we can try it out is to move the cursor in the SkeletonFrameReady event, as well as checking the button

        void kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            SkeletonData data = e.SkeletonFrame.GetFirstTrackedSkeleton();
            Joint handJoint = data.Joints[JointID.HandRight];

            hand.SetPosition(handJoint);
            btn1.Check(hand);
        }

Voila, we are ready to rock!

Have fun,
Tess

Attachment: KinectControls.zip
  • Welcome back after such a long absence!

  • I got some error when compile your source code...

    at Joint handJoint = data.Joints[JointID.HandRight];

    Object reference not set to an instance of an object.

  • I got some error when compile your source code...

    at Joint handJoint = data.Joints[JointID.HandRight];

    Object reference not set to an instance of an object.

  • Leo,  it should compile fine as long as you have all the references to coding4fun for example and the extension methods.

    Also remember that it is built with the latest SDK release currently available (not the first Beta)

  • A little off topic, but how did you get the XBOX Avatars in your windows App for the second example.  Is it just an image, because I have been trying to make Avatars work...

  • aidesigner,  yepp they are just images, it was shown as an example of how the hover button can work in a less button-looking scenario

  • @Leo, @Tess. I got the same problem and tried to reboot, and referenced the .dll's again. Didn't work. Then I turned the debug mode to x86 instead of 'All'. This worked! Thanks for your code samples.

  • Don`t work 4 me... I have te same errors than @leo

  • Hi There,

    Firstly, thank you for sharing this. I am able to use the controls without any problems when the buttons are created manually, but if I dynamically generate the hover buttons and then use a method to iterate over them (say a set of 10) and call the Check() method on every frame, performance is severely impacted. I wonder if anyone has encountered / overcome this?

    Sam.

  • @Leo, @Judavi

    You should check that you are retrieving skeleton data  before trying to access it... (if skeletondata !=null)

  • Cool.. Nice example..

    If you add

    hand.Width = 150 / joint.Position.Z;

    hand.Height = 150 / joint.Position.Z;

    In setposition of the handcursor - the cursor hand resizes.

    You can then

    add

     if (cursor.ActualWidth<120)

                       return false;

    to IsCursorInButton in hoverbutton

    This will extend the button so that you can push your hand to towards the button to trigger..

  • Mike, nice extension of the hover button.  How did you come up with the seemingly arbitrary values of 150 and 120 in your code snippet above?

  • hi everyone...

    i'm trying ti run this demo. but when kinect_skeletonframeready event occurs, the hand image disappears and i'm unable to proceed with demo then. Please share with me, why this is happening??

  • hi everyone,

    sorry for my bad English.

    this project is using SDK BETA version, but now is SDK v1.

    anyone have SDK V1 version of this project?

    hope to read reply from you guys.

    thanks :)

  • I second what kindrick asked. I'd like to see this updated for the release version of the Kinect SDK

Page 1 of 1 (15 items)
Leave a Comment
  • Please add 5 and 4 and type the answer here:
  • Post