~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to class/Microsoft.SilverlightControls/Controls/Src/ButtonBase/ButtonBase.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
ļ»æ// Copyright Ā© Microsoft Corporation. 
 
2
// This source is subject to the Microsoft Source License for Silverlight Controls (March 2008 Release).
 
3
// Please see http://go.microsoft.com/fwlink/?LinkID=111693 for details.
 
4
// All other rights reserved. 
 
5
 
 
6
using System;
 
7
using System.Diagnostics; 
 
8
using System.Windows.Input; 
 
9
using System.Windows.Media.Animation;
 
10
using System.Windows.Controls;
 
11
 
 
12
namespace System.Windows.Controls.Primitives
 
13
{
 
14
    /// <summary> 
 
15
    /// Represents the base class for all Button controls.
 
16
    /// </summary>
 
17
    public partial class ButtonBase : ContentControl 
 
18
    { 
 
19
        #region ClickMode
 
20
        /// <summary> 
 
21
        /// Gets or sets when the Click event should occur.
 
22
        /// </summary>
 
23
        public ClickMode ClickMode 
 
24
        {
 
25
            get { return (ClickMode) GetValue(ClickModeProperty); }
 
26
            set { SetValue(ClickModeProperty, value); } 
 
27
        } 
 
28
 
 
29
        /// <summary> 
 
30
        /// Identifies the ClickMode dependency property.
 
31
        /// </summary>
 
32
        public static readonly DependencyProperty ClickModeProperty = 
 
33
            DependencyProperty.Register(
 
34
                "ClickMode",
 
35
                typeof(ClickMode), 
 
36
                typeof(ButtonBase), 
 
37
                new PropertyMetadata(OnClickModePropertyChanged));
 
38
 
 
39
        /// <summary>
 
40
        /// ClickModeProperty property changed handler.
 
41
        /// </summary> 
 
42
        /// <param name="d">ButtonBase that changed its ClickMode.</param>
 
43
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
 
44
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Name")] 
 
45
        private static void OnClickModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
 
46
        {
 
47
            ButtonBase source = d as ButtonBase; 
 
48
            Debug.Assert(source != null,
 
49
                "The source is not an instance of ButtonBase!");
 
50
 
 
51
            Debug.Assert(typeof(ClickMode).IsInstanceOfType(e.NewValue),
 
52
                "The value is not an instance of ClickMode!");
 
53
            ClickMode value = (ClickMode) e.NewValue; 
 
54
 
 
55
            if (value != ClickMode.Release && value != ClickMode.Press && value != ClickMode.Hover)
 
56
            { 
 
57
                throw new ArgumentException(Resource.ButtonBase_OnClickModePropertyChanged_InvalidValue, "value");
 
58
            }
 
59
        } 
 
60
        #endregion ClickMode
 
61
 
 
62
        #region IsFocused 
 
63
        /// <summary> 
 
64
        /// Gets a value that determines whether this element has logical focus.
 
65
        /// </summary> 
 
66
        /// <remarks>
 
67
        /// IsFocused will not be set until OnFocus has been called.  It may not
 
68
        /// yet have been set if you check it in your own Focus event handler. 
 
69
        /// </remarks>
 
70
        public bool IsFocused
 
71
        { 
 
72
            get { return (bool) GetValue(IsFocusedProperty); } 
 
73
            internal set { SetValue(IsFocusedProperty, value); }
 
74
        } 
 
75
 
 
76
        /// <summary>
 
77
        /// Identifies the IsFocused dependency property. 
 
78
        /// </summary>
 
79
        public static readonly DependencyProperty IsFocusedProperty =
 
80
            DependencyProperty.Register( 
 
81
                "IsFocused", 
 
82
                typeof(bool),
 
83
                typeof(ButtonBase), 
 
84
                null);
 
85
        #endregion IsFocused
 
86
 
 
87
        #region IsMouseOver
 
88
        /// <summary>
 
89
        /// Gets a value indicating whether the mouse pointer is located over 
 
90
        /// this element. 
 
91
        /// </summary>
 
92
        /// <remarks> 
 
93
        /// IsMouseOver will not be set until MouseEnter has been called.  It
 
94
        /// may not yet have been set if you check it in your own MouseEnter
 
95
        /// event handler. 
 
96
        /// </remarks>
 
97
        public bool IsMouseOver
 
98
        { 
 
99
            get { return (bool) GetValue(IsMouseOverProperty); } 
 
100
            internal set { SetValue(IsMouseOverProperty, value); }
 
101
        } 
 
102
 
 
103
        /// <summary>
 
104
        /// Identifies the IsMouseOver dependency property. 
 
105
        /// </summary>
 
106
        public static readonly DependencyProperty IsMouseOverProperty =
 
107
            DependencyProperty.Register( 
 
108
                "IsMouseOver", 
 
109
                typeof(bool),
 
110
                typeof(ButtonBase), 
 
111
                null);
 
112
        #endregion IsMouseOver
 
113
 
 
114
        #region IsPressed
 
115
        /// <summary>
 
116
        /// Gets a value that indicates whether a ButtonBase is currently 
 
117
        /// activated. 
 
118
        /// </summary>
 
119
        public bool IsPressed 
 
120
        {
 
121
            get { return (bool) GetValue(IsPressedProperty); }
 
122
            protected internal set { SetValue(IsPressedProperty, value); } 
 
123
        }
 
124
 
 
125
        /// <summary> 
 
126
        /// Identifies the IsPressed dependency property. 
 
127
        /// </summary>
 
128
        public static readonly DependencyProperty IsPressedProperty = 
 
129
            DependencyProperty.Register(
 
130
                "IsPressed",
 
131
                typeof(bool), 
 
132
                typeof(ButtonBase),
 
133
                new PropertyMetadata(OnIsPressedPropertyChanged));
 
134
 
 
135
        /// <summary> 
 
136
        /// IsPressedProperty property changed handler.
 
137
        /// </summary> 
 
138
        /// <param name="d">ButtonBase that changed its IsPressed.</param>
 
139
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
 
140
        private static void OnIsPressedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
 
141
        {
 
142
            ButtonBase source = d as ButtonBase;
 
143
            Debug.Assert(source != null, 
 
144
                "The source is not an instance of ButtonBase!"); 
 
145
 
 
146
            source.OnIsPressedChanged(e); 
 
147
        }
 
148
        #endregion IsPressed
 
149
 
 
150
        /// <summary>
 
151
        /// True if the control has been loaded; false otherwise.
 
152
        /// </summary> 
 
153
        internal bool _isLoaded; 
 
154
 
 
155
        /// <summary> 
 
156
        /// True if the mouse has been captured by this button, false otherwise.
 
157
        /// </summary>
 
158
        internal bool _isMouseCaptured; 
 
159
 
 
160
        /// <summary>
 
161
        /// True if the SPACE key is currently pressed, false otherwise. 
 
162
        /// </summary> 
 
163
        internal bool _isSpaceKeyDown;
 
164
 
 
165
        /// <summary>
 
166
        /// True if the mouse's left button is currently down, false otherwise.
 
167
        /// </summary> 
 
168
        internal bool _isMouseLeftButtonDown;
 
169
 
 
170
        /// <summary> 
 
171
        /// Last known position of the mouse with respect to this Button. 
 
172
        /// </summary>
 
173
        internal Point _mousePosition; 
 
174
 
 
175
        /// <summary>
 
176
        /// Current visual state of the button. 
 
177
        /// </summary>
 
178
        internal Storyboard _currentState;
 
179
 
 
180
        /// <summary> 
 
181
        /// True if visual state changes are suspended; false otherwise.
 
182
        /// </summary> 
 
183
        internal bool _suspendStateChanges;
 
184
 
 
185
        /// <summary> 
 
186
        /// Occurs when a Button is clicked.
 
187
        /// </summary>
 
188
        public event RoutedEventHandler Click; 
 
189
 
 
190
        /// <summary>
 
191
        /// Initializes a new instance of the ButtonBase class. 
 
192
        /// </summary>
 
193
        protected ButtonBase()
 
194
        { 
 
195
            // Allow the button to respond to the ENTER key and be focused
 
196
            KeyboardNavigation.SetAcceptsReturn(this, true);
 
197
            IsTabStop = true; 
 
198
 
 
199
            // Attach the necessary events to their virtual counterparts
 
200
            Loaded += delegate { _isLoaded = true; UpdateVisualState(); }; 
 
201
            GotFocus += OnGotFocus;
 
202
            LostFocus += OnLostFocus;
 
203
            KeyDown += OnKeyDown; 
 
204
            KeyUp += OnKeyUp;
 
205
            MouseEnter += OnMouseEnter;
 
206
            MouseLeave += OnMouseLeave; 
 
207
            MouseLeftButtonDown += OnMouseLeftButtonDown; 
 
208
            MouseLeftButtonUp += OnMouseLeftButtonUp;
 
209
            MouseMove += OnMouseMove; 
 
210
        }
 
211
 
 
212
        /// <summary> 
 
213
        /// Update the current visual state of the button.
 
214
        /// </summary>
 
215
        internal void UpdateVisualState() 
 
216
        { 
 
217
            if (!_suspendStateChanges)
 
218
            { 
 
219
                ChangeVisualState();
 
220
            }
 
221
        } 
 
222
 
 
223
        /// <summary>
 
224
        /// Change to the correct visual state for the button. 
 
225
        /// </summary> 
 
226
        internal virtual void ChangeVisualState()
 
227
        { 
 
228
        }
 
229
 
 
230
        /// <summary> 
 
231
        /// Change the visual state of the button.
 
232
        /// </summary>
 
233
        /// <param name="state">Next visual state of the button.</param> 
 
234
        /// <remarks> 
 
235
        /// This method should not be called by controls to force a change to
 
236
        /// the current visual state.  UpdateVisualState is preferred because 
 
237
        /// it properly handles suspension of state changes.
 
238
        /// </remarks>
 
239
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "An invalid Storyboard or playing a Storyboard at the wrong time throws System.Exception.")] 
 
240
        internal void ChangeVisualState(Storyboard state)
 
241
        {
 
242
            // Do nothing if we're already in the right state 
 
243
            Storyboard previousState = _currentState; 
 
244
            if (state == previousState)
 
245
            { 
 
246
                return;
 
247
            }
 
248
 
 
249
            // Try to prevent transitioning to a new state before the control
 
250
            // has been loaded by waiting for the Loaded event and checking if
 
251
            // it has a Parent because it was added to the visual tree (since 
 
252
            // playing a Storyboard before a control is loaded will raise an 
 
253
            // Exception as per Jolt Bug 13025).
 
254
            if (state != null && _isLoaded && Parent != null) 
 
255
            {
 
256
                try
 
257
                { 
 
258
                    // Transition into the state
 
259
                    state.Begin();
 
260
 
 
261
                    // Don't make the new state the current state until after we 
 
262
                    // we know it didn't fail to play the transition (so the
 
263
                    // next call to UpdateVisualState will have a chance to try 
 
264
                    // again)
 
265
                    _currentState = state;
 
266
 
 
267
                    // Back out of the previous state after moving to the next
 
268
                    // state to restore any values not used by the new state.
 
269
                    if (previousState != null) 
 
270
                    { 
 
271
                        previousState.Stop();
 
272
                    } 
 
273
                }
 
274
                catch (Exception)
 
275
                { 
 
276
                }
 
277
            }
 
278
        } 
 
