简体   繁体   中英

How to Uncheck radio button in WPF (MVVM)

I have a radio buttons group. The choice is not mandatory to fill the form. At the beginning all the radio buttons are unchecked. If the user unintentionally clicks on one of them, he can not go back, because at least one has to be checked.

So How can I uncheck a radio button and not force the user to make an unwanted choice?

ps the form is built at run-time and I am following the MVVM design pattern. For Mandatory choices, the radio buttons solution fit very well and I already use it in this case.

Try this:

public class OptionalRadioButton : RadioButton
{
    #region bool IsOptional dependency property
    public static readonly DependencyProperty IsOptionalProperty = 
        DependencyProperty.Register(
            "IsOptional", 
            typeof(bool), 
            typeof(OptionalRadioButton), 
            new PropertyMetadata((bool)true,
                (obj, args) =>
                {
                    ((OptionalRadioButton)obj).OnIsOptionalChanged(args);
                }));
    public bool IsOptional
    {
        get
        {
            return (bool)GetValue(IsOptionalProperty);
        }
        set
        {
            SetValue(IsOptionalProperty, value);
        }
    }
    private void OnIsOptionalChanged(DependencyPropertyChangedEventArgs args)
    {
        // TODO: Add event handler if needed
    }
    #endregion

    protected override void OnClick()
    {
        bool? wasChecked = this.IsChecked;
        base.OnClick();
        if ( this.IsOptional && wasChecked == true )
            this.IsChecked = false;
    }
}

Personally when I want this behavior I use a ListBox with the Template overwritten to use RadioButtons .

It's the best control suited to do all of the following :

  • display a list of items
  • only one item can be selected at a time, so only one property maintained in data model
  • user can leave selected item as null, indicating no item is selected

My custom style for the ListBox removes the borders and background color, and draws each item using a RadioButton with the IsChecked bound to the ListBoxItem.IsSelected . Typically something like this :

<Style x:Key="RadioButtonListBoxStyle" TargetType="{x:Type ListBox}">
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}" >
                <Setter Property="Margin" Value="2, 2, 2, 0" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border Background="Transparent">
                                <RadioButton
                                    Content="{TemplateBinding ContentPresenter.Content}" VerticalAlignment="Center"
                                    IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

And to display the RadioButtons themselves is usually something very simple, like this :

<ListBox ItemsSource="{Binding AvailableValues}"
         SelectedValue="{Binding SelectedValue}"
         Style="{StaticResource RadioButtonListBoxStyle}" />

I used for this Scenario some Eventhandlers

<RadioButton Checked="RB_Checked" Click="RB_Clicked"/>

In the Codebehind of the XAML:

Private JustChecked as Boolean

Private Sub RB_Checked(sender As Object, e As RoutedEventArgs)
    Dim s As RadioButton = sender
    ' Action on Check...
    JustChecked = True
End Sub

Private Sub RB_Clicked(sender As Object, e As RoutedEventArgs)
    If JustChecked Then
        JustChecked = False
        e.Handled = True
        Return
    End If
    Dim s As RadioButton = sender
    If s.IsChecked Then s.IsChecked = False        
End Sub

Or in C#

private bool JustChecked;
private void RB_Checked(object sender, RoutedEventArgs e)
{
    RadioButton s = sender;
    // Action on Check...
    JustChecked = true;
}

private void RB_Clicked(object sender, RoutedEventArgs e)
{
    if (JustChecked) {
        JustChecked = false;
        e.Handled = true;
        return;
    }
    RadioButton s = sender;
    if (s.IsChecked)
        s.IsChecked = false;
}

Click Event fires after Checked Event

Your radio button:

<RadioButton x:Name="MyRadioButton">Some Radio Button</RadioButton>

Code Behind:

 MyRadioButton.IsChecked = false;

If you want to uncheck it using bindings then do:

Code behind:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = this;

    }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    #endregion


    private bool _isRadioChecked;

    public bool IsRadioChecked
    {
        get
        {
            return _isRadioChecked;
        }
        set
        {
            _isRadioChecked = value;
            OnPropertyChanged("IsRadioChecked");
        }
    }
}

xaml (view):

<RadioButton IsChecked="{Binding IsRadioChecked}">Some Radio Button</RadioButton>

The dirty way is to create collapsed RadioButton

<StackPanel>
    <RadioButton Content="Option 1" GroupName="optionSet1" PreviewMouseDown="RadioButton_OnPreviewMouseDown"/>
    <RadioButton Content="Option 2" GroupName="optionSet1" PreviewMouseDown="RadioButton_OnPreviewMouseDown"/>
    <RadioButton GroupName="optionSet1" x:Name="rbEmpty" Visibility="Collapsed" />
</StackPanel>

And check the empty RadioButton if user click on already checked RadioButton

Code behind

private void RadioButton_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        var radioButton = sender as RadioButton;
        if (radioButton.IsChecked.GetValueOrDefault())
        {
            rbEmpty.IsChecked = true;
            e.Handled = true;
        }
    }

In this case you shouldn't use a RadioButton . The purpose of this control is that always one is checked. In your scenario I would prefer to use a CheckBox

You could, as @Tomtom pointed out, use Checkboxes, but that would also give the opportunity to check more than one option at the same time, or you'd have to implement a complex mechanism to make sure only one option is checked at a time.

