none
RadioButton.IsChecked Binding in MVVM scenario

    Question

  • Situation:

    I have a UniformGrid with a couple of RadioButtons in it:
    <UniformGrid x:Name="qty" Grid.Column="0" Rows="10" Columns="1" Width="50">
      <RadioButton x:Name="btn1" Style="{DynamicResource RadioButton4}"
                   Command="{Binding Path=cmd_QtyClicked}" CommandParameter="1"
    IsChecked="{Binding Path=Qty, Converter={StaticResource isQtySelectedConv}, ConverterParameter=1}">1</RadioButton> <RadioButton x:Name="btn2" Style="{DynamicResource RadioButton4}"
    Command="{Binding Path=cmd_QtyClicked}" CommandParameter="2"
    IsChecked="{Binding Path=Qty, Converter={StaticResource isQtySelectedConv}, ConverterParameter=2}">2</RadioButton>
    </UniformGrid>

    The property 'Qty' is in a viewmodel that's the datacontext of the usercontrol the uniformgrid is on. The command the RadioButton is executing is also in the viewmodel. The command sets the Qty property to the value given in the CommandParameter. The IsChecked-property of each radiobutton is bound to the Qty-property. A converter converts Qty to True/False based on the ConverterParameter passed in the binding. The reason for this is that, if the Qty prop gets changed in the viewmodel, the IsChecked prop of the right button gets updated automatically.

    The problem is the following : when I click for example the second radiobutton, the Qty value gets set to '2' and when logging the ValueConverters Convert method, i see that the converter fires for the first button only. When I click the first button, it doesn't fire for anything anymore.

    Similarly, if i try for example with 5 buttons:
    - I click on button 2, Valueconverter fires for buttons 1,3,4 and 5
    - then click on button 3, valueconverter fires for buttons 1,,4 and 5
    - then click on button 4, it fires for buttons 1 and 5

    et cetera.

    So, in the end, when you've clicked all buttons once, the converter doesn't fire anymore. Which is a pain, because the IsChecked state no longer gets updated.

    I tried getting rid of the command by making IsChecked bind TwoWay and implementing the ConvertBack method of the converter, but that didn't solve anything either. Also fiddled around with UpdateSourceTrigger , but that didn't help either.
    Any ideas?

    Monday, October 26, 2009 12:29 PM

Answers

  • Hi KVds,

    Since you have bound the IsChecked property of the RadioButton to the Qty property in the ViewModel in TwoWay binding mode, why do you set a command on the RadioButton? It's not necessary and this is the reason of your probem.

    Either bind the RadioButton in OneWay binding mode and set a command on the RadioButton OR bind the RadioButton in TwoWay mode and not use a command. For example:

     <RadioButton CommandParameter="1"
            IsChecked="{Binding Path=RB1Visible, Mode=OneWay}" Content="1" />
    -or-
     <RadioButton IsChecked="{Binding Path=RB1Visible}" Content="1" />

    Please try my suggestion to see if it can solve the problem.

    Sincerely,
    Linda Liu


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Linda Liu Wednesday, November 04, 2009 11:19 AM
    Monday, November 02, 2009 6:36 AM

All replies

  • Hello,

    In MVVM there is no need to use binding converters.
    In your view model you can expose an boolean property that you can use in your view to determine the is checked property of the checkbox.

    if you need more than one property you can expose a list of boolean properties, for example an observable collection of bool and use the index in your example to access each property.


    <UniformGrid x:Name="qty" Grid.Column="0" Rows="10" Columns="1" Width="50">
      <RadioButton x:Name="btn1" Style="{DynamicResource RadioButton4}"
                   Command="{Binding Path=cmd_QtyClicked}" CommandParameter="1"
                   IsChecked="{Binding Path=IsCheckedList[1] }">1</RadioButton>
      <RadioButton x:Name="btn2" Style="{DynamicResource RadioButton4}"
                   Command="{Binding Path=cmd_QtyClicked}" CommandParameter="2"
                   IsChecked="{Binding Path=IsCheckedList[2] }">2</RadioButton>
    </UniformGrid>


    Last, you should move the logic that you use in the binding converter to a method in the view model and call this method each time the command is executed to update all boolean properties.

    Good Luck.
    Monday, October 26, 2009 1:13 PM
  • Sounds reasonable!
    Like you said, I created a ObservableCollection<bool> in the ViewModel and bound the IsChecked to the indexed collection. However, when putting Qty to 1 in the VM , the selection doesn't get updated.
    Do I need to trigger PropertyChanged in this case?
    Monday, October 26, 2009 2:15 PM
  • I have now isolated the problem in a small test problem.

    I've made 5 properties in the VM: "RB1Visible" through "RB5Visible".

    This the XAML:
    <Window x:Class="RadioButtonTest.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="*" />
          <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
          <UniformGrid Rows="5">
          <RadioButton Command="{Binding Path=cmd_QtyClicked}" CommandParameter="1"
            IsChecked="{Binding Path=RB1Visible}" Content="1" />
          <RadioButton Command="{Binding Path=cmd_QtyClicked}" CommandParameter="2"
                       IsChecked="{Binding Path=RB2Visible}" Content="2" />
          <RadioButton Command="{Binding Path=cmd_QtyClicked}" CommandParameter="3"
                       IsChecked="{Binding Path=RB3Visible}" Content="3" />
          <RadioButton Command="{Binding Path=cmd_QtyClicked}" CommandParameter="4"
                       IsChecked="{Binding Path=RB4Visible}" Content="4" />
          <RadioButton Command="{Binding Path=cmd_QtyClicked}" CommandParameter="5"
                       IsChecked="{Binding Path=RB5Visible}" Content="5" />
        </UniformGrid>
    
        <Button Grid.Row="1" Content="Order product" MinHeight="30" Padding="3" Margin="2" Click="Button_Click" />
    
    
      </Grid>
    </Window>
    


    The command sets the corresponding RBXVisible property (they're public properties backed by a private one) to true and the others to false.
    The button sets RB1Visible to true and the others to false.

    If you first click one of the radiobuttons (other than RB1), then click on the Button, you'll see that the binding does not get updated (IsChecked does not change to true on RB1). I've tried this both with Dependency properties and with INotifyPropertyChanged.
    Monday, October 26, 2009 4:06 PM
  • Hello,

    Do you call OnPropertyChanged("RBXVisible") explicitly when its value is updated in  the view model?


    Monday, October 26, 2009 5:26 PM
  • Yes.
    And I also tried it with DependencyProperties.
    Tuesday, October 27, 2009 8:19 AM
  • Hi KVdS,

    have you tried explicitly telling your CheckBox(es) to update the VM when their value changes? I.e.:

    ... IsChecked="{Binding Path=YourField, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ...

    Cheers,
    Olaf
    Tuesday, October 27, 2009 11:00 AM
  • Tried that and it doesn't work either.

    Somehow binding to IsChecked doesn't quite work as I expect it too.
    So as a workaround in the View I am now subscribing to an event in the ViewModel to couple my ViewModel to my view. I know this is not the way to go and not an elegant solution, but i got fed up with looking for a way to get the proper way to do it to work.

    Thanks for your feedback anyway!
    Tuesday, October 27, 2009 1:26 PM
  • Hi KVdS,

    ahem. Error: Name 'GiveUp()' is not declared. ;-)

    Let's start with how you generally bind your checkboxes. Here's a sample I fetched from a view in my current project and which might just be a situation at least similar to yours:

    <CheckBox HorizontalContentAlignment="Center" HorizontalAlignment="Center"
    	 IsChecked="{Binding Path=IsMember, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    	 Command="{Binding Path=DataContext.ChangeGroupMembershipCommand, 
    	 RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
    	 CommandParameter="{Binding}"
     />


    In my ViewModel, the Command-Binding targets an ICommand (I generally use the RelayCommand-class that Karl Shifflet once introduced in his CiperText sample application on the CodeProject).
    As a result, the ChangeGroupMembershipExecute() method will be called in the VM. To actually allow the View to find the command in the VM, it bubbles up to the main level (here, the UserControl whose DataContext is set to the VM) and uses its DataContext. The CommandParameter="{Binding}" will pass a reference to the bound object whose property was changed (by means of the checkbox ticked/unticked), allowing the method to use the object that's "behind" the checkbox. Since the Checkbox above is actually part of a ListView, this is the selected item there, which could of course be completely different depending on your scenario.

    All this may as well be completely inapplicable and thus useless to you, but my guess is that you could use a strategy like that to make your converter obsolete and rather convert your stuff right in the called VM-method. Worth a try?

    Also, it seems that I don't quite understand what you're really doing there (might of course be my personal stupidity).
    I understand that you have a field that can take 5 different values (say 1-5), so, instead of a ComboBox offering a choice, you want a group of RadioButtons.
    Now if you want each of your RadioButtons to be bound to their own property (i.e. Qty1...Qty5) in the ViewModel (at least that's what your XAML tells me), then why not have the VM expose these properties "as is"? You'd really have the "converter" in our VM, which would fire <OnPropertyChanged("QtyX");> for each of the aforementioned props when it sets the quantity (i.e. after retrieving it from your DB) and it could also be called in the QtyX-property-setters in order to notify the other RadioButtons that a change in the group has been processed.

    Cheers,
    Olaf
    Tuesday, October 27, 2009 3:52 PM
  • Actually, I have one property in my VM: Qty.

    What I want is:

    - a group of radiobuttons, labeled 1 through 9.
    - when you click on radiobutton 1, qty gets set to 1, when you click on 2, it gets set to 2, et cetera
    - the IsChecked property of RadioButton must reflect the value of Qty. so if qty == 1 the first button is checked

    So if you set the value of Qty in the VM (from another button than one of the radiobuttons for example) the isChecked state of radiobutton1 should follow.

    If you want, i can send you  a small app where I've tried to accomplish it, so you can see what happens.
    Wednesday, October 28, 2009 8:25 AM
  • Hi KVdS,

    alright, I guess I understand now - the XAML you posted once was a little irritating due to the fact that you were binding each RB to its own property, but I see now that this wasn't the case in your original posting.

    Have you seen the FAQ-entry about the XAML-only approach to using a RadioButton-group?
    -> http://social.msdn.microsoft.com/forums/en-US/wpf/thread/a2988ae8-e7b8-4a62-a34f-b851aaf13886#radiobuttonlist

    I have put up a little sample solution that should show you how to work this out in MVVM, if you don't want to go with a ListBox.
    You can download it here [Edit: I have removed the VB-sample for now as the OP is using C# - see the younger postings].

    Cheers,
    Olaf
    • Edited by Olaf Rabbachin Thursday, October 29, 2009 12:07 PM now using C# instead of VB
    Wednesday, October 28, 2009 9:43 AM
  • Hi Kevin,

    from your e-mail I can see that you're actually using C# (I prefer VB), so I took the solution you sent me and changed it so it basically matches what I showed in the VB-solution in my previous post, using C#. You can download it here .

    Back to doing some real work now ... ;-)

    Cheers,
    Olaf
    Wednesday, October 28, 2009 11:57 AM
  • Ah, thanks.
    This works, however! You've left the tricky part out! :-)
    If you add a button to the window and in its event handler you set the _vm.QtySelected = 4, the isChecked value of radiobutton 4 doesn't change in your example. 
    So in your example I've changed the binding mode to TwoWay. If you do that, you'll see that if you set _vm.QtySelected = 4, it will reflect in the radiobuttons: RadioButton 4 gets selected. So that works.

    But the tricky part is when you do the following.

    After putting the TwoWay-change in, add another button titled '?'.  If you click this button, the value of QtySelected gets set to 1. For now, do not bind the IsChecked property of this RadioButton.

    Now to test this:

    - Click on radiobutton labeled '4'
    - Click on the '?' radiobutton: radiobutton '1' gets checked, as by design
    - click on radiobutton labeled '4' again
    - click on '?' again: radiobutton '1' DOES NOT GET CHECKED. instead the '?' radiobutton gets selected, EVEN when you can clearly see (because you've added the 'current selection' caption) that _vm.QtySelected is actually set to 1

    I've sent you your adapted example program through e-mail (due to lack of serverspace to upload it)

    cheers

    Wednesday, October 28, 2009 12:54 PM
  • Wow. That sure is strange - haven't come across that yet. I see the problem you're having, but I haven't found a way around this and neither do I have any idea as to what could be causing the behaviour.

    FWIW - I have also changed the sample-solution so that it does not use a converter; instead, the IsChecked-prop three binds to a method that accepts an index and compares that to the currently selected number. This was just to confirm that it's not the converter that causes any problems. However, this produces the exact same behavior - while the selection can be transfered reliably from either a RadioButton or a regular button, the group of RadioButtons does not always reflect the change, even though the INotifyChanged-implementation should actually do just that.

    I'm stumped.

    Cheers,
    Olaf
    Wednesday, October 28, 2009 5:15 PM
  • Hmm yes, indeed weird, huh?

    As i said, I've solved it now using an event. But it kind of breaks the MVVM design.
    And anyway, the INotifyPropertyChanged should do this for us, so I don't see the point.  Let's hope someday this gets fixed..

    Thanks for looking into it anyway!

    cheers
    Kevin
    Thursday, October 29, 2009 12:01 PM
  • Hi Kevin,

    I sure don't like giving up, but at present I don't really see any corners left to dig into; it simply seems that INotifyPropertyChanged in this case isn't doing its job properly! :-(
    While I do not currently have the necessity to implement something like this, it's fundamental enough that I might actually stumble over something similar in the near future, so I guess it'd be good to actually get to the ground of the problem and fix it.

    FWIW, I've done some cleaning up (i.e. the old sample was still using the RelativeAncestor since it was taken from one of my projects, but this isn't required for this sample) and have updated the sample solution . It contains the two fundamental approaches (one with the converter class and one that instead uses a method in the VM). The VM-method based approach is even worse, which sure makes me think that I'm overseeing something stupidly simple really.

    HomeroThompson, are you still with us and care to take a look? :-)

    Cheers,
    Olaf
    Thursday, October 29, 2009 12:14 PM
  • Hi KVds,

    Since you have bound the IsChecked property of the RadioButton to the Qty property in the ViewModel in TwoWay binding mode, why do you set a command on the RadioButton? It's not necessary and this is the reason of your probem.

    Either bind the RadioButton in OneWay binding mode and set a command on the RadioButton OR bind the RadioButton in TwoWay mode and not use a command. For example:

     <RadioButton CommandParameter="1"
            IsChecked="{Binding Path=RB1Visible, Mode=OneWay}" Content="1" />
    -or-
     <RadioButton IsChecked="{Binding Path=RB1Visible}" Content="1" />

    Please try my suggestion to see if it can solve the problem.

    Sincerely,
    Linda Liu


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Linda Liu Wednesday, November 04, 2009 11:19 AM
    Monday, November 02, 2009 6:36 AM