279
 
 
280
        /// <summary>
 
281
        /// Capture the mouse. 
 
282
        /// </summary>
 
283
        internal void CaptureMouseInternal()
 
284
        { 
 
285
            if (!_isMouseCaptured)
 
286
            {
 
287
                _isMouseCaptured = CaptureMouse(); 
 
288
            } 
 
289
        }
 
290
 
 
291
        /// <summary>
 
292
        /// Release mouse capture if we already had it.
 
293
        /// </summary> 
 
294
        internal void ReleaseMouseCaptureInternal()
 
295
        {
 
296
            ReleaseMouseCapture(); 
 
297
           _isMouseCaptured = false; 
 
298
        }
 
299
 
 
300
        /// <summary>
 
301
        /// Invoke OnClick for testing.
 
302
        /// </summary> 
 
303
        internal void OnClickInternal()
 
304
        {
 
305
            OnClick(); 
 
306
        } 
 
307
 
 
308
        /// <summary> 
 
309
        /// Raises the Click routed event.
 
310
        /// </summary>
 
311
        protected virtual void OnClick() 
 
312
        {
 
313
            RoutedEventHandler handler = Click;
 
314
            if (handler != null) 
 
315
            { 
 
316
                handler(this, new RoutedEventArgs { OriginalSource = this });
 
317
            } 
 
318
        }
 
319
 
 
320
        /// <summary> 
 
321
        /// Called when the IsEnabled property changes.
 
322
        /// </summary>
 
323
        /// <param name="isEnabled">New value of the IsEnabled property.</param> 
 
324
        protected override void OnIsEnabledChanged(bool isEnabled) 
 
325
        {
 
326
            base.OnIsEnabledChanged(isEnabled); 
 
327
            _suspendStateChanges = true;
 
328
            try
 
329
            { 
 
330
                if (!isEnabled)
 
331
                {
 
332
                    IsFocused = false; 
 
333
                    IsPressed = false; 
 
334
                    _isMouseCaptured = false;
 
335
                    _isSpaceKeyDown = false; 
 
336
                    _isMouseLeftButtonDown = false;
 
337
                }
 
338
            } 
 
339
            finally
 
340
            {
 
341
                _suspendStateChanges = false; 
 
342
                UpdateVisualState(); 
 
343
            }
 
344
        } 
 
345
 
 
346
        /// <summary>
 
347
        /// Called when the IsPressed property changes. 
 
348
        /// </summary>
 
349
        /// <param name="e">
 
350
        /// The data for DependencyPropertyChangedEventArgs. 
 
351
        /// </param> 
 
352
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "e", Justification = "Compat with WPF.")]
 
353
        protected virtual void OnIsPressedChanged(DependencyPropertyChangedEventArgs e) 
 
354
        {
 
355
            UpdateVisualState();
 
356
        } 
 
357
 
 
358
        /// <summary>
 
359
        /// Handle the GotFocus event. 
 
360
        /// </summary> 
 
361
        /// <param name="sender">The source of the event.</param>
 
362
        /// <param name="e">RoutedEventArgs.</param> 
 
363
        internal void OnGotFocus(object sender, RoutedEventArgs e)
 
364
        {
 
365
            IsFocused = true; 
 
366
            OnGotFocus(e);
 
367
        }
 
368
 
 
369
        /// <summary> 
 
370
        /// Responds to the GotFocus event.
 
371
        /// </summary> 
 
372
        /// <param name="e">The event data for the GotFocus event.</param>
 
373
        protected virtual void OnGotFocus(RoutedEventArgs e)
 
374
        { 
 
375
            if (e == null)
 
376
            {
 
377
                throw new ArgumentNullException("e"); 
 
378
            } 
 
379
            UpdateVisualState();
 
380
        } 
 
381
 
 
382
        /// <summary>
 
383
        /// Handle the LostFocus event. 
 
384
        /// </summary>
 
385
        /// <param name="sender">The source of the event.</param>
 
386
        /// <param name="e">RoutedEventArgs.</param> 
 
387
        internal void OnLostFocus(object sender, RoutedEventArgs e) 
 
388
        {
 
389
            IsFocused = false; 
 
390
            OnLostFocus(e);
 
391
        }
 
392
 
 
393
        /// <summary>
 
394
        /// Responds to the LostFocus event.
 
395
        /// </summary> 
 
396
        /// <param name="e">The event data for the LostFocus event.</param> 
 
397
        protected virtual void OnLostFocus(RoutedEventArgs e)
 
398
        { 
 
399
            if (e == null)
 
400
            {
 
401
                throw new ArgumentNullException("e"); 
 
402
            }
 
403
 
 
404
            _suspendStateChanges = true; 
 
405
            try 
 
406
            {
 
407
                if (ClickMode != ClickMode.Hover) 
 
408
                {
 
409
                    IsPressed = false;
 
410
                    ReleaseMouseCaptureInternal(); 
 
411
                    _isSpaceKeyDown = false;
 
412
                }
 
413
            } 
 
414
            finally 
 
415
            {
 
416
                _suspendStateChanges = false; 
 
417
                UpdateVisualState();
 
418
            }
 
419
        } 
 
420
 
 
421
        /// <summary>
 
422
        /// Handle the KeyDown event. 
 
423
        /// </summary> 
 
424
        /// <param name="sender">The source of the event.</param>
 
425
        /// <param name="e">KeyEventArgs.</param> 
 
426
        internal void OnKeyDown(object sender, KeyEventArgs e)
 
427
        {
 
428
            OnKeyDown(e); 
 
429
        }
 
430
 
 
431
        /// <summary> 
 
432
        /// Responds to the KeyDown event. 
 
433
        /// </summary>
 
434
        /// <param name="e">The event data for the KeyDown event.</param> 
 
435
        protected virtual void OnKeyDown(KeyEventArgs e)
 
436
        {
 
437
            if (e == null) 
 
438
            {
 
439
                throw new ArgumentNullException("e");
 
440
            } 
 
441
            else if (e.Handled) 
 
442
            {
 
443
                return; 
 
444
            }
 
445
 
 
446
            if (OnKeyDownInternal(e.Key)) 
 
447
            {
 
448
                e.Handled = true;
 
449
            } 
 
450
        } 
 
451
 
 
452
        /// <summary> 
 
453
        /// Handles the KeyDown event for ButtonBase.
 
454
        /// </summary>
 
455
        /// <param name="key"> 
 
456
        /// The keyboard key associated with the event.
 
457
        /// </param>
 
458
        /// <returns>True if the event was handled, false otherwise.</returns> 
 
459
        /// <remarks> 
 
460
        /// This method exists for the purpose of unit testing since we can't
 
461
        /// set KeyEventArgs.Key to simulate key press events. 
 
462
        /// </remarks>
 
463
        internal virtual bool OnKeyDownInternal(Key key)
 
464
        { 
 
465
            // True if the button will handle the event, false otherwise.
 
466
            bool handled = false;
 
467
 
 
468
            // Key presses can be ignored when disabled or in ClickMode.Hover 
 
469
            if (IsEnabled && (ClickMode != ClickMode.Hover))
 
470
            { 
 
471
                // Hitting the SPACE key is equivalent to pressing the mouse
 
472
                // button
 
473
                if (key == Key.Space) 
 
474
                {
 
475
                    // Ignore the SPACE key if we already have the mouse
 
476
                    // captured 
 
477
                    if (!_isMouseCaptured) 
 
478
                    {
 
479
                        _isSpaceKeyDown = true; 
 
480
                        IsPressed = true;
 
481
                        CaptureMouseInternal();
 
482
 
 
483
                        if (ClickMode == ClickMode.Press)
 
484
                        {
 
485
                            OnClick(); 
 
486
                        } 
 
487
 
 
488
                        handled = true; 
 
489
                    }
 
490
                }
 
491
                // The ENTER key forces a click 
 
492
                else if ((key == Key.Enter) && KeyboardNavigation.GetAcceptsReturn(this))
 
493
                {
 
494
                    _isSpaceKeyDown = false; 
 
495
                    IsPressed = false; 
 
496
                    ReleaseMouseCaptureInternal();
 
497
 
 
498
                    OnClick();
 
499
 
 
500
                    handled = true; 
 
501
                }
 
502
                // Any other keys pressed are irrelevant
 
503
                else if (_isSpaceKeyDown) 
 
504
                { 
 
505
                    IsPressed = false;
 
506
                    _isSpaceKeyDown = false; 
 
507
                    ReleaseMouseCaptureInternal();
 
508
                }
 
509
            } 
 
510
 
 
511
            return handled;
 
512
        } 
 