Another very cheap workaround would be to add an extra entry in your radio button choices.

How did you hear about us ?

  • Google
  • A friend
  • an ad on TV
  • Other

I agree with Tomtom that RadioButton is perhaps not the best design choice, but if that selection is out of your control or it is necessary for other reasons, you will need some mechanism by which to force the deselect.

Here are some possibilities:

  • A Clear button near the RadioButton group
  • Deselect a RadioButton that is already checked if it is clicked again.
  • Provide a context menu Clear option when a checked RadioButton is right clicked

Since the form is built at run-time, you will need to attach the appropriate event handler when you are creating the control, like so:

   // Create an instance of the control
   var controlType = typeof(Control).Assembly.GetType(fullyQualifiedControl, true);
   var control = Activator.CreateInstance(controlType);

    switch (fullyQualifiedControl)
    {
       case "System.Windows.Controls.RadioButton":
          ((RadioButton)control).Checked += new RoutedEventHandler(DataDrivenControl_Checked);
          ((RadioButton)control).Unchecked += new RoutedEventHandler(DataDrivenControl_UnChecked);
          break;
    }

I know that this is a bit late in the day for an answer but I had a similar problem using UWP and I thought that this might help. I was developing a custom menu where the user can pick and unpick an option and my solution is shown in the following test code:

Main Page Xaml:

<Grid>
    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
        <RadioButton x:Name="RB1" GroupName="RBTest" Content="Item No. 1" Click="RBtn_Click"/>
        <RadioButton x:Name="RB2" GroupName="RBTest" Content="Item No. 2" Click="RBtn_Click"/>
        <RadioButton x:Name="RB3" GroupName="RBTest" Content="Item No. 3" Click="RBtn_Click"/>
        <RadioButton x:Name="RB4" GroupName="RBTest" Content="Item No. 4" Click="RBtn_Click"/>
        <RadioButton x:Name="RB5" GroupName="RBTest" Content="Item No. 5" Click="RBtn_Click"/>
    </StackPanel>
</Grid>

Main Page Code Behind:

public sealed partial class MainPage : Page
{
    private bool rb1PrevState;
    private bool rb2PrevState;
    private bool rb3PrevState;
    private bool rb4PrevState;
    private bool rb5PrevState;

    public MainPage()
    {
        this.InitializeComponent();
        rb1PrevState = this.RB1.IsChecked.Value;
        rb2PrevState = this.RB2.IsChecked.Value;
        rb3PrevState = this.RB3.IsChecked.Value;
        rb4PrevState = this.RB4.IsChecked.Value;
        rb5PrevState = this.RB5.IsChecked.Value;

    }

    private void RBtn_Click(object sender, RoutedEventArgs e)
    {
        RadioButton rbtn = sender as RadioButton;

        if (rbtn != null)
        {
            if (rbtn.IsChecked.Value == true)
            {
                switch (rbtn.Name)
                {
                    case "RB1":
                        if (rb1PrevState == true)
                        {
                            rbtn.IsChecked = false;
                            rb1PrevState = false;
                        }
                        else
                        {
                            rb1PrevState = true;
                            ResetRBPrevStates("RB1");
                        }
                        break;
                    case "RB2":
                        if (rb2PrevState == true)
                        {
                            rbtn.IsChecked = false;
                            rb2PrevState = false;
                        }
                        else
                        {
                            rb2PrevState = true;
                            ResetRBPrevStates("RB2");
                        }
                        break;
                    case "RB3":
                        if (rb3PrevState == true)
                        {
                            rbtn.IsChecked = false;
                            rb3PrevState = false;
                        }
                        else
                        {
                            rb3PrevState = true;
                            ResetRBPrevStates("RB3");
                        }
                        break;
                    case "RB4":
                        if (rb4PrevState == true)
                        {
                            rbtn.IsChecked = false;
                            rb4PrevState = false;
                        }
                        else
                        {
                            rb4PrevState = true;
                            ResetRBPrevStates("RB4");
                        }
                        break;
                    case "RB5":
                        if (rb5PrevState == true)
                        {
                            rbtn.IsChecked = false;
                            rb5PrevState = false;
                        }
                        else
                        {
                            rb5PrevState = true;
                            ResetRBPrevStates("RB5");
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    private void ResetRBPrevStates(string _excludeRB)
    {
        rb1PrevState = (_excludeRB == "RB1" ? rb1PrevState : false);
        rb2PrevState = (_excludeRB == "RB2" ? rb2PrevState : false);
        rb3PrevState = (_excludeRB == "RB3" ? rb3PrevState : false);
        rb4PrevState = (_excludeRB == "RB4" ? rb4PrevState : false);
        rb5PrevState = (_excludeRB == "RB5" ? rb5PrevState : false);
    }
}

This worked well for me and, a far as I'm concerned, doesn't break any MVVM rules.

hi this is how to check and uncheck the radio button each times click on it.

  int click = 0;
    private void RadioButton_Click(object sender, RoutedEventArgs e)
    {
        click++;
        ((RadioButton)sender).IsChecked = click % 2 == 1 ;
        click %= 2;
    }

A RadioButton cannot be unchecked. If you have to keep using RadioButton s, I would add a default 'Not selected' choice. In this case, I prefer a ComboBox with an empty item or clear button at the top.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM