Bonjour à tous,
Je suis entrain de développer une application windows (wpf) avec le sdk Kinect. Le but étant de contrôler l'application par le biais de la kinect (Donc pas de souris, ni de clavier). Les gestes swipe gauche et swipe droit remplaceront les évenements gauche et droite du clavier. Dans un premier temps, je pense utiliser les touches du clavier pour naviguer dans le menu.
Voici ce que j'aimerai réaliser :
Ma page d'accueil contient une liste de catégorie ou j'aimerai pouvoir naviguer au sein de ma listbox (navigation gauche et droite) et pouvoir boucler sur ma listbox (cad, lorsque j'arrive à la fin, revenir au premier).
Par conséquent, je voudrai sélectionner la première catégorie au chargement de la vue et ainsi pouvoir naviguer dans mon menu. Pour montrer à l'utilisateur quelle est la catégorie sélectionnée, j'aimerai avoir un état "Hover". Pour le moment, je suis obligé d'appuyer sur la touche tab pour avoir le focus sur les catégories. Je ne sais pas comment m'y prendre.
Une image sera plus simple pour la compréhension:
Voici mon code:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 <Grid> <!-- IsTabStop="False" KeyboardNavigation.TabNavigation="Cycle" --> <ListBox x:Name="CategoryListBox" Margin="10,50,10,20" ItemsSource="{Binding Categories, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CategorySelectionControl}}}" BorderThickness="0" Background="{x:Null}" SelectionMode="Multiple" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Center" VerticalAlignment="Center" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" SelectionChanged="OuterListBox_SelectionChanged" Visibility="Visible"> <ListBox.Style> <Style TargetType="{x:Type ListBox}"> <Setter Property="IsHitTestVisible" Value="False" /> <Setter Property="Opacity" Value="0" /> <Style.Triggers> <DataTrigger Binding="{Binding SelectedCategory, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CategorySelectionControl}}}" Value="{x:Null}"> <Setter Property="IsHitTestVisible" Value="True" /> <DataTrigger.EnterActions> <BeginStoryboard x:Name="FadeIn"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers> </Style> </ListBox.Style> <ListBox.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Columns="4" VerticalAlignment="Center" HorizontalAlignment="Center" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Margin" Value="30" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Stretch" /> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListBox.ItemContainerStyle> <ListBox.ItemTemplate> <DataTemplate> <local:HoverDwellButton x:Name="Button" HoverClick="Button_HoverClick" Height="300" Width="345" Style="{StaticResource EnlargingHoverDwellButton}" VerticalAlignment="Top" GotFocus="CategoryListBox_GotFocus" LostFocus="CategoryListBox_LostFocus" > <local:HoverDwellButton.RenderTransform> <ScaleTransform ScaleX="1.0" ScaleY="1.0" /> </local:HoverDwellButton.RenderTransform> <Grid x:Name="ButtonGrid"> <Grid.Background> <SolidColorBrush x:Name="ItemGridBackground" Color="Transparent" /> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="100" /> <RowDefinition Height="200" /> </Grid.RowDefinitions> <Border Grid.Row="1" x:Name="ImageBorder" BorderBrush="{StaticResource SelectedColorBrush}" BorderThickness="0"> <Image x:Name="img" DataContext="{Binding ContentImage}" Margin="1" Source="{Binding}" RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="Top" Stretch="UniformToFill"> <Image.Style> <Style> <Style.Triggers> <DataTrigger Binding="{Binding }" Value="{x:Null}"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard Storyboard.TargetProperty="Opacity"> <DoubleAnimation From="0" To="1" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <BeginStoryboard> <Storyboard Storyboard.TargetProperty="Opacity"> <DoubleAnimation From="1" To="0" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers> </Style> </Image.Style> </Image> </Border> <TextBlock x:Name="CategoryTitle" Text="{Binding Title}" Foreground="Black" FontFamily="Segoe UI" FontWeight="Light" TextAlignment="Left" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="1,0,0,10" FontSize="40" /> </Grid> </local:HoverDwellButton> <DataTemplate.Triggers> <DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard Storyboard="{StaticResource EnableGridColor}" /> </DataTrigger.EnterActions> <!--<DataTrigger.ExitActions> <BeginStoryboard Storyboard="{StaticResource DisableGridColor}" /> </DataTrigger.ExitActions>--> </DataTrigger> <DataTrigger Binding="{Binding IsSelected, ElementName=Button}" Value="true"> <Setter TargetName="CategoryTitle" Property="TextBlock.Foreground" Value="{StaticResource SelectedColorBrush}" /> <Setter TargetName="CategoryTitle" Property="TextBlock.FontWeight" Value="Bold" /> <Setter TargetName="ImageBorder" Property="Border.BorderThickness" Value="8" /> </DataTrigger> <DataTrigger Binding="{Binding IsHoveredOver, ElementName=Button}" Value="true"> <Setter TargetName="CategoryTitle" Property="TextBlock.Foreground" Value="{StaticResource SelectedColorBrush}" /> <Setter TargetName="ImageBorder" Property="Border.BorderThickness" Value="4" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 namespace Kinect { /// <summary> /// Interaction logic for CategorySelectionControl.xaml /// </summary> public partial class CategorySelectionControl : UserControl { public static readonly DependencyProperty CategoriesProperty = DependencyProperty.Register("Categories", typeof(IEnumerable<Category>), typeof(CategorySelectionControl), new UIPropertyMetadata(null)); public static readonly DependencyProperty SelectedCategoryProperty = DependencyProperty.Register("SelectedCategory", typeof(Category), typeof(CategorySelectionControl), new UIPropertyMetadata(null)); public CategorySelectionControl() { this.InitializeComponent(); } public IEnumerable<Category> Categories { get { return (IEnumerable<Category>)this.GetValue(CategoriesProperty); } set { this.SetValue(CategoriesProperty, value); } } public Category SelectedCategory { get { return (Category)this.GetValue(SelectedCategoryProperty); } set { this.SetValue(SelectedCategoryProperty, value); } } public void TransitionToGrid(object selectedItem) { this.Visibility = Visibility.Visible; this.IsHitTestVisible = false; // move all grid items to position of selected item var pt = new Point(); var li = this.CategoryListBox.ItemContainerGenerator.ContainerFromItem(selectedItem) as ListBoxItem; if (li != null) { pt = li.TranslatePoint(new Point(li.ActualWidth / 2, li.ActualHeight / 2), this); } var sb = new Storyboard(); double speedFactor = 0.0005; foreach (Category item in this.CategoryListBox.Items) { li = this.CategoryListBox.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem; if (li != null) { Point itemPt = li.TranslatePoint(new Point(li.ActualWidth / 2, li.ActualHeight / 2), this); double diffX = pt.X - itemPt.X; double diffY = pt.Y - itemPt.Y; li.RenderTransform = new TranslateTransform(diffX, diffY); var d = new Duration(TimeSpan.FromSeconds(Math.Abs(diffY) * speedFactor)); var animateY = new DoubleAnimation(0, d); animateY.BeginTime = TimeSpan.FromSeconds(0.5); Storyboard.SetTarget(animateY, li); Storyboard.SetTargetProperty(animateY, new PropertyPath("RenderTransform.Y")); sb.Children.Add(animateY); var d2 = new Duration(TimeSpan.FromSeconds(Math.Abs(diffX) * speedFactor)); var animateX = new DoubleAnimation(0, d2); animateX.BeginTime = d.TimeSpan; Storyboard.SetTarget(animateX, li); Storyboard.SetTargetProperty(animateX, new PropertyPath("RenderTransform.X")); sb.Children.Add(animateX); var d3 = new Duration(d.TimeSpan + d2.TimeSpan); var animateOpacity = new DoubleAnimation(1, d3); animateOpacity.BeginTime = animateY.BeginTime; Storyboard.SetTarget(animateOpacity, li); Storyboard.SetTargetProperty(animateOpacity, new PropertyPath("Opacity")); sb.Children.Add(animateOpacity); } } sb.Completed += this.TransitionToGridCompleted; sb.Begin(); } internal Point GetCenterOfSelectedItem(object selectedItem) { var pt = new Point(); if (selectedItem != null) { var li = this.CategoryListBox.ItemContainerGenerator.ContainerFromItem(selectedItem) as ListBoxItem; if (li != null) { pt = li.PointToScreen(new Point(li.ActualWidth / 2, li.ActualHeight / 2)); } } return pt; } private void TransitionToGridCompleted(object sender, EventArgs e) { this.IsHitTestVisible = true; if (this.CategoryListBox.Items.Count > 0) { FocusManager.SetFocusedElement(CategoryListBox, (ListBoxItem)this.CategoryListBox.ItemContainerGenerator.ContainerFromIndex(0)); IInputElement focusedElement = FocusManager.GetFocusedElement(CategoryListBox); } } private void UserControl_OnPreviewKeyUp(object sender, KeyEventArgs e) { switch (e.Key) { case Key.Left: this.MoveFocus(FocusNavigationDirection.Previous); break; case Key.Right: this.MoveFocus(FocusNavigationDirection.Next); break; default: break; } } private void MoveFocus(FocusNavigationDirection direction) { var request = new TraversalRequest(direction); this.MoveFocus(request); } private void ImageDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { var img = sender as Image; if (img != null) { var animateOpacity = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromMilliseconds(300))); animateOpacity.AutoReverse = true; animateOpacity.FillBehavior = FillBehavior.Stop; img.BeginAnimation(CategorySelectionControl.OpacityProperty, animateOpacity); } } private void Button_HoverClick(object sender, HandInputEventArgs e) { MessageBox.Show("Button_HoverClick"); /*var selected = ((HoverDwellButton)sender).DataContext as Category; if (selected != null) { this.SelectedCategory = selected; }*/ } private void Button_PreviewKeyUp(object sender, KeyEventArgs e) { switch (e.Key) { case Key.Left: MessageBox.Show("Button_PreviewKeyUp : " + e.Key.ToString()); break; case Key.Right: MessageBox.Show("Button_PreviewKeyUp : " + e.Key.ToString()); break; case Key.Enter: MessageBox.Show("Button_PreviewKeyUp : " + e.Key.ToString()); /*var selected = ((HoverDwellButton)sender).DataContext as Category; if (selected != null) { this.SelectedCategory = selected; * //((HoverDwellButton) sender).IsHoveredOver = true; }*/ break; default: break; } } private void CategoryListBox_GotFocus(object sender, RoutedEventArgs e) { this.CategoryListBox.SelectedItem = (sender as HoverDwellButton).DataContext; ((HoverDwellButton)sender).IsHoveredOver = true; } private void CategoryListBox_LostFocus(object sender, RoutedEventArgs e) { ((HoverDwellButton)sender).IsHoveredOver = false; } private void OuterListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { object item = CategoryListBox.SelectedItem; if (item == null) { MessageBox.Show("No item currently selected."); } else { MessageBox.Show(item.ToString()); } } } }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 namespace Kinect { public class HoverDwellButton : ContentControl, IDisposable { public static readonly RoutedEvent HoverClickEvent = EventManager.RegisterRoutedEvent("HoverClick", RoutingStrategy.Bubble, typeof(EventHandler<HandInputEventArgs>), typeof(HoverDwellButton)); public static readonly DependencyProperty IsHoveredOverProperty = DependencyProperty.Register("IsHoveredOver", typeof(bool), typeof(HoverDwellButton), new UIPropertyMetadata(false)); // Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(HoverDwellButton), new UIPropertyMetadata(false)); // Using a DependencyProperty as the backing store for Magnetic. This enables animation, styling, binding, etc... public static readonly DependencyProperty MagneticProperty = DependencyProperty.Register("Magnetic", typeof(bool), typeof(HoverDwellButton), new UIPropertyMetadata(false)); private readonly List<HandHoverTimer> trackedHandHovers = new List<HandHoverTimer>(); private SoundPlayer soundPlayerOnClick; private SoundPlayer soundPlayerOnEnter; [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Overriding metadata must occur within a static constructor")] static HoverDwellButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(HoverDwellButton), new FrameworkPropertyMetadata(typeof(HoverDwellButton))); } public HoverDwellButton() { KinectController.AddPreviewHandEnterHandler(this, this.OnPreviewHandEnter); KinectController.AddPreviewHandLeaveHandler(this, this.OnPreviewHandLeave); } public event EventHandler<HandInputEventArgs> HoverClick { add { this.AddHandler(HoverClickEvent, value); } remove { this.RemoveHandler(HoverClickEvent, value); } } public bool IsHoveredOver { get { return (bool)this.GetValue(IsHoveredOverProperty); } set { this.SetValue(IsHoveredOverProperty, value); } } public bool IsSelected { get { return (bool)this.GetValue(IsSelectedProperty); } set { this.SetValue(IsSelectedProperty, value); } } public bool Magnetic { get { return (bool)this.GetValue(MagneticProperty); } set { this.SetValue(MagneticProperty, value); } } #region IDisposable Members public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } #endregion public void InvokeHoverClick(HandPosition hand) { if (hand != null) { hand.IsInteracting = false; HandHoverTimer timer = this.trackedHandHovers.FirstOrDefault(h => h.Hand.Equals(hand)); if (timer != null) { timer.Stop(); this.trackedHandHovers.Remove(timer); } } this.IsSelected = true; if (this.soundPlayerOnClick != null) { this.soundPlayerOnClick.Play(); } var t = new DispatcherTimer(); t.Interval = TimeSpan.FromSeconds(0.6); t.Tick += (o, s) => { t.Stop(); var clickArgs = new HandInputEventArgs(HoverClickEvent, this, hand); this.RaiseEvent(clickArgs); this.IsSelected = false; }; t.Start(); } public TimeSpan GetTimeRemaining(HandPosition hand) { HandHoverTimer timer = this.trackedHandHovers.FirstOrDefault(h => h.Hand.Equals(hand)); if (timer != null) { return timer.TimeRemaining; } return TimeSpan.MaxValue; } protected virtual void Dispose(bool disposing) { if (disposing) { if (this.soundPlayerOnEnter != null) { this.soundPlayerOnEnter.Dispose(); this.soundPlayerOnEnter = null; } if (this.soundPlayerOnClick != null) { this.soundPlayerOnClick.Dispose(); this.soundPlayerOnClick = null; } KinectController.RemovePreviewHandEnterHandler(this, this.OnPreviewHandEnter); KinectController.RemovePreviewHandLeaveHandler(this, this.OnPreviewHandLeave); } } private void OnPreviewHandEnter(object sender, HandInputEventArgs args) { if (this.trackedHandHovers.FirstOrDefault(t => t.Hand.Equals(args.Hand)) == null) { this.IsHoveredOver = true; /*if (this.SoundOnEnter.Length > 0) { if (this.soundPlayerOnEnter == null) { Assembly a = Assembly.GetExecutingAssembly(); Stream s = a.GetManifestResourceStream("Microsoft.Samples.Kinect.BasicInteractions.Resources.Sounds." + this.SoundOnEnter); this.soundPlayerOnEnter = new SoundPlayer(s); } this.soundPlayerOnEnter.Play(); }*/ args.Hand.IsInteracting = true; if (this.Magnetic) { // set the X and Y of the hand so it is centered over the button var element = this.Content as FrameworkElement; if (element != null) { var pt = new Point(element.ActualWidth / 2, element.ActualHeight / 2); Point lockedPoint = element.PointToScreen(pt); args.Hand.X = (int)lockedPoint.X; args.Hand.Y = (int)lockedPoint.Y; } args.Hand.Magnetized = true; } var timer = new HandHoverTimer(DispatcherPriority.Normal, this.Dispatcher); timer.Hand = args.Hand; timer.Interval = TimeSpan.FromMilliseconds(Settings.Default.SelectionTime); timer.Tick += (o, s) => { this.InvokeHoverClick(args.Hand); }; this.trackedHandHovers.Add(timer); timer.Start(); } args.Handled = true; } private void OnPreviewHandLeave(object sender, HandInputEventArgs args) { if (Application.Current != null && Application.Current.MainWindow != null) { IInputElement result = Application.Current.MainWindow.InputHitTest(new Point(args.Hand.X, args.Hand.Y)); var parent = Utility.FindParent<HoverDwellButton>(result); if (parent != this) { this.RemoveHand(args.Hand); args.Handled = true; } } } private void RemoveHand(HandPosition hand) { this.IsHoveredOver = false; hand.IsInteracting = false; hand.Magnetized = false; // Stop active hover timer (if it exists) for this hand. HandHoverTimer timer = this.trackedHandHovers.FirstOrDefault(h => h.Hand.Equals(hand)); if (timer != null) { timer.Stop(); this.trackedHandHovers.Remove(timer); } } } }
J'ésepere que mes explications sont claires. Si besoin, n'hésitez pas à me demander plus d'informations.
PS: je me base sur l'exemple donné par microsoft (http://msdn.microsoft.com/en-us/library/jj663801.aspx)
Cordialement,
Alexandre
Partager