513
 
 
514
        /// <summary>
 
515
        /// Handle the KeyUp event. 
 
516
        /// </summary>
 
517
        /// <param name="sender">The source of the event.</param>
 
518
        /// <param name="e">KeyEventArgs.</param> 
 
519
        internal void OnKeyUp(object sender, KeyEventArgs e)
 
520
        {
 
521
            OnKeyUp(e); 
 
522
        } 
 
523
 
 
524
        /// <summary> 
 
525
        /// Responds to the KeyUp event.
 
526
        /// </summary>
 
527
        /// <param name="e">The event data for the KeyUp event.</param> 
 
528
        protected virtual void OnKeyUp(KeyEventArgs e)
 
529
        {
 
530
            if (e == null) 
 
531
            { 
 
532
                throw new ArgumentNullException("e");
 
533
            } 
 
534
            else if (e.Handled)
 
535
            {
 
536
                return; 
 
537
            }
 
538
 
 
539
            if (OnKeyUpInternal(e.Key)) 
 
540
            { 
 
541
                e.Handled = true;
 
542
            } 
 
543
        }
 
544
 
 
545
        /// <summary> 
 
546
        /// Handles the KeyUp event for ButtonBase.
 
547
        /// </summary>
 
548
        /// <param name="key">The keyboard key associated with the event.</param> 
 
549
        /// <returns>True if the event was handled, false otherwise.</returns> 
 
550
        /// <remarks>
 
551
        /// This method exists for the purpose of unit testing since we can't 
 
552
        /// set KeyEventArgs.Key to simulate key press events.
 
553
        /// </remarks>
 
554
        internal bool OnKeyUpInternal(Key key) 
 
555
        {
 
556
            // True if the button will handle the event, false otherwise.
 
557
            bool handled = false; 
 
558
 
 
559
            // Key presses can be ignored when disabled or in ClickMode.Hover
 
560
            // or if any other key than SPACE was released. 
 
561
            if (IsEnabled && (ClickMode != ClickMode.Hover) && (key == Key.Space))
 
562
            {
 
563
                _isSpaceKeyDown = false; 
 
564
 
 
565
                if (!_isMouseLeftButtonDown)
 
566
                { 
 
567
                    // If the mouse isn't in use, raise the Click event if we're 
 
568
                    // in the correct click mode
 
569
                    ReleaseMouseCaptureInternal(); 
 
570
                    if (IsPressed && (ClickMode == ClickMode.Release))
 
571
                    {
 
572
                        OnClick(); 
 
573
                    }
 
574
 
 
575
                    IsPressed = false; 
 
576
                } 
 
577
                else if (_isMouseCaptured)
 
578
                { 
 
579
                    // Determine if the button should still be pressed based on
 
580
                    // the position of the mouse.
 
581
                    bool isValid = IsValidMousePosition(); 
 
582
                    IsPressed = isValid;
 
583
                    if (!isValid)
 
584
                    { 
 
585
                        ReleaseMouseCaptureInternal(); 
 
586
                    }
 
587
                } 
 
588
 
 
589
                handled = true;
 
590
            } 
 
591
 
 
592
            return handled;
 
593
        } 
 
594
 
 
595
        /// <summary>
 
596
        /// Handle the MouseEnter event. 
 
597
        /// </summary>
 
598
        /// <param name="sender">The source of the event.</param>
 
599
        /// <param name="e">MouseEventArgs.</param> 
 
600
        internal void OnMouseEnter(object sender, MouseEventArgs e)
 
601
        {
 
602
            IsMouseOver = true; 
 
603
            OnMouseEnter(e); 
 
604
        }
 
605
 
 
606
        /// <summary>
 
607
        /// Responds to the MouseEnter event.
 
608
        /// </summary> 
 
609
        /// <param name="e">The event data for the MouseEnter event.</param>
 
610
        protected virtual void OnMouseEnter(MouseEventArgs e)
 
611
        { 
 
612
            if (e == null) 
 
613
            {
 
614
                throw new ArgumentNullException("e"); 
 
615
            }
 
616
            else if (e.Handled)
 
617
            { 
 
618
                return;
 
619
            }
 
620
 
 
621
            _suspendStateChanges = true; 
 
622
            try
 
623
            { 
 
624
                if ((ClickMode == ClickMode.Hover) && IsEnabled)
 
625
                {
 
626
                    IsPressed = true; 
 
627
                    OnClick();
 
628
                    e.Handled = true;
 
629
                } 
 
630
 
 
631
            }
 
632
            finally 
 
633
            {
 
634
                _suspendStateChanges = false;
 
635
                UpdateVisualState(); 
 
636
            }
 
637
        }
 
638
 
 
639
        /// <summary> 
 
640
        /// Handle the MouseLeave event.
 
641
        /// </summary> 
 
642
        /// <param name="sender">The source of the event.</param>
 
643
        /// <param name="e">MouseEventArgs.</param>
 
644
        internal void OnMouseLeave(object sender, MouseEventArgs e) 
 
645
        {
 
646
            IsMouseOver = false;
 
647
            OnMouseLeave(e); 
 
648
        } 
 
649
 
 
650
        /// <summary> 
 
651
        /// Responds to the MouseLeave event.
 
652
        /// </summary>
 
653
        /// <param name="e">The event data for the MouseLeave event.</param> 
 
654
        protected virtual void OnMouseLeave(MouseEventArgs e)
 
655
        {
 
656
            if (e == null) 
 
657
            { 
 
658
                throw new ArgumentNullException("e");
 
659
            } 
 
660
            else if (e.Handled)
 
661
            {
 
662
                return; 
 
663
            }
 
664
 
 
665
            _suspendStateChanges = true; 
 
666
            try 
 
667
            {
 
668
                if ((ClickMode == ClickMode.Hover) && IsEnabled) 
 
669
                {
 
670
                    IsPressed = false;
 
671
                    e.Handled = true; 
 
672
                }
 
673
            }
 
674
            finally 
 
675
            { 
 
676
                _suspendStateChanges = false;
 
677
                UpdateVisualState(); 
 
678
            }
 
679
        }
 
680
 
 
681
        /// <summary>
 
682
        /// Handle the MouseLeftButtonDown event.
 
683
        /// </summary> 
 
684
        /// <param name="sender">The source of the event.</param> 
 
685
        /// <param name="e">MouseButtonEventArgs.</param>
 
686
        internal void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
 
687
        {
 
688
            _isMouseLeftButtonDown = true;
 
689
            OnMouseLeftButtonDown(e); 
 
690
        }
 
691
 
 
692
        /// <summary> 
 
693
        /// Responds to the MouseLeftButtonDown event. 
 
694
        /// </summary>
 
695
        /// <param name="e"> 
 
696
        /// The event data for the MouseLeftButtonDown event.
 
697
        /// </param>
 
698
        protected virtual void OnMouseLeftButtonDown(MouseButtonEventArgs e) 
 
699
        {
 
700
            if (e == null)
 
701
            { 
 
702
                throw new ArgumentNullException("e"); 
 
703
            }
 
704
            else if (e.Handled) 
 
705
            {
 
706
                return;
 
707
            } 
 
708
 
 
709
            if (!IsEnabled || (ClickMode == ClickMode.Hover))
 
710
            { 
 
711
                return; 
 
712
            }
 
713
 
 
714
            e.Handled = true;
 
715
            _suspendStateChanges = true;
 
716
            try 
 
717
            {
 
718
                Focus();
 
719
 
 
720
                CaptureMouseInternal(); 
 
721
                if (_isMouseCaptured)
 
722
                { 
 
723
                    IsPressed = true;
 
724
                }
 
725
            } 
 
726
            finally
 
727
            {
 
728
                _suspendStateChanges = false; 
 
729
                UpdateVisualState(); 
 
730
            }
 
731
 
 
732
            if (ClickMode == ClickMode.Press)
 
733
            {
 
734
                OnClick(); 
 
735
            }
 
736
        }
 
