using System; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Input; namespace ZeroLevel.WPF { public static class VirtualToggleButton { #region attached properties #region IsChecked /// /// IsChecked Attached Dependency Property /// public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(Nullable), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((Nullable)false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnIsCheckedChanged))); /// /// Gets the IsChecked property. This dependency property /// indicates whether the toggle button is checked. /// public static Nullable GetIsChecked(DependencyObject d) { return (Nullable)d.GetValue(IsCheckedProperty); } /// /// Sets the IsChecked property. This dependency property /// indicates whether the toggle button is checked. /// public static void SetIsChecked(DependencyObject d, Nullable value) { d.SetValue(IsCheckedProperty, value); } /// /// Handles changes to the IsChecked property. /// private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement pseudobutton = d as UIElement; if (pseudobutton != null) { Nullable newValue = (Nullable)e.NewValue; if (newValue == true) { RaiseCheckedEvent(pseudobutton); } else if (newValue == false) { RaiseUncheckedEvent(pseudobutton); } else { RaiseIndeterminateEvent(pseudobutton); } } } #endregion #region IsThreeState /// /// IsThreeState Attached Dependency Property /// public static readonly DependencyProperty IsThreeStateProperty = DependencyProperty.RegisterAttached("IsThreeState", typeof(bool), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((bool)false)); /// /// Gets the IsThreeState property. This dependency property /// indicates whether the control supports two or three states. /// IsChecked can be set to null as a third state when IsThreeState is true. /// public static bool GetIsThreeState(DependencyObject d) { return (bool)d.GetValue(IsThreeStateProperty); } /// /// Sets the IsThreeState property. This dependency property /// indicates whether the control supports two or three states. /// IsChecked can be set to null as a third state when IsThreeState is true. /// public static void SetIsThreeState(DependencyObject d, bool value) { d.SetValue(IsThreeStateProperty, value); } #endregion #region IsVirtualToggleButton /// /// IsVirtualToggleButton Attached Dependency Property /// public static readonly DependencyProperty IsVirtualToggleButtonProperty = DependencyProperty.RegisterAttached("IsVirtualToggleButton", typeof(bool), typeof(VirtualToggleButton), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnIsVirtualToggleButtonChanged))); /// /// Gets the IsVirtualToggleButton property. This dependency property /// indicates whether the object to which the property is attached is treated as a VirtualToggleButton. /// If true, the object will respond to keyboard and mouse input the same way a ToggleButton would. /// public static bool GetIsVirtualToggleButton(DependencyObject d) { return (bool)d.GetValue(IsVirtualToggleButtonProperty); } /// /// Sets the IsVirtualToggleButton property. This dependency property /// indicates whether the object to which the property is attached is treated as a VirtualToggleButton. /// If true, the object will respond to keyboard and mouse input the same way a ToggleButton would. /// public static void SetIsVirtualToggleButton(DependencyObject d, bool value) { d.SetValue(IsVirtualToggleButtonProperty, value); } /// /// Handles changes to the IsVirtualToggleButton property. /// private static void OnIsVirtualToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { IInputElement element = d as IInputElement; if (element != null) { if ((bool)e.NewValue) { element.MouseLeftButtonDown += OnMouseLeftButtonDown; element.KeyDown += OnKeyDown; } else { element.MouseLeftButtonDown -= OnMouseLeftButtonDown; element.KeyDown -= OnKeyDown; } } } #endregion #endregion #region routed events #region Checked /// /// A static helper method to raise the Checked event on a target element. /// /// UIElement or ContentElement on which to raise the event internal static RoutedEventArgs RaiseCheckedEvent(UIElement target) { if (target == null) return null; RoutedEventArgs args = new RoutedEventArgs(); args.RoutedEvent = ToggleButton.CheckedEvent; RaiseEvent(target, args); return args; } #endregion #region Unchecked /// /// A static helper method to raise the Unchecked event on a target element. /// /// UIElement or ContentElement on which to raise the event internal static RoutedEventArgs RaiseUncheckedEvent(UIElement target) { if (target == null) return null; RoutedEventArgs args = new RoutedEventArgs(); args.RoutedEvent = ToggleButton.UncheckedEvent; RaiseEvent(target, args); return args; } #endregion #region Indeterminate /// /// A static helper method to raise the Indeterminate event on a target element. /// /// UIElement or ContentElement on which to raise the event internal static RoutedEventArgs RaiseIndeterminateEvent(UIElement target) { if (target == null) return null; RoutedEventArgs args = new RoutedEventArgs(); args.RoutedEvent = ToggleButton.IndeterminateEvent; RaiseEvent(target, args); return args; } #endregion #endregion #region private methods private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { e.Handled = true; UpdateIsChecked(sender as DependencyObject); } private static void OnKeyDown(object sender, KeyEventArgs e) { if (e.OriginalSource == sender) { if (e.Key == Key.Space) { // ignore alt+space which invokes the system menu if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) return; UpdateIsChecked(sender as DependencyObject); e.Handled = true; } else if (e.Key == Key.Enter && (bool)(sender as DependencyObject).GetValue(KeyboardNavigation.AcceptsReturnProperty)) { UpdateIsChecked(sender as DependencyObject); e.Handled = true; } } } private static void UpdateIsChecked(DependencyObject d) { Nullable isChecked = GetIsChecked(d); if (isChecked == true) { SetIsChecked(d, GetIsThreeState(d) ? (Nullable)null : (Nullable)false); } else { SetIsChecked(d, isChecked.HasValue); } } private static void RaiseEvent(DependencyObject target, RoutedEventArgs args) { if (target is UIElement) { (target as UIElement).RaiseEvent(args); } else if (target is ContentElement) { (target as ContentElement).RaiseEvent(args); } } #endregion } }