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