737
 
 
738
        /// <summary> 
 
739
        /// Handle the MouseLeftButtonUp event.
 
740
        /// </summary> 
 
741
        /// <param name="sender">The source of the event.</param>
 
742
        /// <param name="e">MouseButtonEventArgs.</param>
 
743
        internal void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
 
744
        {
 
745
            _isMouseLeftButtonDown = false;
 
746
            OnMouseLeftButtonUp(e); 
 
747
        } 
 
748
 
 
749
        /// <summary> 
 
750
        /// Responds to the MouseLeftButtonUp event.
 
751
        /// </summary>
 
752
        /// <param name="e"> 
 
753
        /// The event data for the MouseLeftButtonUp event.
 
754
        /// </param>
 
755
        protected virtual void OnMouseLeftButtonUp(MouseButtonEventArgs e) 
 
756
        { 
 
757
            if (e == null)
 
758
            { 
 
759
                throw new ArgumentNullException("e");
 
760
            }
 
761
            else if (e.Handled) 
 
762
            {
 
763
                return;
 
764
            } 
 
765
 
 
766
            if (!IsEnabled || (ClickMode == ClickMode.Hover))
 
767
            { 
 
768
                return;
 
769
            }
 
770
 
 
771
            e.Handled = true;
 
772
            if (!_isSpaceKeyDown && IsPressed && (ClickMode == ClickMode.Release))
 
773
            { 
 
774
                OnClick(); 
 
775
            }
 
776
 
 
777
            if (!_isSpaceKeyDown)
 
778
            {
 
779
                ReleaseMouseCaptureInternal(); 
 
780
                IsPressed = false;
 
781
            }
 
782
        } 
 
783
 
 
784
        /// <summary>
 
785
        /// Handle the MouseMove event. 
 
786
        /// </summary>
 
787
        /// <param name="sender">The source of the event.</param>
 
788
        /// <param name="e">MouseEventArgs.</param> 
 
789
        internal void OnMouseMove(object sender, MouseEventArgs e)
 
790
        {
 
791
            OnMouseMove(e); 
 
792
        } 
 
793
 
 
794
        /// <summary> 
 
795
        /// Responds to the MouseMove event.
 
796
        /// </summary>
 
797
        /// <param name="e">The event data for the MouseMove event.</param> 
 
798
        protected virtual void OnMouseMove(MouseEventArgs e)
 
799
        {
 
800
            if (e == null) 
 
801
            { 
 
802
                throw new ArgumentNullException("e");
 
803
            } 
 
804
 
 
805
            // Cache the latest mouse position.
 
806
            _mousePosition = e.GetPosition(this); 
 
807
 
 
808
            if (e.Handled)
 
809
            { 
 
810
                return; 
 
811
            }
 
812
 
 
813
            // Determine if the button is still pressed based on the mouse's
 
814
            // current position.
 
815
            if (_isMouseLeftButtonDown && 
 
816
                IsEnabled &&
 
817
                (ClickMode != ClickMode.Hover) &&
 
818
                _isMouseCaptured && 
 
819
                !_isSpaceKeyDown) 
 
820
            {
 
821
                IsPressed = IsValidMousePosition(); 
 
822
                e.Handled = true;
 
823
            }
 
824
        } 
 
825
 
 
826
        /// <summary>
 
827
        /// Determine if the mouse is above the button based on its last known 
 
828
        /// position. 
 
829
        /// </summary>
 
830
        /// <returns> 
 
831
        /// True if the mouse is considered above the button, false otherwise.
 
832
        /// </returns>
 
833
        internal bool IsValidMousePosition() 
 
834
        {
 
835
            return (_mousePosition.X >= 0.0) &&
 
836
                (_mousePosition.X <= ActualWidth) && 
 
837
                (_mousePosition.Y >= 0.0) && 
 
838
                (_mousePosition.Y <= ActualHeight);
 
839
        } 
 
840
    }
 
841
}