How can I prevent a ToggleButton from being Toggled without setting IsEnabled
Asked Answered
T

5

14

I have a list of ToggleButtons being used as the ItemTemplate in a ListBox similar to this answer using the MultiSelect mode of the Listbox. However I need to make sure at least one item is always selected.

I can get the proper behavior from the ListBox by just adding an item back into the ListBox's SelectedItems collection on the ListBox.SelectionChanged event but my ToggleButton still moves out of its toggled state so I think I need to stop it earlier in the process.

I would like to do it without setting IsEnabled="False" on the last button Selected because I'd prefer to stay with the Enabled visual style without having to redo my button templates. Any ideas?

Tendentious answered 30/3, 2010 at 21:25 Comment(0)
H
36

You can override the OnToggle method to prevent toggling the state, by not calling the base implementation :

public class LockableToggleButton : ToggleButton
{
    protected override void OnToggle()
    {
        if (!LockToggle)
        {
            base.OnToggle();
        }
    }

    public bool LockToggle
    {
        get { return (bool)GetValue(LockToggleProperty); }
        set { SetValue(LockToggleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LockToggle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LockToggleProperty =
        DependencyProperty.Register("LockToggle", typeof(bool), typeof(LockableToggleButton), new UIPropertyMetadata(false));
}
Huerta answered 30/3, 2010 at 21:33 Comment(1)
Works without a glitch :-)Esch
B
9

Have you tried using RadioButtons instead? It normally can't be deselected without selecting another one. It can also be styled to look like a ToggleButton:

<RadioButton Style="{StaticResource {x:Type ToggleButton}}"/>

Or, if you already have a Style for it, just make it BasedOn="{x:Type ToggleButton}". Note that the Visual Studio Editor shows an error in the first case, but it compiles and works fine.

Bouton answered 3/2, 2016 at 15:27 Comment(0)
V
2

This is hackey, but if you don't want custom code you could always use the property "IsHitTestVisible", when you don't want them to uncheck it, simply set IsHitTestVisible equal to false. However, they may be able to tab to the control and toggle it using the space bar.

Vicenta answered 15/6, 2010 at 19:15 Comment(2)
Also it may be desirable to make the audible ding sound when a user tries to toggle it while it is locked. Using IsHitTestVisible wouldn't allow one to achieve this.Voracity
You can use IsTabStop="False" to also keep the user from tabbing to the button. I prefer this solution over the accepted answer since it can be accomplished right in the XAML, is MVVM friendly, and doesn't require any custom code. A style can be used if you need to apply it in multiple places.Gen
C
2

Thomas's answer works fine, but you don't even need the extra dependency property. Your button will update correctly if you have the class inherit from ToggleButton so you can override the OnToggle method, and you change the IsChecked bound property on the ViewModel.

Xaml:

<myControls:OneWayFromSourceToTargetToggle x:Name="MyCustomToggleButton"
                                           Command="{Binding Path=ToggleDoStuffCommand}"
                                           CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"
                                           IsChecked="{Binding Path=ToggleIsCheckedConditionVar, 
                                                               Mode=OneWay}"
                                           />

Added ToggleButton Class:

public class OneWayFromSourceToTargetToggle : ToggleButton
{
   /// <summary>
   /// Overrides the OnToggle method, so it does not set the IsChecked Property automatically
   /// </summary>
   protected override void OnToggle()
   {
      // do nothing
   }
}

Then in the ViewModel just set bool ToggleIsCheckedCondition to true or false. This is a nice way to do it because you are following good MVVM practices.

ViewModel:

public bool ToggleIsCheckedCondition
{
   get { return _toggleIsCheckedCondition; }
   set
   {
      if (_toggleIsCheckedCondition != value)
      {
         _toggleIsCheckedCondition = value;
         NotifyPropertyChanged("ToggleIsCheckedCondition");
      }
   }
}

public ICommand ToggleDoStuffCommand
{
    get {
         return _toggleDoStuffCommand ?? 
               (_toggleDoStuffCommand = new RelayCommand(ExecuteToggleDoStuffCommand));
        }
}

private void ExecuteToggleDoStuffCommand(object param)
{
   var btn = param as ToggleButton;
   if (btn?.IsChecked == null)
   {
      return;
   }
   // has not been updated yet at this point
   ToggleIsCheckedCondition = btn.IsChecked == false;

   // do stuff

   }
}
Callous answered 10/2, 2017 at 18:43 Comment(0)
G
0

Adding a little bit to @Joachim-Mairböck's great answer in case you want to do the same programmatically:

new RadioButton {
    ...
    GroupName = "myButtonGroup"
    Style = Application.Current.TryFindResource(typeof(ToggleButton)) as Style
    ...
} 
Germanize answered 18/6, 2020 at 11:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.