A criação de botões customizados para o Kinect for Windows é bastante simples de ser feita. Para tanto faremos uso de recursos nativos do WPF, orientação a objetos e o Kinect for Windows Developer Toolkit.
Por padrão, o Windows Developer Toolkit contém dois modelos de botões: o KinectTileButton e o KinectCircleButton. O KinectTileButton geralmente é utilizado para navegação entre páginas, enquanto que o KinectCircleButton é utilizado para execução de ações e barras de ferramentas.
Para ampliar as opções existentes é preciso criar controles customizados que herdem as características básicas de um botão. O Kinect for Windows Developer Toolkit contém uma classe chamada KinectButtonBase que funciona como base para criação de todo e qualquer botão que se comunica com o Kinect for Windows (por meio do WPF).
Observação: neste exemplo foi utilizada a versão 1.8 do Kinect for Windows Developer Toolkit, mas este exemplo é totalmente compatível com a versão 1.7 do mesmo developer toolkit.
Neste exemplo criaremos um botão com uma imagem de fundo. Este botão irá disparar dois eventos não nativos: um evento quando a mão passar por cima do controle (hand enter) e outro quando a mão deixar o controle (hand leave).
Para fazer o download desse exemplo utilize este link: Criando botões para o Kinect for Windows
Criando a classe do botão
Neste primeiro ponto será preciso criar a classe do botão customizado. Neste exemplo criarei um botão chamado “MyCustomKinectButton”.
O código do botão é apresentado abaixo:
using Microsoft.Kinect.Toolkit.Controls;
using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
public class MyCustomKinectButton : KinectButtonBase {
public event EventHandler OnHandPointerEnter;
public event EventHandler OnHandPointerLeave;
public MyCustomKinectButton() {
InitializeControl();
}
private void InitializeControl() {
KinectRegion.AddHandPointerEnterHandler(this, OnHandPointerEnterHandler);
KinectRegion.AddHandPointerLeaveHandler(this, OnHandPointerLeaveHandler);
this.MouseEnter += OnMouseEnter;
this.MouseLeave += OnMouseLeave;
this.Style = (Style)Application.Current.FindResource(typeof(MyCustomKinectButton));
}
private void OnHandPointerLeaveHandler(object sender, HandPointerEventArgs e) {
ExecuteHandPointerLeave(this, e);
}
private void OnMouseLeave(object sender, System.Windows.Input.MouseEventArgs e) {
ExecuteHandPointerLeave(this, null);
}
private void OnHandPointerEnterHandler(object sender, HandPointerEventArgs e) {
ExecuteHandPointerEnter(this, e);
}
private void OnMouseEnter(object sender, System.Windows.Input.MouseEventArgs e) {
ExecuteHandPointerEnter(this, null);
}
private void ExecuteHandPointerEnter(object sender, HandPointerEventArgs e) {
if (this.OnHandPointerEnter != null) {
this.OnHandPointerEnter(this, e);
}
}
private void ExecuteHandPointerLeave(object sender, HandPointerEventArgs e) {
if (this.OnHandPointerLeave != null) {
this.OnHandPointerLeave(this, e);
}
}
public override void OnApplyTemplate() {
ConfigImage();
base.OnApplyTemplate();
}
private void ConfigImage() {
StackPanel mainPanel = this.GetTemplateChild("MainPanel") as StackPanel;
Image mainImage = this.GetTemplateChild("MainImage") as Image;
if (mainPanel != null && mainImage != null) {
SetImageSize(mainImage);
SetImage(mainImage);
}
}
private void SetImageSize(Image mainImage) {
if (this.ActualWidth > 0 && this.ActualHeight > 0) {
mainImage.Width = this.ActualWidth;
mainImage.Height = this.ActualHeight;
}
else if (this.Width > 0 && this.Height > 0) {
mainImage.Width = this.Width;
mainImage.Height = this.Height;
}
}
private static void SetImage(Image mainImage) {
Assembly currentAssembly = Assembly.GetAssembly(typeof(MyCustomKinectButton));
using (Stream s = currentAssembly.GetManifestResourceStream("WpfKinect.Images.visualstudio.png")) {
BitmapImage img = new BitmapImage();
img.BeginInit();
img.StreamSource = s;
img.EndInit();
mainImage.Source = img;
}
}
}
Pontos a serem destacados:
– MyCustomKinectButton herda da classe KinectButtonBase.
– Existem dois eventos neste botão: OnHandPointerEnter e OnHandPointerLeave. Um deles é chamado quando a mão passa por cima do controle e o outro quando a mão deixa o controle. Veja que para a assinatura destes eventos temos de utilizar a classe KinectRegion (http://msdn.microsoft.com/en-us/library/microsoft.kinect.toolkit.controls.kinectregion_members.aspx). Além disso, associei estes mesmos dois eventos com os eventos MouseEnter e o MouseLeave existentes no controle. Desta forma o controle não perde o seu funcionamento quando usado com o mouse.
– O método OnApplyTemplate é sobrescrito: note que no construtor desta classe a propriedade Style é definida, carregando recursos associados com o tipo de dados MyCustomKinectButton. Além disso, no método OnApplyTemplate é definido o tamanho da imagem e a imagem que será carregada.
A imagem que será carregada e associada com o controle está embutida dentro do assembly do controle como um Embedded Resource – veremos como adicionar um Embedded Resource mais a frente – o código que faz acesso ao Embedded Resource pode ser encontrado no método SetImage.
Criando o dicionário de recursos
Um dicionário de recursos (resource dictionary) é um dicionário de objetos que podem ser utilizados via managed code e/ou via XAML. Geralmente, dicionários de recursos são utilizados na definição de templates para controles – e este será o seu uso neste exemplo.
Como padrão, crio um arquivo chamado Generic.xaml dentro de uma pasta chamada Themes, como na imagem abaixo:
O conteúdo do arquivo Generic.xaml pode ser encontrado logo abaixo:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ck="clr-namespace:WpfKinect"
xmlns:local="clr-namespace:WpfKinect">
<Style TargetType="{x:Type local:MyCustomKinectButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCustomKinectButton">
<StackPanel x:Name="MainPanel">
<Image x:Name="MainImage" ClipToBounds="True">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Black" Opacity="1" BlurRadius="10"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Note que o arquivo se baseia no namespace do assembly corrente, e também perceba que o estilo (theme) descrito via XAML é orientado ao objeto que será renderizado, no caso o objeto MyCustomKinectButton.
Detalhe adicional no XAML: veja que existe uma trigger para a propriedade IsMouseOver. Essa trigger quando acionada inicia uma animação que inclui uma sombra ao redor da imagem.
Abaixo as imagens com o efeito de sombra (esquerda) e sem o defeito de sombra (direita).
Imagem incorporada ao assembly
Outro ponto importante a ser discutido é a origem da imagem. Neste exemplo fizemos uso de uma imagem incorporada ao assembly. Essa característica é vantajosa, pois torna a solução compacta e centralizada, sem exigir a existência de artefatos externos. Por outro lado, a incorporação de imagens, áudios e vídeos (como qualquer outro tipo de artefato) a um assembly pode ser negativa, pois pode tornar o tamanho do assembly maior.
Incorporar uma imagem a um assembly é simples, basta adicionar a imagem ao projeto (como na imagem abaixo), clicar com o botão direito sobre a imagem, acessar as propriedades da imagem e definir a propriedade “Build Action” como “Embedded Resource” (como em uma das imagens abaixo).
O código necessário para extração dos bytes da imagem do binário pode ser encontrado no método SetImage, e também é apresentado abaixo:
private static void SetImage(Image mainImage) {
Assembly currentAssembly = Assembly.GetAssembly(typeof(MyCustomKinectButton));
using (Stream s = currentAssembly.GetManifestResourceStream("WpfKinect.Images.visualstudio.png")) {
BitmapImage img = new BitmapImage();
img.BeginInit();
img.StreamSource = s;
img.EndInit();
mainImage.Source = img;
}
}
Por
MSc. Fernando Henrique Inocêncio Borba Ferreira
Microsoft Most Valuable Professional – Visual C#
Referências:
http://msdn.microsoft.com/en-us/library/cc903952(v=vs.95).aspx
http://msdn.microsoft.com/en-us/library/microsoft.kinect.toolkit.controls.kinectbuttonbase.aspx